From 2fa0853e42f2d7f1a78634b4e63c798e5359df87 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sun, 20 Sep 2026 04:24:42 -0400 Subject: [PATCH] [VERY VERY VERY BAD IDEA] --- .../DatapackStructureStateFilter.java | 509 ----- .../nms/v26_2_R1/ImportedFeatureStage.java | 440 ----- .../nms/v26_2_R1/IrisChunkGenerator.java | 1643 ---------------- .../bukkit/nms/v26_2_R1/NMSBinding.java | 1644 ---------------- .../v26_2_R1/NativeStructurePoiUpdates.java | 142 -- .../NativeStructureWorldgenAccess.java | 606 ------ .../nms/v26_2_R1/NmsGenerationHooks.java | 61 - .../nms/v26_2_R1/NmsGenerationRegistry.java | 182 -- .../nms/v26_2_R1/NmsSavedTerrainCapture.java | 266 --- .../nms/v26_2_R1/NmsWorldLifecycle.java | 403 ---- .../nms/v26_2_R1/VanillaStructureBiomes.java | 80 - .../IrisChunkGeneratorHeightCacheTest.java | 16 +- .../NativeGenerationWriteGuardTest.java | 30 +- .../nativegen/NativeStructureFactoryTest.java | 26 +- ...veStructureGenerationKeysContractTest.java | 2 + .../NativeStructureLocatePersistenceTest.java | 2 + ...tiveStructureOwnershipFingerprintTest.java | 49 +- .../NativeStructureOwnershipRecoveryTest.java | 61 +- ...ativeStructurePostProcessorEncaseTest.java | 42 +- ...iveStructurePostProcessorMonumentTest.java | 8 +- ...turePostProcessorScatteredFeatureTest.java | 6 + ...ucturePostProcessorSurfaceTerrainTest.java | 160 +- ...eStructurePostProcessorVegetationTest.java | 6 + .../NativeStructureTemplateOccupancyTest.java | 4 + .../NativeTerrainHeightCacheTest.java | 2 + .../nativegen/NativeTransitionColumnTest.java | 25 +- ...ustomBiomeSourceStructureContractTest.java | 12 +- ...nkGeneratorMonumentLocateContractTest.java | 2 +- .../IrisChunkGeneratorSpawnAdmissionTest.java | 15 +- .../NMSBindingDatapackStructureScopeTest.java | 48 +- .../NativeStructurePoiUpdatesTest.java | 2 + .../NativeStructureWorldgenAccessTest.java | 10 +- .../nms/v26_2_R1/NmsChunkIOFlushTest.java | 3 +- .../nms/v26_2_R1/NmsChunkTaskPollingTest.java | 7 +- .../NmsEntitySpawnDifficultyTest.java | 9 +- .../NmsSavedTerrainCheckpointTest.java | 114 -- .../v26_2_R1/NmsSavedTerrainSnapshotTest.java | 223 --- .../NmsSavedTerrainWriteBarrierTest.java | 135 -- .../WorldgenTerrainHeightmapsTest.java | 6 +- ...terListPoolElementRuntimeContractTest.java | 38 +- .../NativeStructureVolumeIndexTest.java | 23 +- .../structure/StructureReaderFixture.java | 12 + .../ForcedStructureChunkGenerator.java | 83 - .../v26_3_R1/NativeBuildFutures.java | 31 - .../v26_3_R1/NativeGenerationWriteGuard.java | 78 - .../v26_3_R1/NativeStructureFactory.java | 286 --- .../NativeStructureFoundationBuilder.java | 212 --- .../NativeStructureGenerationException.java | 35 - .../NativeStructureGenerationKeys.java | 121 -- .../NativeStructureLocatePersistence.java | 351 ---- .../NativeStructureLocateResults.java | 58 - .../NativeStructureOwnershipFingerprint.java | 239 --- .../NativeStructureOwnershipRecovery.java | 195 -- .../NativeStructurePostProcessor.java | 66 - .../NativeStructureReferenceEnvelope.java | 239 --- .../NativeStructureReferenceRepair.java | 246 --- .../v26_3_R1/NativeStructureReflection.java | 386 ---- .../NativeStructureStartInjector.java | 147 -- .../NativeStructureSurfaceFitter.java | 1221 ------------ .../NativeStructureSurfaceSupportBuilder.java | 305 --- .../NativeStructureTemplateOccupancy.java | 165 -- .../NativeStructureTemplatePoolBounds.java | 228 --- .../NativeStructureTerrainIntegrator.java | 1096 ----------- .../NativeStructureVanillaLocator.java | 224 --- .../NativeStructureVegetationClearer.java | 174 -- .../NativeStructureVerticalPlacer.java | 384 ---- .../v26_3_R1/NativeStructureVolumeIndex.java | 643 ------- .../v26_3_R1/NativeTerrainHeightCache.java | 42 - .../v26_3_R1/NativeTransitionColumn.java | 63 - .../v26_3_R1/WorldgenTerrainHeightmaps.java | 113 -- .../nms/v26_3_R1/CustomBiomeSource.java | 1287 ------------- .../DatapackStructureStateFilter.java | 513 ----- .../nms/v26_3_R1/ImportedFeatureStage.java | 441 ----- .../nms/v26_3_R1/InitialMobSpawner.java | 132 -- .../nms/v26_3_R1/IrisChunkGenerator.java | 1621 ---------------- .../bukkit/nms/v26_3_R1/NMSBinding.java | 1645 ----------------- .../v26_3_R1/NativeStructurePoiUpdates.java | 142 -- .../NativeStructureWorldgenAccess.java | 606 ------ .../nms/v26_3_R1/NmsGenerationHooks.java | 61 - .../nms/v26_3_R1/NmsGenerationRegistry.java | 182 -- .../nms/v26_3_R1/NmsSavedTerrainCapture.java | 266 --- .../nms/v26_3_R1/NmsWorldLifecycle.java | 403 ---- .../nms/v26_3_R1/VanillaStructureBiomes.java | 80 - .../IrisChunkGeneratorHeightCacheTest.java | 16 +- .../NativeGenerationWriteGuardTest.java | 30 +- .../nativegen/NativeStructureFactoryTest.java | 26 +- ...veStructureGenerationKeysContractTest.java | 2 + .../NativeStructureLocatePersistenceTest.java | 2 + ...tiveStructureOwnershipFingerprintTest.java | 49 +- .../NativeStructureOwnershipRecoveryTest.java | 61 +- ...ativeStructurePostProcessorEncaseTest.java | 42 +- ...iveStructurePostProcessorMonumentTest.java | 8 +- ...turePostProcessorScatteredFeatureTest.java | 6 + ...ucturePostProcessorSurfaceTerrainTest.java | 160 +- ...eStructurePostProcessorVegetationTest.java | 6 + .../NativeStructureTemplateOccupancyTest.java | 4 + .../NativeTerrainHeightCacheTest.java | 2 + .../nativegen/NativeTransitionColumnTest.java | 25 +- .../nms/v26_3_R1/CustomBiomeCodecTest.java | 2 +- ...ustomBiomeSourceStructureContractTest.java | 12 +- ...nkGeneratorMonumentLocateContractTest.java | 2 +- .../IrisChunkGeneratorSpawnAdmissionTest.java | 15 +- .../NMSBindingDatapackStructureScopeTest.java | 48 +- .../NativeStructurePoiUpdatesTest.java | 2 + .../NativeStructureWorldgenAccessTest.java | 10 +- .../nms/v26_3_R1/NmsChunkIOFlushTest.java | 3 +- .../nms/v26_3_R1/NmsChunkTaskPollingTest.java | 7 +- .../NmsEntitySpawnDifficultyTest.java | 9 +- .../NmsSavedTerrainCheckpointTest.java | 114 -- .../v26_3_R1/NmsSavedTerrainSnapshotTest.java | 223 --- .../NmsSavedTerrainWriteBarrierTest.java | 135 -- .../WorldgenTerrainHeightmapsTest.java | 6 +- ...terListPoolElementRuntimeContractTest.java | 38 +- .../NativeStructureVolumeIndexTest.java | 23 +- .../structure/StructureReaderFixture.java | 12 + .../src/main/java/art/arcane/iris/Iris.java | 5 +- .../java/art/arcane/iris/IrisBootstrap.java | 3 +- .../art/arcane/iris/IrisPluginLoader.java | 7 +- .../arcane/iris/command/CommandDeveloper.java | 2 +- .../arcane/iris/command/CommandObject.java | 6 +- .../art/arcane/iris/command/CommandSVC.java | 2 +- .../art/arcane/iris/command/CommandWhat.java | 2 +- .../iris/studio/view/BukkitVisionOverlay.java | 20 +- .../art/arcane/iris/studio/wand/WandSVC.java | 4 +- .../iris/studio/wand/WandSelection.java | 2 +- .../iris/world/BukkitWorldReconciler.java | 17 +- .../runtime/BukkitEnginePlatformHooks.java | 2 +- .../native-lifecycle-fixture/build.gradle | 34 + .../resources/plugin.yml | 11 + .../native-lifecycle-fixture/settings.gradle | 1 + .../iris/fixture/NativeLifecycleFixture.java | 252 +++ .../src/test/gameplay/native-lifecycle.mjs | 22 + .../native-terrain-pack/biomes/flat.json | 23 + .../dimensions/native-terrain.json | 28 + .../native-terrain-pack/generators/flat.json | 15 + .../native-terrain-pack/regions/flat.json | 12 + .../src/test/gameplay/native-terrain.mjs | 18 + ...CommandObjectCustomBlockPlacementTest.java | 6 +- .../iris/studio/wand/WandSelectionTest.java | 2 +- .../art/arcane/iris/client/IrisClient.java | 5 +- .../art/arcane/iris/client/IrisClientHud.java | 32 +- .../iris/client/IrisClientKeybinds.java | 25 +- .../art/arcane/iris/client/IrisPregenHud.java | 24 +- .../iris/client/IrisToastPresenter.java | 23 +- .../arcane/iris/client/IrisVisionScreen.java | 130 +- .../arcane/iris/client/IrisWhatOverlay.java | 40 +- .../client/mixin/IrisWorldOpenFlowsMixin.java | 121 -- .../client/mixin/IrisWorldTypeEntryMixin.java | 43 - adapters/fabric/build.gradle | 5 +- .../fabric/FabricForcedDatapackSources.java | 40 - .../iris/fabric/FabricModdedLoader.java | 122 -- .../iris/fabric/FabricProtocolNetworking.java | 49 +- .../iris/fabric/IrisFabricBootstrap.java | 88 +- .../arcane/iris/fabric/IrisFabricClient.java | 31 +- .../iris/fabric/mixin/BlockItemMixin.java | 25 - .../arcane/iris/fabric/mixin/BlockMixin.java | 61 - .../fabric/mixin/PackRepositoryMixin.java | 49 - .../fabric/src/main/resources/fabric.mod.json | 66 +- .../main/resources/irisworldgen.accesswidener | 6 - .../main/resources/irisworldgen.mixins.json | 14 - adapters/forge/build.gradle | 13 +- .../iris/forge/ForgeClientProtocol.java | 31 - .../arcane/iris/forge/ForgeModdedLoader.java | 131 -- .../iris/forge/ForgeProtocolNetworking.java | 85 +- .../forge/IrisForgeBlockLootModifier.java | 78 - .../arcane/iris/forge/IrisForgeBootstrap.java | 144 +- .../arcane/iris/forge/IrisForgeClient.java | 32 +- .../resources/META-INF/accesstransformer.cfg | 3 - .../ForcedStructureChunkGenerator.java | 94 - .../iris/nativegen/NativeBuildFutures.java | 31 - .../nativegen/NativeGenerationWriteGuard.java | 78 - .../nativegen/NativeStructureFactory.java | 284 --- .../NativeStructureFoundationBuilder.java | 212 --- .../NativeStructureGenerationException.java | 35 - .../NativeStructureGenerationKeys.java | 117 -- .../NativeStructureLocatePersistence.java | 351 ---- .../NativeStructureLocateResults.java | 58 - .../NativeStructureOwnershipFingerprint.java | 239 --- .../NativeStructureOwnershipRecovery.java | 195 -- .../NativeStructurePostProcessor.java | 66 - .../NativeStructureReferenceEnvelope.java | 239 --- .../NativeStructureReferenceRepair.java | 246 --- .../nativegen/NativeStructureReflection.java | 386 ---- .../NativeStructureStartInjector.java | 147 -- .../NativeStructureSurfaceFitter.java | 1221 ------------ .../NativeStructureSurfaceSupportBuilder.java | 305 --- .../NativeStructureTemplateOccupancy.java | 165 -- .../NativeStructureTemplatePoolBounds.java | 228 --- .../NativeStructureTerrainIntegrator.java | 1096 ----------- .../NativeStructureVanillaLocator.java | 224 --- .../NativeStructureVegetationClearer.java | 174 -- .../NativeStructureVerticalPlacer.java | 384 ---- .../nativegen/NativeTerrainHeightCache.java | 42 - .../nativegen/NativeTransitionColumn.java | 63 - .../nativegen/WorldgenTerrainHeightmaps.java | 113 -- .../arcane/iris/modded/IrisItemRecipe.java | 116 ++ .../iris/modded/IrisModdedChunkGenerator.java | 1107 ++++------- .../arcane/iris/modded/MainWorldService.java | 146 +- .../art/arcane/iris/modded/ModdedBiome.java | 58 - ...iomeSource.java => ModdedBiomePolicy.java} | 474 ++--- .../arcane/iris/modded/ModdedBiomeWriter.java | 137 +- .../iris/modded/ModdedBlockBreakHandler.java | 182 +- .../arcane/iris/modded/ModdedBlockBuffer.java | 25 +- .../iris/modded/ModdedBlockResolution.java | 535 +----- .../arcane/iris/modded/ModdedBlockState.java | 321 ---- .../arcane/iris/modded/ModdedDeathLoot.java | 74 +- .../iris/modded/ModdedDecoratorHooks.java | 106 +- .../iris/modded/ModdedDimensionManager.java | 521 +----- .../iris/modded/ModdedDimensionMetadata.java | 6 +- .../modded/ModdedDimensionRegistryStore.java | 18 +- .../iris/modded/ModdedDimensionStorage.java | 13 +- .../iris/modded/ModdedEngineBootstrap.java | 154 +- .../iris/modded/ModdedEngineEffects.java | 160 +- .../iris/modded/ModdedEntityAwareness.java | 39 - .../modded/ModdedEntityCommandRunner.java | 28 +- .../iris/modded/ModdedEntityPersistence.java | 43 - .../iris/modded/ModdedEntitySpawner.java | 472 ++--- .../arcane/iris/modded/ModdedEntityType.java | 64 - .../iris/modded/ModdedForcedDatapack.java | 66 +- .../art/arcane/iris/modded/ModdedGenPool.java | 230 +-- .../ModdedGenerationHistoryStorage.java | 27 +- .../iris/modded/ModdedGenerationRegistry.java | 134 +- .../arcane/iris/modded/ModdedHeightmaps.java | 22 - .../modded/ModdedImportedFeaturePolicy.java | 21 + .../modded/ModdedImportedFeatureStage.java | 382 +--- .../arcane/iris/modded/ModdedIrisPayload.java | 43 - .../arcane/iris/modded/ModdedIrisSplash.java | 8 +- .../art/arcane/iris/modded/ModdedItem.java | 58 - .../iris/modded/ModdedItemTranslator.java | 335 +--- .../iris/modded/ModdedLifecycleCallbacks.java | 35 + .../art/arcane/iris/modded/ModdedLoader.java | 54 - .../arcane/iris/modded/ModdedLootApplier.java | 234 +-- .../arcane/iris/modded/ModdedMixinAudit.java | 47 +- .../arcane/iris/modded/ModdedMixinFlags.java | 112 -- .../modded/ModdedNativeStructurePolicy.java | 163 ++ .../modded/ModdedNativeStructureStage.java | 598 ------ .../ModdedNativeStructureWorldgenAccess.java | 567 ------ .../modded/ModdedNativeTerrainReceipts.java | 44 +- .../arcane/iris/modded/ModdedParityProbe.java | 30 +- .../arcane/iris/modded/ModdedPlatform.java | 57 +- .../iris/modded/ModdedPlatformWorld.java | 108 -- .../iris/modded/ModdedPrimaryWorldRouter.java | 42 +- .../iris/modded/ModdedProtocolChannel.java | 27 - .../iris/modded/ModdedProtocolDefinition.java | 11 + .../iris/modded/ModdedProtocolHandler.java | 95 +- .../iris/modded/ModdedProtocolTransport.java | 22 +- .../arcane/iris/modded/ModdedRegistries.java | 267 +-- .../iris/modded/ModdedRuntimeRegistry.java | 105 -- .../modded/ModdedSavedTerrainCapture.java | 134 +- .../arcane/iris/modded/ModdedScheduler.java | 18 +- .../iris/modded/ModdedServerAccess.java | 43 - .../iris/modded/ModdedServerCommands.java | 41 - .../iris/modded/ModdedServerLevels.java | 326 ---- .../iris/modded/ModdedServiceManager.java | 6 +- .../iris/modded/ModdedSpawnBiomePolicy.java | 59 + .../iris/modded/ModdedSpawnTableMerger.java | 182 -- .../art/arcane/iris/modded/ModdedStartup.java | 20 +- .../arcane/iris/modded/ModdedStateMerger.java | 76 - .../iris/modded/ModdedStateRotator.java | 370 +--- .../iris/modded/ModdedStructureHooks.java | 404 +--- .../ModdedStructureSetFrequencyOverrides.java | 285 --- .../iris/modded/ModdedTeleportBounds.java | 50 - .../arcane/iris/modded/ModdedTileData.java | 391 +--- .../arcane/iris/modded/ModdedTileReader.java | 361 ---- .../arcane/iris/modded/ModdedWorldCheck.java | 101 +- .../iris/modded/ModdedWorldEngines.java | 114 +- .../iris/modded/ModdedWorldManager.java | 171 +- .../iris/modded/NativeSpawnTableMerger.java | 34 - .../modded/NativeTerrainReceiptHolder.java | 11 - .../modded/WorldCheckDimensionContract.java | 55 +- .../modded/WorldCheckFeaturePlacement.java | 5 +- .../iris/modded/WorldCheckMaterials.java | 9 +- .../iris/modded/WorldCheckPredicates.java | 2 +- .../iris/modded/WorldCheckStructureAudit.java | 587 +----- .../arcane/iris/modded/api/IrisModdedAPI.java | 43 +- .../iris/modded/api/ModdedBlockData.java | 12 +- .../api/ModdedBlockPlacementContext.java | 26 +- .../api/ModdedCustomContentRegistry.java | 75 +- .../iris/modded/api/ModdedDataProvider.java | 22 +- .../iris/modded/api/ModdedDataType.java | 4 +- .../modded/command/IrisModdedCommands.java | 166 +- .../modded/command/ModdedCommandFeedback.java | 104 +- .../modded/command/ModdedCommandHelp.java | 104 +- .../command/ModdedCommandSuggestions.java | 95 +- .../modded/command/ModdedCommandTree.java | 335 ++-- .../command/ModdedDatapackCommands.java | 97 +- .../command/ModdedDeveloperCommands.java | 22 +- .../modded/command/ModdedDustRevealer.java | 148 +- .../modded/command/ModdedEditCommands.java | 32 +- .../iris/modded/command/ModdedGoldenHash.java | 47 +- .../iris/modded/command/ModdedGuiHost.java | 20 +- .../command/ModdedLanguageCommands.java | 45 +- .../modded/command/ModdedLocateCommands.java | 200 +- .../modded/command/ModdedObjectCommands.java | 456 +++-- .../modded/command/ModdedObjectPlacer.java | 83 +- .../iris/modded/command/ModdedObjectUndo.java | 37 +- .../modded/command/ModdedPackCommands.java | 140 +- .../modded/command/ModdedPregenBossBar.java | 63 +- .../modded/command/ModdedPregenCommands.java | 33 +- .../iris/modded/command/ModdedPregenJob.java | 53 +- .../modded/command/ModdedPregenMethod.java | 123 +- .../iris/modded/command/ModdedRegen.java | 139 +- .../command/ModdedStructureCommands.java | 99 +- .../modded/command/ModdedStudioCommands.java | 319 ++-- .../command/ModdedUnregisteredStructures.java | 32 +- .../modded/command/ModdedVisionOverlay.java | 87 +- .../modded/command/ModdedWandService.java | 207 +-- .../modded/command/ModdedWhatCommands.java | 275 ++- .../modded/command/ModdedWorldCommands.java | 172 +- .../mixin/ChunkMapTerrainReceiptAccess.java | 18 - .../mixin/ChunkTerrainReceiptMixin.java | 37 - .../modded/mixin/EntityPersistenceMixin.java | 19 - .../mixin/LevelChunkTerrainReceiptMixin.java | 21 - .../modded/mixin/LivingEntityLootMixin.java | 22 - .../iris/modded/mixin/MobAwarenessMixin.java | 41 - .../SerializableTerrainReceiptMixin.java | 89 - ...uctureTemplatePaletteConcurrencyMixin.java | 31 - .../mixin/WorldGenerationWriteGuardMixin.java | 25 - .../service/ModdedChunkUpdateService.java | 132 +- .../ModdedEngineMaintenanceService.java | 4 +- .../service/ModdedEntitySpawnService.java | 14 +- .../service/ModdedLogFilterService.java | 23 +- .../service/ModdedSettingsHotloadService.java | 4 +- .../service/ModdedStudioHotloadService.java | 57 +- .../modded/service/ModdedTickableService.java | 4 +- .../service/ModdedTreeFellerPresentation.java | 138 +- .../service/ModdedTreeFellerService.java | 269 +-- .../resources/irisworldgen.client.mixins.json | 13 - .../resources/irisworldgen.entity.mixins.json | 20 - .../IrisModdedBlockEntityParityTest.java | 14 +- .../IrisModdedChunkGeneratorSpawnTest.java | 2 + .../modded/IrisModdedHeightmapParityTest.java | 2 + .../modded/IrisModdedLocateRoutingTest.java | 2 +- .../modded/IrisModdedStructureParityTest.java | 58 +- .../iris/modded/ModdedBiomePolicyTest.java | 103 ++ .../modded/ModdedBiomeWriterCacheTest.java | 11 +- .../modded/ModdedBlockBreakHandlerTest.java | 16 +- .../ModdedBlockResolutionContractTest.java | 40 +- .../ModdedClientPackageIsolationTest.java | 10 +- .../iris/modded/ModdedDeathLootTest.java | 37 + .../modded/ModdedDeferredBlockParityTest.java | 20 +- .../modded/ModdedDimensionTypeParityTest.java | 10 +- .../modded/ModdedEntityAwarenessTest.java | 21 +- .../modded/ModdedEntityPersistenceTest.java | 23 +- .../ModdedEntitySpawnDifficultyTest.java | 19 +- .../modded/ModdedEntitySpawnerParityTest.java | 34 +- .../iris/modded/ModdedForcedDatapackTest.java | 2 +- .../ModdedGenerationBootstrapBindingTest.java | 92 +- .../ModdedImportedFeatureStageTest.java | 72 + .../iris/modded/ModdedLootApplierTest.java | 18 +- ...dedNativeStructurePoiRegistrationTest.java | 6 +- ...ddedNativeStructureWorldgenAccessTest.java | 10 +- .../iris/modded/ModdedPlatformPathsTest.java | 22 +- .../modded/ModdedSavedBiomeConsumersTest.java | 26 +- ...dedStructureSetFrequencyOverridesTest.java | 12 +- .../ModdedStructureTemplateCacheTest.java | 4 +- .../modded/ModdedTileDataIdentityTest.java | 11 +- .../iris/modded/ModdedTileParityTest.java | 39 +- .../modded/ModdedWaterEntitySpawnTest.java | 32 +- .../iris/modded/ModdedWorldCheckTest.java | 8 +- .../modded/NativeCommandExecutorTest.java | 61 + .../iris/modded/NativeItemTranslatorTest.java | 83 + .../modded/NativePayloadProtocolTest.java | 44 + .../iris/modded/NativeRegistryAccessTest.java | 54 + .../modded/NativeStateOperationsTest.java | 51 + .../NativeStructureFailureContractTest.java | 2 +- .../api/ModdedCustomContentRegistryTest.java | 47 +- .../command/IrisModdedCommandParityTest.java | 5 +- .../IrisModdedStructureCommandTest.java | 2 +- .../command/ModdedDustRevealerTest.java | 22 +- .../command/ModdedLanguageCommandsTest.java | 10 +- .../ModdedPregenMethodLifecycleTest.java | 104 ++ .../modded/command/NativeEditWorldTest.java | 95 + .../modded/command/NativeTaggedItemsTest.java | 55 + .../service/ModdedChunkUpdateServiceTest.java | 7 +- .../ModdedTreeFellerPresentationTest.java | 15 +- .../service/ModdedWorldBoundaryTest.java | 13 +- .../NativeStructureReferenceRepairTest.java | 42 +- .../NativeStructureVacuumParityTest.java | 8 +- .../modded/BrigadierSourceMapperTest.java | 75 + .../modded/ModdedLevelDataAttachmentTest.java | 4 +- .../modded/ModdedServerLevelsTest.java | 4 +- .../modded/NativeChunkRegenerationTest.java | 97 + .../modded/NativeCommandTextTest.java | 54 + .../modded/NativeGeneratorOwnershipTest.java | 69 + .../modded/NativeHarvestSessionTest.java | 113 ++ .../modded/NativeMixinRegistrationTest.java | 101 + .../modded/NativePregenRuntimeTest.java | 66 + .../modded/NativeServerLogMessagesTest.java | 27 + .../modded/NativeServerWorldLayoutTest.java} | 46 +- .../modded/NativeSpawnBiomeTablesTest.java | 88 + .../NativeStructureOperationsTest.java} | 63 +- .../modded/NativeTeleportBoundsTest.java} | 4 +- .../modded/NativeWorldIdentityTest.java | 46 + .../modded/NativeWorldInspectionTest.java | 79 + .../modded/NativeWorldMaintenanceTest.java | 47 + adapters/neoforge/build.gradle | 26 +- .../iris/neoforge/IrisNeoForgeBootstrap.java | 181 +- .../iris/neoforge/IrisNeoForgeClient.java | 35 +- .../iris/neoforge/NeoForgeModdedLoader.java | 125 -- .../neoforge/NeoForgeProtocolNetworking.java | 78 +- .../resources/META-INF/accesstransformer.cfg | 3 - .../resources/META-INF/neoforge.mods.toml | 4 +- build.gradle | 128 +- buildSrc/build.gradle | 1 - buildSrc/src/main/java/Config.java | 5 - buildSrc/src/main/java/NMSBinding.java | 285 --- .../src/main/java/NativeRuntimeArtifacts.java | 123 ++ .../buildtools/GenerationRevisionScope.java | 4 - .../java/GenerationRevisionScopeTest.java | 33 +- .../test/java/ModdedArtifactVerifierTest.java | 4 +- .../test/java/NativeRuntimeArtifactsTest.java | 45 + core/build.gradle | 6 + .../data/CraftEngineDataProviderTest.java | 2 +- .../art/arcane/iris/command/IrisCommand.java | 6 +- .../iris/command/IrisCommandRegistry.java | 4 +- .../command/handlers/DataVersionHandler.java | 2 +- .../arcane/iris/diagnostics/LogFilterSVC.java | 10 +- .../iris/generation/biome/IrisBiome.java | 24 +- .../generation/biome/IrisBiomeCustom.java | 2 +- .../biome/IrisBiomeCustomParticle.java | 4 +- .../biome/IrisBiomeCustomSpawn.java | 4 +- .../biome/IrisBiomeDerivatives.java | 2 +- .../generation/biome/IrisBiomeGenLinks.java | 2 +- .../biome/IrisBiomeGeneratorLink.java | 2 +- .../biome/IrisBiomeLayerGenerator.java | 34 +- .../iris/generation/biome/IrisBiomeOres.java | 18 +- .../biome/IrisBiomePaletteLayer.java | 20 +- .../biome/IrisCustomBiomeAliasResolver.java | 2 +- .../biome/IrisFloatingChildBiomes.java | 2 +- .../art/arcane/iris/generation/block/B.java | 56 +- .../block/BlockDataMergeSupport.java | 6 +- .../generation/block/BoundBlockState.java | 6 +- .../iris/generation/block/IrisBlockData.java | 22 +- .../iris/generation/block/LegacyTileData.java | 4 +- .../iris/generation/block/TileData.java | 6 +- .../iris/generation/cache/AtomicCache.java | 197 -- .../generation/chunk/ChunkBiomeHunkView.java | 4 +- .../generation/chunk/ChunkDataHunkHolder.java | 15 +- .../generation/chunk/ChunkDataHunkView.java | 14 +- .../iris/generation/chunk/ChunkHunkView.java | 4 +- .../generation/chunk/LinkedTerrainChunk.java | 16 +- .../iris/generation/chunk/TerrainChunk.java | 16 +- .../chunk/TerrainChunkBiomeHunkView.java | 10 +- .../iris/generation/context/ChunkContext.java | 18 +- .../iris/generation/context/IrisContext.java | 4 +- .../generation/decoration/DecoratorCore.java | 86 +- .../decoration/DecoratorPlatformHooks.java | 6 +- .../decoration/FloatingDecorator.java | 4 +- .../decoration/IrisCeilingDecorator.java | 8 +- .../generation/decoration/IrisDecorator.java | 76 +- .../decoration/IrisDepositGenerator.java | 28 +- .../decoration/IrisDepositVariant.java | 18 +- .../decoration/IrisOreGenerator.java | 6 +- .../decoration/IrisProceduralBlocks.java | 16 +- .../decoration/IrisSeaFloorDecorator.java | 8 +- .../decoration/IrisSeaSurfaceDecorator.java | 8 +- .../decoration/IrisShoreLineDecorator.java | 16 +- .../decoration/IrisSpeleothems.java | 50 +- .../generation/decoration/IrisSugarCane.java | 14 +- .../decoration/IrisSurfaceDecorator.java | 40 +- .../decoration/coral/CoralGenerator.java | 10 +- .../decoration/coral/IrisCoral.java | 2 +- .../decoration/crystal/CrystalGenerator.java | 20 +- .../decoration/crystal/IrisCrystal.java | 2 +- .../formation/FormationBlockResolver.java | 8 +- .../formation/FormationGenerator.java | 6 +- .../decoration/formation/IrisFormation.java | 2 +- .../decoration/fungus/FungusGenerator.java | 12 +- .../decoration/fungus/IrisFungus.java | 2 +- .../generation/decoration/ruin/IrisRuin.java | 2 +- .../decoration/ruin/RuinBlockCanvas.java | 12 +- .../decoration/ruin/RuinDecoratorApplier.java | 4 +- .../decoration/ruin/RuinGenerator.java | 10 +- .../decoration/tree/IrisProceduralTree.java | 2 +- .../tree/ProceduralTreeGenerator.java | 10 +- .../decoration/tree/TreeBlockMaterial.java | 4 +- .../decoration/tree/TreeBlockResolver.java | 20 +- .../decoration/tree/TreePlausibility.java | 8 +- .../generation/image/IrisImageMapRuntime.java | 12 +- .../CaveObjectPlacementTransaction.java | 16 +- .../iris/generation/mantle/EngineMantle.java | 20 +- .../FloatingObjectPlacementTransaction.java | 16 +- .../mantle/GoldenDebugObjectPlacer.java | 6 +- .../mantle/IrisStructureComponent.java | 54 +- .../generation/mantle/IslandObjectPlacer.java | 6 +- .../mantle/MantleCarvingComponent.java | 2 +- .../mantle/MantleFloatingObjectComponent.java | 6 +- .../mantle/MantleHydrologyCaveVoxelView.java | 4 +- .../mantle/MantleHydrologyComponent.java | 12 +- .../mantle/MantleObjectComponent.java | 4 +- .../mantle/MantleSliceRetention.java | 4 +- .../iris/generation/mantle/MantleWriter.java | 60 +- .../mantle/ObjectDestinationTransaction.java | 38 +- .../mantle/StructureCarveEnvelope.java | 147 -- .../mantle/StructureCarvingFootprint.java | 456 ----- .../mantle/StructureFoundationPlanner.java | 8 +- .../mantle/StructurePieceColumns.java | 59 + .../generation/mantle/TerrainMatterView.java | 4 +- .../iris/generation/noise/IrisExpression.java | 2 +- .../generation/noise/IrisExpressionLoad.java | 2 +- .../iris/generation/noise/IrisGenerator.java | 2 +- .../iris/generation/runtime/BlockUpdater.java | 4 +- .../runtime/DimensionStackLayout.java | 14 +- .../runtime/DimensionTerrainContext.java | 32 +- .../iris/generation/runtime/Engine.java | 10 +- .../generation/runtime/EngineDecorator.java | 8 +- .../iris/generation/runtime/EngineMode.java | 8 +- .../runtime/EnginePlatformHooks.java | 2 +- .../iris/generation/runtime/EngineStage.java | 6 +- .../runtime/GenerationSessionLease.java | 4 +- .../iris/generation/runtime/IrisComplex.java | 36 +- .../iris/generation/runtime/IrisEngine.java | 12 +- .../generation/runtime/IrisEngineMantle.java | 8 +- .../runtime/ResolvedTerrainProvider.java | 30 +- .../runtime/UpperDimensionContext.java | 10 +- .../stage/HydrologyFluidLayerSelector.java | 8 +- .../generation/stage/IrisBiomeActuator.java | 28 +- .../generation/stage/IrisCarveModifier.java | 126 +- .../generation/stage/IrisCustomModifier.java | 10 +- .../stage/IrisDecorantActuator.java | 22 +- .../generation/stage/IrisDepositModifier.java | 54 +- .../stage/IrisDimensionStackActuator.java | 20 +- .../stage/IrisFloatingChildBiomeModifier.java | 48 +- .../stage/IrisPerfectionModifier.java | 32 +- .../generation/stage/IrisPostModifier.java | 66 +- .../stage/IrisTerrainNormalActuator.java | 42 +- .../stage/IrisTransitionGeometryActuator.java | 34 +- .../generation/terrain/IrisDimension.java | 20 +- .../terrain/IrisDimensionCarvingEntry.java | 2 +- .../terrain/IrisDimensionRuntimeContract.java | 2 +- .../generation/terrain/IrisDimensionType.java | 2 +- .../terrain/IrisMaterialPalette.java | 20 +- .../iris/generation/terrain/IrisRegion.java | 14 +- .../validation/GoldenHashEngine.java | 20 +- .../validation/GoldenHashScanner.java | 8 +- .../integration/ExternalDataProvider.java | 6 +- .../iris/integration/ExternalDataSVC.java | 4 +- .../iris/integration/WorldEditLink.java | 2 +- .../data/CraftEngineDataProvider.java | 4 +- .../data/MythicCrucibleDataProvider.java | 6 +- .../integration/data/NexoDataProvider.java | 6 +- .../arcane/iris/pack/ContentKeyValidator.java | 18 +- .../pack/DefaultPackBootstrapProvisioner.java | 4 +- .../iris/pack/PackImageMapValidator.java | 4 +- .../arcane/iris/pack/PackSpawnValidator.java | 4 +- .../pack/PackStructurePlacementValidator.java | 2 +- .../nms => pack}/datapack/DataVersion.java | 24 +- .../datapack/DatapackStructureScopeIndex.java | 4 +- .../nms => pack}/datapack/IDataFixer.java | 2 +- .../pack/datapack/IrisDatapackCompiler.java | 4 +- .../pack/datapack/ServerConfigurator.java | 8 +- .../datapack/v1192/DataFixerV1192.java | 4 +- .../datapack/v1206/DataFixerV1206.java | 4 +- .../datapack/v1213/DataFixerV1213.java | 4 +- .../datapack/v1217/DataFixerV1217.java | 4 +- .../datapack/v263/DataFixerV263.java | 4 +- .../arcane/iris/pack/loading/IrisData.java | 6 +- .../iris/pack/loading/ResourceLoader.java | 2 +- .../iris/pack/schema/SchemaBlockStates.java | 18 +- .../iris/pack/validation/ContentGate.java | 18 +- .../IrisCompatabilityBlockFilter.java | 2 +- .../IrisCompatabilityItemFilter.java | 2 +- .../pack/validation/MissingBlockState.java | 18 +- .../art/arcane/iris/pack/value/IrisColor.java | 2 +- .../arcane/iris/platform/BlockStateKey.java | 47 - .../bootstrap/NativeRuntimeLibraries.java | 152 ++ .../iris/platform/bootstrap/SlimJar.java | 74 +- .../iris/platform/bukkit/BukkitBiome.java | 4 +- .../platform/bukkit/BukkitBiomeWriter.java | 8 +- .../bukkit/BukkitBlockResolution.java | 4 +- .../platform/bukkit/BukkitBlockState.java | 21 +- .../platform/bukkit/BukkitEntityType.java | 4 +- .../iris/platform/bukkit/BukkitItem.java | 4 +- .../iris/platform/bukkit/BukkitPlatform.java | 10 +- .../platform/bukkit/BukkitRegistries.java | 46 +- .../iris/platform/bukkit/BukkitScheduler.java | 6 +- .../platform/bukkit/BukkitStructureHooks.java | 12 +- .../iris/platform/bukkit/BukkitWorld.java | 14 +- .../platform/bukkit/nms/BukkitBinding.java | 161 ++ .../bukkit/nms/BukkitBiomePolicy.java | 514 ++--- .../bukkit/nms/BukkitGenerationPolicy.java | 104 ++ .../bukkit/nms/BukkitGenerationRegistry.java | 54 + .../bukkit/nms/BukkitGeneratorContext.java | 138 ++ .../nms/BukkitStructureBootstrapPolicy.java | 23 + .../nms/BukkitStructureStagePolicy.java | 133 ++ .../bukkit/nms/BukkitTerrainColumnPolicy.java | 119 ++ .../bukkit/nms/BukkitTileWriteScheduler.java | 19 + .../nms/BukkitWorldLifecyclePolicy.java | 71 + .../arcane/iris/platform/bukkit/nms/INMS.java | 46 +- .../iris/platform/bukkit/nms/INMSBinding.java | 57 +- .../bukkit/nms/NmsBindingSelector.java | 23 - .../nms/SavedTerrainSnapshotPolicy.java | 20 + .../bukkit/nms/ServerShutdownBoundary.java | 58 - .../bukkit/nms/container/BiomeColor.java | 10 - .../bukkit/nms/container/BlockProperty.java | 204 -- .../platform/bukkit/nms/container/Pair.java | 13 - .../platform/bukkit/nms/v1X/NMSBinding1X.java | 59 +- .../generation/BukkitChunkGenerator.java | 10 +- .../generation/DummyBiomeProvider.java | 6 +- .../iris/platform/reflect/KeyedType.java | 4 +- .../{bukkit => }/registry/Attributes.java | 2 +- .../{bukkit => }/registry/KeyedRegistry.java | 2 +- .../{bukkit => }/registry/Materials.java | 4 +- .../{bukkit => }/registry/Particles.java | 4 +- .../registry/RegistryTypeAdapter.java | 6 +- .../{bukkit => }/registry/RegistryUtil.java | 16 +- .../iris/structure/BulkStructureImporter.java | 128 +- .../iris/structure/VillageImporter.java | 500 +---- .../export/VanillaJigsawExportCompiler.java | 8 +- .../VanillaStructureTemplateEncoder.java | 4 +- .../jigsaw/IrisJigsawConfiguration.java | 4 +- .../structure/jigsaw/IrisJigsawHeightmap.java | 16 +- .../jigsaw/IrisJigsawLiquidSettings.java | 11 +- .../nativegen/GenerationWritePolicy.java | 49 + .../nativegen/ImportedFeaturePolicy.java | 130 ++ .../nativegen/IrisImportedFeatureControl.java | 13 +- .../IrisImportedStructureControl.java | 4 +- .../IrisNativeStructureDecision.java | 4 +- .../nativegen/IrisStructurePolicy.java | 103 ++ .../nativegen/IrisStructureVolumePolicy.java | 43 + .../NativeStructureFrequencyScale.java | 63 - .../NativeStructureOwnershipRecord.java | 41 +- .../nativegen/NativeStructureStartPlan.java | 38 +- .../nativegen/NativeStructureVolume.java | 105 -- .../nativegen/NativeStructureVolumeIndex.java | 234 +-- .../nativegen/NativeStructureVolumeMemo.java | 2 + .../nativegen/StructureLocateSearch.java | 64 + .../structure/object/DecayControlPlacer.java | 6 +- .../object/HeightmapObjectPlacer.java | 6 +- .../iris/structure/object/IObjectLoot.java | 4 +- .../iris/structure/object/IObjectPlacer.java | 6 +- .../structure/object/IPostBlockAccess.java | 6 +- .../iris/structure/object/IrisObject.java | 40 +- .../iris/structure/object/IrisObjectIO.java | 24 +- .../iris/structure/object/IrisObjectLoot.java | 16 +- .../structure/object/IrisObjectMarker.java | 12 +- .../structure/object/IrisObjectPlacement.java | 10 +- .../object/IrisObjectPlacementRunner.java | 60 +- .../structure/object/IrisObjectReplace.java | 18 +- .../structure/object/IrisObjectRotation.java | 6 +- .../structure/object/IrisObjectShaping.java | 24 +- .../object/IrisObjectTransforms.java | 52 +- .../object/IrisObjectVanillaLoot.java | 12 +- .../object/IrisStaticObjectLayer.java | 12 +- .../structure/object/WorldObjectPlacer.java | 6 +- .../placement/IrisStructureCarveShape.java | 36 - .../placement/IrisStructureLocator.java | 42 +- .../placement/IrisStructureStiltSettings.java | 4 +- .../placement/IrisStructureTerrain.java | 20 +- .../placement/IrisStructureTerrainMode.java | 27 - .../placement/IrisStructureYBand.java | 4 +- .../placement/StructureVerticalBounds.java | 51 - .../art/arcane/iris/studio/StudioSVC.java | 2 +- .../arcane/iris/studio/edit/DustRevealer.java | 2 +- .../generation/JigsawStudioGenerator.java | 60 +- .../generation/ObjectStudioGenerator.java | 28 +- .../studio/jigsaw/JigsawStudioCapture.java | 16 +- .../jigsaw/JigsawStudioChunkWriter.java | 4 +- .../jigsaw/JigsawStudioObjectResizer.java | 26 +- .../jigsaw/JigsawStudioPreviewRenderer.java | 12 +- .../iris/studio/tree/TreePlausibilizer.java | 38 +- .../arcane/iris/studio/view/GuiMarker.java | 29 - .../arcane/iris/studio/view/GuiOverlay.java | 6 +- .../arcane/iris/studio/view/VisionGUI.java | 20 +- .../art/arcane/iris/world/IrisCreator.java | 17 +- .../art/arcane/iris/world/IrisTimeBlock.java | 4 +- .../art/arcane/iris/world/IrisWeather.java | 4 +- .../java/art/arcane/iris/world/IrisWorld.java | 4 +- .../art/arcane/iris/world/IrisWorlds.java | 2 +- .../arcane/iris/world/entity/IrisEffect.java | 4 +- .../arcane/iris/world/entity/IrisEntity.java | 12 +- .../iris/world/entity/IrisEntitySpawn.java | 2 +- .../iris/world/entity/IrisPotionEffect.java | 2 +- .../arcane/iris/world/entity/IrisSpawner.java | 6 +- .../world/history/BoundaryColumnGeometry.java | 9 +- .../GenerationEpochContractFactory.java | 2 +- .../GenerationHistoryRuntimeRouter.java | 13 +- .../GenerationRegistryContractFactory.java | 4 +- .../history/GenerationSemanticCapture.java | 17 +- .../history/NativeBiomeSpawnSelection.java | 9 +- .../world/history/NativeTerrainReceipt.java | 2 - .../history/SavedTerrainCapturePolicy.java | 47 + .../history/SavedTerrainChunkReader.java | 18 +- .../world/history/TerrainNativeBlockKeys.java | 20 + .../world/lifecycle/CapabilityResolution.java | 194 -- .../world/lifecycle/CapabilitySnapshot.java | 271 +-- .../lifecycle/PaperLikeRuntimeBackend.java | 43 +- .../lifecycle/WorldLifecycleService.java | 2 +- .../lifecycle/WorldLifecycleSupport.java | 716 +------ .../iris/world/loot/IrisBlockDrops.java | 6 +- .../art/arcane/iris/world/loot/IrisLoot.java | 2 +- .../iris/world/loot/IrisLootReference.java | 2 +- .../iris/world/loot/IrisVanillaLootTable.java | 2 +- .../iris/world/pregen/AsyncPregenMethod.java | 31 +- .../runtime/InPlaceChunkRegenerator.java | 4 +- .../PaperLikeRuntimeControlBackend.java | 257 +-- .../runtime/WorldRuntimeControlService.java | 54 +- .../arcane/iris/world/safeguard/Tasks.java | 7 +- .../storage/matter/PlatformBlockMatter.java | 14 +- .../world/storage/matter/PreObjectMatter.java | 4 +- .../storage/matter/PreObjectMatterCell.java | 12 +- .../iris/world/storage/region/NBTWorld.java | 6 +- .../iris/world/storage/region/Section.java | 4 +- .../art/arcane/iris/world/tree/TreeSVC.java | 6 +- .../handlers/DataVersionHandlerTest.java | 2 +- .../specialhandlers/StructureHandlerTest.java | 6 +- .../biome/IrisBiomeCeilingLayerTest.java | 22 +- .../biome/IrisBiomeCustomCompatTest.java | 2 +- .../biome/IrisBiomeLayerSlopeSourceTest.java | 14 +- .../biome/IrisBiomeLockedLayerTest.java | 44 +- .../biome/IrisSurfaceLayerFallbackTest.java | 22 +- .../block/PlatformStateRebindTest.java | 22 +- .../generation/cache/AtomicCacheTest.java | 2 + .../decoration/DecoratorCoreTest.java | 188 +- .../IrisDecoratorCaveContextTest.java | 78 +- .../decoration/IrisDepositTuningTest.java | 18 +- .../decoration/IrisOreGeneratorTest.java | 4 +- .../IrisSeaDecoratorBoundsTest.java | 10 +- .../IrisShoreLineDecoratorTest.java | 76 +- .../IrisSugarCanePlacementTest.java | 18 +- .../decoration/IrisSugarCaneTest.java | 24 +- .../decoration/coral/CoralGeneratorTest.java | 8 +- .../formation/FormationBlockResolverTest.java | 6 +- .../tree/ProceduralTreeGeneratorTest.java | 10 +- .../tree/TreeBlockResolverTest.java | 18 +- .../CaveObjectPlacementTransactionTest.java | 14 +- .../mantle/EngineMantleCleanupTest.java | 12 +- .../mantle/EngineMantleCoverageTest.java | 6 +- ...loatingObjectPlacementTransactionTest.java | 10 +- .../IrisStructureComponentMarkerTest.java | 10 +- .../IrisStructureFoundationWriteTest.java | 4 +- .../MantleCarvingComponentScratchTest.java | 4 +- .../MantleHydrologyCaveVoxelViewTest.java | 14 +- .../mantle/MantleHydrologyComponentTest.java | 6 +- ...ntleObjectComponentBoundaryRadiusTest.java | 4 +- .../mantle/MantleWriterOverlayTest.java | 36 +- .../MantleWriterPreObjectJournalTest.java | 24 +- .../ObjectDestinationTransactionTest.java | 22 +- .../mantle/ResolvedTerrainPlacementTest.java | 12 +- .../mantle/StructureCarveEnvelopeTest.java | 20 +- .../mantle/StructureCarvingFootprintTest.java | 34 +- .../StructureFoundationPlannerTest.java | 20 +- .../mantle/TerrainMatterViewTest.java | 6 +- .../DimensionTerrain3DCompositionTest.java | 42 +- .../DimensionTerrainContextFallbackTest.java | 10 +- .../runtime/EngineHostHeightTest.java | 8 +- .../runtime/EngineShutdownDrainTest.java | 4 +- .../IrisCarveWallPaletteScopeTest.java | 20 +- .../runtime/IrisComplexBiomeBuffetTest.java | 6 +- .../runtime/ResolvedTerrainProviderTest.java | 34 +- .../UpperDimensionImageMapRuntimeTest.java | 8 +- .../IrisBiomeActuatorCoordinateTest.java | 18 +- .../IrisCarveModifierBoundarySupportTest.java | 40 +- .../IrisCarveModifierFluidIntentTest.java | 18 +- ...risCarveModifierHydrologyMaterialTest.java | 8 +- .../stage/IrisCarveModifierHydrologyTest.java | 30 +- .../stage/IrisDecorantActuatorRiverTest.java | 30 +- .../IrisDepositModifierContainmentTest.java | 26 +- .../stage/IrisDepositModifierContextTest.java | 4 +- .../IrisDepositModifierOrderingTest.java | 28 +- .../stage/IrisDepositModifierParityTest.java | 30 +- ...risDimensionStackActuatorMetadataTest.java | 6 +- .../stage/IrisNaturalCaveScanTest.java | 32 +- .../IrisPerfectionModifierSpeleothemTest.java | 26 +- .../stage/IrisPostModifierWallShoreTest.java | 16 +- ...risTerrainNormalActuatorHydrologyTest.java | 30 +- ...risTerrainNormalActuatorStrataCutTest.java | 20 +- .../IrisTransitionGeometryActuatorTest.java | 32 +- .../validation/GoldenHashEngineTest.java | 28 +- .../ExternalDataSVCBlockRoutingTest.java | 4 +- .../iris/pack/ContentKeyValidatorTest.java | 42 +- .../DefaultPackBootstrapProvisionerTest.java | 2 +- .../PackValidatorDimensionHeightTest.java | 2 +- .../pack/PackValidatorStructureGraphTest.java | 2 +- .../datapack/DataVersionTest.java | 2 +- .../datapack/IrisDatapackCompilerTest.java | 4 +- ...rConfiguratorRegistryVerificationTest.java | 2 +- .../v1217/DataFixerV1217ClockTest.java | 4 +- .../v1217/DataFixerV1217CustomBiomeTest.java | 2 +- .../DataFixerV1217DimensionTypeTest.java | 4 +- .../loading/IrisDataAuthoringCacheTest.java | 8 +- .../schema/SchemaBuilderIdentityTest.java | 50 +- .../pack/schema/SchemaBuilderParityTest.java | 36 +- .../iris/pack/validation/CompatFixtures.java | 42 +- .../ContentGatePlaceholderTest.java | 24 +- .../IrisBlockDataResolutionTest.java | 6 +- .../value/PlatformStateHookContractTest.java | 14 +- .../iris/platform/BlockStateKeyTest.java | 36 - .../bootstrap/NativeRuntimeLibrariesTest.java | 53 + .../bukkit/BoardSVCSavedBiomeTest.java | 4 +- .../iris/platform/bukkit/BukkitBiomeTest.java | 20 +- .../BukkitCustomBlockTransformTest.java | 26 +- .../bukkit/BukkitSpiConformanceTest.java | 22 +- .../bukkit/nms/BukkitBindingTest.java | 51 + .../bukkit/nms/NmsBindingSelectorTest.java | 54 - .../nms/ServerShutdownBoundaryTest.java | 2 + ...BukkitChunkGeneratorHistoryTargetTest.java | 2 +- ...kkitChunkGeneratorStudioSelectionTest.java | 2 +- ...eBukkitOpsDeferredMaterializationTest.java | 4 +- .../purity/PackTypeBukkitPurityGateTest.java | 2 +- ...VillageImporterConnectorRoundTripTest.java | 23 +- .../VillageImporterListPoolElementTest.java | 60 +- .../VillageImporterMaxDistanceReadTest.java | 44 - .../VanillaJigsawDatapackExporterTest.java | 10 +- .../IrisImportedStructureControlTest.java | 16 +- .../NativeStructureFrequencyScaleTest.java | 2 + .../NativeStructureObjectVetoTest.java | 16 +- .../NativeStructureOwnershipRecordTest.java | 17 +- .../NativeStructureOwnershipStoreTest.java | 4 +- .../NativeStructurePlacementPlannerTest.java | 4 +- .../NativeStructureVolumeMemoTest.java | 2 + .../nativegen/NativeStructureVolumeTest.java | 2 + .../nativegen/StructureLocateSearchTest.java | 68 + .../structure/object/IrisObjectCopyTest.java | 6 +- .../object/IrisObjectDirectPlacementTest.java | 4 +- .../object/IrisObjectGlobalScaleTest.java | 4 +- .../object/IrisObjectIoPaletteTest.java | 4 +- .../object/IrisObjectIoWriteLimitsTest.java | 4 +- ...isObjectPlacementRunnerRegressionTest.java | 64 +- .../object/IrisObjectScaleCacheTest.java | 4 +- .../object/IrisObjectShapingTest.java | 16 +- ...IrisObjectSurfaceSupportPlacementTest.java | 12 +- ...ObjectTransformsNearestBlockIndexTest.java | 42 +- .../object/IrisObjectTransformsTileTest.java | 20 +- .../object/IrisObjectTransientBlockTest.java | 8 +- .../object/IrisStaticObjectLayerTest.java | 12 +- .../object/IrisSurfaceSupportTest.java | 10 +- .../IrisStructureLocatorContractTest.java | 14 +- ...isStructurePlacementCarveSettingsTest.java | 34 +- .../StructureReachabilityContractTest.java | 6 +- .../StructureVerticalBoundsTest.java | 2 + .../generation/JigsawStudioGeneratorTest.java | 54 +- .../generation/ObjectStudioGeometryTest.java | 26 +- .../JigsawStudioGraphEditorResizeTest.java | 18 +- .../JigsawStudioPreviewRendererTest.java | 8 +- .../JigsawStudioServiceCaptureTest.java | 28 +- .../studio/tree/TreePlausibilizerTest.java | 12 +- .../workspace/ExternalBlockSchemaTest.java | 4 +- .../iris/testsupport/CompatTestPlatform.java | 36 +- .../iris/testsupport/KeyedBlockState.java | 12 +- .../history/ArchivedRegistryCompilerTest.java | 2 +- .../GenerationEpochContractFactoryTest.java | 2 +- ...GenerationHistoryRuntimeRouterSupport.java | 4 +- ...GenerationRegistryContractFactoryTest.java | 4 +- .../GenerationSemanticCaptureTest.java | 4 +- .../world/history/SavedTerrainChunkTest.java | 10 +- .../lifecycle/CapabilityResolutionTest.java | 164 -- .../lifecycle/CapabilitySnapshotFixtures.java | 131 +- .../WorldLifecycleRuntimeLevelStemTest.java | 135 -- .../lifecycle/WorldLifecycleSeedTest.java | 104 -- ...WorldLifecycleSupportCloseFailureTest.java | 66 - .../WorldLifecycleUnloadAsyncTest.java | 177 -- ...aperLikeRuntimeControlBackendTimeTest.java | 82 - ...orldRuntimeControlServiceTimeLockTest.java | 180 +- .../storage/matter/PreObjectMatterTest.java | 8 +- .../iris/testsupport/PlatformBinding.java | 8 +- gradle.properties | 2 - gradle/generation-build-revision.gradle | 19 +- .../modded-generation-build-revision.gradle | 2 +- gradle/native-minecraft-sources.gradle | 35 + gradle/volmlib-resolution.settings.gradle | 6 + packaging/shrink.pro | 6 + .../iris/probe/GenerationOrderProbe.java | 16 +- .../arcane/iris/probe/GenerationProbe.java | 14 +- .../iris/probe/HydrologyBlockProbe.java | 26 +- .../arcane/iris/probe/HydrologyPackProbe.java | 38 +- .../iris/probe/RealPackProbeSupport.java | 16 +- .../art/arcane/iris/probe/StubPlatform.java | 70 +- .../art/arcane/iris/probe/StubTileData.java | 4 +- .../art/arcane/iris/probe/Terrain3DProbe.java | 22 +- .../DimensionStackSuperflatProbeTest.java | 4 +- .../iris/probe/GenerationOrderProbeTest.java | 20 +- .../iris/probe/HydrologyBlockProbeTest.java | 10 +- .../iris/probe/HydrologyPackProbeTest.java | 22 +- .../iris/probe/StubPlatformStateTest.java | 26 +- spi/build.gradle | 3 + .../art/arcane/iris/spi/IrisPlatform.java | 10 +- .../art/arcane/iris/spi/PlatformBiome.java | 44 - .../arcane/iris/spi/PlatformBiomeWriter.java | 4 +- .../iris/spi/PlatformBlockProperty.java | 43 - .../arcane/iris/spi/PlatformBlockState.java | 201 -- .../arcane/iris/spi/PlatformEntityType.java | 51 - .../iris/spi/PlatformGenerationRegistry.java | 52 +- .../art/arcane/iris/spi/PlatformItem.java | 45 - .../arcane/iris/spi/PlatformNumericRange.java | 35 - .../arcane/iris/spi/PlatformRegistries.java | 28 +- .../arcane/iris/spi/PlatformScheduler.java | 8 +- .../iris/spi/PlatformStructureHooks.java | 30 +- .../art/arcane/iris/spi/PlatformWorld.java | 97 - .../spi/IrisPlatformsBindContractTest.java | 4 +- .../iris/spi/PlatformValueContractTest.java | 46 +- 892 files changed, 13482 insertions(+), 53554 deletions(-) delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/DatapackStructureStateFilter.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/ImportedFeatureStage.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGenerator.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdates.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccess.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationHooks.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationRegistry.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCapture.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/VanillaStructureBiomes.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCheckpointTest.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainSnapshotTest.java delete mode 100644 adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainWriteBarrierTest.java rename adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/{ => structure}/nativegen/NativeStructureVolumeIndexTest.java (96%) create mode 100644 adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/ForcedStructureChunkGenerator.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeBuildFutures.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeGenerationWriteGuard.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFactory.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFoundationBuilder.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationException.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationKeys.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocatePersistence.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocateResults.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipFingerprint.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipRecovery.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructurePostProcessor.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceEnvelope.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceRepair.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReflection.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureStartInjector.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceFitter.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceSupportBuilder.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplateOccupancy.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplatePoolBounds.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTerrainIntegrator.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVanillaLocator.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVegetationClearer.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVerticalPlacer.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVolumeIndex.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTerrainHeightCache.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTransitionColumn.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/WorldgenTerrainHeightmaps.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSource.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/DatapackStructureStateFilter.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/ImportedFeatureStage.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/InitialMobSpawner.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGenerator.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBinding.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdates.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccess.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationHooks.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationRegistry.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCapture.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/VanillaStructureBiomes.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCheckpointTest.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainSnapshotTest.java delete mode 100644 adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainWriteBarrierTest.java rename adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/{ => structure}/nativegen/NativeStructureVolumeIndexTest.java (96%) create mode 100644 adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/build.gradle create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/resources/plugin.yml create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/settings.gradle create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/src/art/arcane/iris/fixture/NativeLifecycleFixture.java create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-lifecycle.mjs create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/biomes/flat.json create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/dimensions/native-terrain.json create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/generators/flat.json create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/regions/flat.json create mode 100644 adapters/bukkit/plugin/src/test/gameplay/native-terrain.mjs delete mode 100644 adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldOpenFlowsMixin.java delete mode 100644 adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldTypeEntryMixin.java delete mode 100644 adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricForcedDatapackSources.java delete mode 100644 adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricModdedLoader.java delete mode 100644 adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockItemMixin.java delete mode 100644 adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockMixin.java delete mode 100644 adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/PackRepositoryMixin.java delete mode 100644 adapters/fabric/src/main/resources/irisworldgen.accesswidener delete mode 100644 adapters/fabric/src/main/resources/irisworldgen.mixins.json delete mode 100644 adapters/forge/src/main/java/art/arcane/iris/forge/ForgeClientProtocol.java delete mode 100644 adapters/forge/src/main/java/art/arcane/iris/forge/ForgeModdedLoader.java delete mode 100644 adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBlockLootModifier.java delete mode 100644 adapters/forge/src/main/resources/META-INF/accesstransformer.cfg delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/ForcedStructureChunkGenerator.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeBuildFutures.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeGenerationWriteGuard.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFactory.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFoundationBuilder.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationException.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationKeys.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocatePersistence.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocateResults.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprint.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecovery.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructurePostProcessor.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceEnvelope.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceRepair.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReflection.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureStartInjector.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceFitter.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceSupportBuilder.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancy.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplatePoolBounds.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTerrainIntegrator.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVanillaLocator.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVegetationClearer.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVerticalPlacer.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTerrainHeightCache.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTransitionColumn.java delete mode 100644 adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/WorldgenTerrainHeightmaps.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisItemRecipe.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiome.java rename adapters/modded-common/src/main/java/art/arcane/iris/modded/{IrisModdedBiomeSource.java => ModdedBiomePolicy.java} (73%) delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockState.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityAwareness.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityPersistence.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityType.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedHeightmaps.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeaturePolicy.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisPayload.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItem.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLifecycleCallbacks.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLoader.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinFlags.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructurePolicy.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureStage.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccess.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatformWorld.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolChannel.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolDefinition.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRuntimeRegistry.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerAccess.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerCommands.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerLevels.java create mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnBiomePolicy.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnTableMerger.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateMerger.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverrides.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTeleportBounds.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileReader.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeSpawnTableMerger.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeTerrainReceiptHolder.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkMapTerrainReceiptAccess.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkTerrainReceiptMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/EntityPersistenceMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LevelChunkTerrainReceiptMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LivingEntityLootMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/MobAwarenessMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/SerializableTerrainReceiptMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java delete mode 100644 adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/WorldGenerationWriteGuardMixin.java delete mode 100644 adapters/modded-common/src/main/resources/irisworldgen.client.mixins.json delete mode 100644 adapters/modded-common/src/main/resources/irisworldgen.entity.mixins.json create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeathLootTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedImportedFeatureStageTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeCommandExecutorTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeItemTranslatorTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/NativePayloadProtocolTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeRegistryAccessTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStateOperationsTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedPregenMethodLifecycleTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeEditWorldTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeTaggedItemsTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/BrigadierSourceMapperTest.java rename adapters/modded-common/src/test/java/art/arcane/{iris => volmlib/nativelib/minecraft26_2}/modded/ModdedLevelDataAttachmentTest.java (94%) rename adapters/modded-common/src/test/java/art/arcane/{iris => volmlib/nativelib/minecraft26_2}/modded/ModdedServerLevelsTest.java (94%) create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeChunkRegenerationTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeCommandTextTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeGeneratorOwnershipTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeHarvestSessionTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeMixinRegistrationTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativePregenRuntimeTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerLogMessagesTest.java rename adapters/modded-common/src/test/java/art/arcane/{iris/modded/MainWorldServiceInstanceRootTest.java => volmlib/nativelib/minecraft26_2/modded/NativeServerWorldLayoutTest.java} (56%) create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnBiomeTablesTest.java rename adapters/modded-common/src/test/java/art/arcane/{iris/modded/ModdedStructureHooksTest.java => volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java} (63%) rename adapters/modded-common/src/test/java/art/arcane/{iris/modded/ModdedDimensionManagerTeleportTest.java => volmlib/nativelib/minecraft26_2/modded/NativeTeleportBoundsTest.java} (90%) create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldIdentityTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldInspectionTest.java create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldMaintenanceTest.java delete mode 100644 adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeModdedLoader.java delete mode 100644 adapters/neoforge/src/main/resources/META-INF/accesstransformer.cfg delete mode 100644 buildSrc/src/main/java/Config.java delete mode 100644 buildSrc/src/main/java/NMSBinding.java create mode 100644 buildSrc/src/main/java/NativeRuntimeArtifacts.java create mode 100644 buildSrc/src/test/java/NativeRuntimeArtifactsTest.java delete mode 100644 core/src/main/java/art/arcane/iris/generation/cache/AtomicCache.java delete mode 100644 core/src/main/java/art/arcane/iris/generation/mantle/StructureCarveEnvelope.java delete mode 100644 core/src/main/java/art/arcane/iris/generation/mantle/StructureCarvingFootprint.java create mode 100644 core/src/main/java/art/arcane/iris/generation/mantle/StructurePieceColumns.java rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/DataVersion.java (70%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/IDataFixer.java (94%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1192/DataFixerV1192.java (96%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1206/DataFixerV1206.java (95%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1213/DataFixerV1213.java (76%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1217/DataFixerV1217.java (98%) rename core/src/main/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v263/DataFixerV263.java (94%) delete mode 100644 core/src/main/java/art/arcane/iris/platform/BlockStateKey.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibraries.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBinding.java rename adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSource.java => core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBiomePolicy.java (65%) create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationPolicy.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationRegistry.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGeneratorContext.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureBootstrapPolicy.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureStagePolicy.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTerrainColumnPolicy.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTileWriteScheduler.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitWorldLifecyclePolicy.java delete mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelector.java create mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/SavedTerrainSnapshotPolicy.java delete mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundary.java delete mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BiomeColor.java delete mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BlockProperty.java delete mode 100644 core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/Pair.java rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/Attributes.java (78%) rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/KeyedRegistry.java (98%) rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/Materials.java (53%) rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/Particles.java (91%) rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/RegistryTypeAdapter.java (87%) rename core/src/main/java/art/arcane/iris/platform/{bukkit => }/registry/RegistryUtil.java (91%) create mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/GenerationWritePolicy.java create mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/ImportedFeaturePolicy.java create mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructurePolicy.java create mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructureVolumePolicy.java delete mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScale.java delete mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolume.java rename {adapters/minecraft-common/src/main/java/art/arcane/iris => core/src/main/java/art/arcane/iris/structure}/nativegen/NativeStructureVolumeIndex.java (60%) create mode 100644 core/src/main/java/art/arcane/iris/structure/nativegen/StructureLocateSearch.java delete mode 100644 core/src/main/java/art/arcane/iris/structure/placement/IrisStructureCarveShape.java delete mode 100644 core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrainMode.java delete mode 100644 core/src/main/java/art/arcane/iris/structure/placement/StructureVerticalBounds.java delete mode 100644 core/src/main/java/art/arcane/iris/studio/view/GuiMarker.java create mode 100644 core/src/main/java/art/arcane/iris/world/history/SavedTerrainCapturePolicy.java create mode 100644 core/src/main/java/art/arcane/iris/world/history/TerrainNativeBlockKeys.java rename core/src/test/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/DataVersionTest.java (96%) rename core/src/test/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1217/DataFixerV1217ClockTest.java (90%) rename core/src/test/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1217/DataFixerV1217CustomBiomeTest.java (97%) rename core/src/test/java/art/arcane/iris/{platform/bukkit/nms => pack}/datapack/v1217/DataFixerV1217DimensionTypeTest.java (93%) delete mode 100644 core/src/test/java/art/arcane/iris/platform/BlockStateKeyTest.java create mode 100644 core/src/test/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibrariesTest.java create mode 100644 core/src/test/java/art/arcane/iris/platform/bukkit/nms/BukkitBindingTest.java delete mode 100644 core/src/test/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelectorTest.java delete mode 100644 core/src/test/java/art/arcane/iris/structure/VillageImporterMaxDistanceReadTest.java create mode 100644 core/src/test/java/art/arcane/iris/structure/nativegen/StructureLocateSearchTest.java delete mode 100644 core/src/test/java/art/arcane/iris/world/lifecycle/CapabilityResolutionTest.java delete mode 100644 core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleRuntimeLevelStemTest.java delete mode 100644 core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSeedTest.java delete mode 100644 core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupportCloseFailureTest.java delete mode 100644 core/src/test/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackendTimeTest.java create mode 100644 gradle/native-minecraft-sources.gradle delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformBiome.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformBlockProperty.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformBlockState.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformEntityType.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformItem.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformNumericRange.java delete mode 100644 spi/src/main/java/art/arcane/iris/spi/PlatformWorld.java diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/DatapackStructureStateFilter.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/DatapackStructureStateFilter.java deleted file mode 100644 index d642e45ef..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/DatapackStructureStateFilter.java +++ /dev/null @@ -1,509 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; -import art.arcane.iris.structure.nativegen.NativeStructureFrequencyScale; -import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderOwner; -import net.minecraft.core.Vec3i; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -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; - -final class DatapackStructureStateFilter { - private DatapackStructureStateFilter() { - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources - ) { - return filter(structureSets, scopeIndex, declaredSources, new IrisImportedStructureControl()); - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures - ) { - return filter( - structureSets, - scopeIndex, - declaredSources, - importedStructures, - keyIndex(structureSets)); - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures, - StructureSetKeyIndex keyIndex - ) { - if (scopeIndex.isEmpty()) { - return new Selection( - scaleFrequencyOverrides(structureSets, importedStructures, keyIndex), - 0, - 0); - } - Map> holdersByKey = new HashMap<>(); - for (Holder holder : structureSets) { - String key = structureSetKey(holder, keyIndex); - if (key != null) { - holdersByKey.putIfAbsent(key, holder); - } - } - Map, Holder> scopedByIdentity = new IdentityHashMap<>(); - Set> visiting = Collections.newSetFromMap(new IdentityHashMap<>()); - List> filteredSets = new ArrayList<>(structureSets.size()); - int retainedManagedSets = 0; - int excludedManagedSets = 0; - for (Holder holder : structureSets) { - String setKey = structureSetKey(holder, keyIndex); - boolean managedSet = setKey != null && scopeIndex.isManagedStructureSet(setKey); - Holder scopedHolder = scopeHolder( - holder, - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - if (scopedHolder == null) { - if (managedSet) { - excludedManagedSets++; - } - continue; - } - if (managedSet) { - retainedManagedSets++; - } - filteredSets.add(scopedHolder); - } - return new Selection( - scaleFrequencyOverrides( - List.copyOf(filteredSets), importedStructures, keyIndex), - retainedManagedSets, - excludedManagedSets); - } - - static String structureSetKey(Holder holder) { - return structureSetKey(holder, keyIndex(List.of(holder))); - } - - static StructureSetKeyIndex keyIndex( - Iterable> registeredStructureSets - ) { - return StructureSetKeyIndex.create(registeredStructureSets); - } - - private static String structureSetKey( - Holder holder, - StructureSetKeyIndex keyIndex - ) { - return keyIndex.resolve(holder) - .map(key -> key.identifier().toString()) - .orElse(null); - } - - private static List filterEntries( - List entries, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources - ) { - List filtered = new ArrayList<>(entries.size()); - for (StructureSet.StructureSelectionEntry entry : entries) { - String structureKey = entry.structure().unwrapKey() - .map(key -> key.identifier().toString()) - .orElse(null); - if (structureKey == null || scopeIndex.allowsStructure(structureKey, declaredSources)) { - filtered.add(entry); - } - } - return filtered; - } - - private static Holder scopeHolder( - Holder holder, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - Map> holdersByKey, - Map, Holder> scopedByIdentity, - Set> visiting, - StructureSetKeyIndex keyIndex - ) { - if (scopedByIdentity.containsKey(holder)) { - return scopedByIdentity.get(holder); - } - if (!visiting.add(holder)) { - return null; - } - try { - String setKey = structureSetKey(holder, keyIndex); - if (setKey != null && scopeIndex.isManagedStructureSet(setKey) - && !scopeIndex.allowsStructureSet(setKey, declaredSources)) { - scopedByIdentity.put(holder, null); - return null; - } - StructureSet originalSet = holder.value(); - List entries = filterEntries( - originalSet.structures(), scopeIndex, declaredSources); - if (entries.isEmpty()) { - scopedByIdentity.put(holder, null); - return null; - } - StructurePlacement placement = scopePlacement( - originalSet.placement(), - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - if (entries.size() == originalSet.structures().size() - && placement == originalSet.placement()) { - scopedByIdentity.put(holder, holder); - return holder; - } - Holder scoped = replacementHolder( - holder, new StructureSet(entries, placement), keyIndex); - scopedByIdentity.put(holder, scoped); - return scoped; - } finally { - visiting.remove(holder); - } - } - - static Optional exclusionZone(StructurePlacement placement) { - Object value = declaredFieldValue(StructurePlacement.class, placement, Optional.class); - if (value instanceof Optional optional && (optional.isEmpty() - || optional.get() instanceof StructurePlacement.ExclusionZone)) { - @SuppressWarnings("unchecked") - Optional resolved = - (Optional) optional; - return resolved; - } - throw new IllegalStateException("Could not read structure placement exclusion zone from " - + placement.getClass().getName()); - } - - private static StructurePlacement scopePlacement( - StructurePlacement placement, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - Map> holdersByKey, - Map, Holder> scopedByIdentity, - Set> visiting, - StructureSetKeyIndex keyIndex - ) { - Optional currentZone = exclusionZone(placement); - if (currentZone.isEmpty()) { - return placement; - } - - Holder target = currentZone.get().otherSet(); - String targetKey = structureSetKey(target, keyIndex); - if (targetKey != null) { - target = holdersByKey.getOrDefault(targetKey, target); - } - Holder scopedTarget = scopeHolder( - target, - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - Optional scopedZone = Optional.empty(); - if (scopedTarget != null) { - scopedZone = Optional.of(new StructurePlacement.ExclusionZone( - scopedTarget, - currentZone.get().chunkCount())); - } - if (scopedTarget == currentZone.get().otherSet()) { - return placement; - } - return copyPlacement(placement, scopedZone, 1D); - } - - private static List> scaleFrequencyOverrides( - List> structureSets, - IrisImportedStructureControl importedStructures, - StructureSetKeyIndex keyIndex - ) { - if (!importedStructures.hasFrequencyOverrides()) { - return structureSets; - } - Map> holdersByKey = new HashMap<>(); - for (Holder holder : structureSets) { - String key = structureSetKey(holder, keyIndex); - if (key != null) { - holdersByKey.putIfAbsent(key, holder); - } - } - Map, Holder> scaledByIdentity = new IdentityHashMap<>(); - List> scaledSets = new ArrayList<>(structureSets.size()); - boolean changed = false; - for (Holder holder : structureSets) { - Holder scaled = scaleFrequencyHolder( - holder, - importedStructures, - holdersByKey, - scaledByIdentity, - keyIndex); - scaledSets.add(scaled); - changed |= scaled != holder; - } - return changed ? List.copyOf(scaledSets) : structureSets; - } - - private static Holder scaleFrequencyHolder( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey, - Map, Holder> scaledByIdentity, - StructureSetKeyIndex keyIndex - ) { - if (scaledByIdentity.containsKey(holder)) { - return scaledByIdentity.get(holder); - } - if (!dependsOnFrequencyOverride( - holder, importedStructures, holdersByKey, keyIndex)) { - scaledByIdentity.put(holder, holder); - return holder; - } - - ResourceKey holderKey = keyIndex.resolve(holder).orElseThrow(() -> - new IllegalStateException( - "An affected native structure-set exclusion graph has an unkeyed holder")); - ReboundStructureSetHolder scaledHolder = new ReboundStructureSetHolder(holderKey); - scaledByIdentity.put(holder, scaledHolder); - - StructureSet originalSet = holder.value(); - StructurePlacement originalPlacement = originalSet.placement(); - Optional originalZone = exclusionZone(originalPlacement); - Optional scaledZone = originalZone; - if (originalZone.isPresent()) { - Holder target = canonicalHolder( - originalZone.get().otherSet(), holdersByKey, keyIndex); - Holder scaledTarget = scaleFrequencyHolder( - target, - importedStructures, - holdersByKey, - scaledByIdentity, - keyIndex); - if (scaledTarget != originalZone.get().otherSet()) { - scaledZone = Optional.of(new StructurePlacement.ExclusionZone( - scaledTarget, originalZone.get().chunkCount())); - } - } - - double multiplier = importedStructures.frequencyMultiplier( - structureSetKey(holder, keyIndex)); - StructurePlacement scaledPlacement = multiplier == 1D && scaledZone.equals(originalZone) - ? originalPlacement - : copyPlacement(originalPlacement, scaledZone, multiplier); - scaledHolder.bind(new StructureSet(originalSet.structures(), scaledPlacement)); - return scaledHolder; - } - - private static boolean dependsOnFrequencyOverride( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey, - StructureSetKeyIndex keyIndex - ) { - Set> visited = Collections.newSetFromMap(new IdentityHashMap<>()); - Holder current = holder; - while (visited.add(current)) { - if (importedStructures.frequencyMultiplier( - structureSetKey(current, keyIndex)) != 1D) { - return true; - } - Optional zone = - exclusionZone(current.value().placement()); - if (zone.isEmpty()) { - return false; - } - current = canonicalHolder(zone.get().otherSet(), holdersByKey, keyIndex); - } - return false; - } - - private static Holder canonicalHolder( - Holder holder, - Map> holdersByKey, - StructureSetKeyIndex keyIndex - ) { - String key = structureSetKey(holder, keyIndex); - return key == null ? holder : holdersByKey.getOrDefault(key, holder); - } - - private static Holder replacementHolder( - Holder original, - StructureSet replacement, - StructureSetKeyIndex keyIndex - ) { - Optional> key = keyIndex.resolve(original); - if (key.isEmpty()) { - return Holder.direct(replacement); - } - ReboundStructureSetHolder holder = new ReboundStructureSetHolder(key.get()); - holder.bind(replacement); - return holder; - } - - private static StructurePlacement copyPlacement( - StructurePlacement placement, - Optional exclusionZone, - double frequencyMultiplier - ) { - Vec3i locateOffset = (Vec3i) declaredFieldValue( - StructurePlacement.class, placement, Vec3i.class); - StructurePlacement.FrequencyReductionMethod frequencyReductionMethod = - (StructurePlacement.FrequencyReductionMethod) declaredFieldValue( - StructurePlacement.class, - placement, - StructurePlacement.FrequencyReductionMethod.class); - float frequency = (float) declaredFieldValue( - StructurePlacement.class, placement, float.class); - int salt = (int) declaredFieldValue( - StructurePlacement.class, placement, int.class); - - if (placement instanceof RandomSpreadStructurePlacement randomSpread) { - NativeStructureFrequencyScale scale = NativeStructureFrequencyScale.randomSpread( - frequency, randomSpread.spacing(), randomSpread.separation(), frequencyMultiplier); - return new RandomSpreadStructurePlacement( - locateOffset, - frequencyReductionMethod, - scale.frequency(), - salt, - exclusionZone, - scale.spacing(), - randomSpread.separation(), - randomSpread.spreadType()); - } - if (placement.getClass() == ConcentricRingsStructurePlacement.class) { - ConcentricRingsStructurePlacement rings = - (ConcentricRingsStructurePlacement) placement; - float scaledFrequency = NativeStructureFrequencyScale.probability( - frequency, frequencyMultiplier); - return new ConcentricRingsStructurePlacement( - locateOffset, - frequencyReductionMethod, - scaledFrequency, - salt, - exclusionZone, - rings.distance(), - rings.spread(), - rings.count(), - rings.preferredBiomes()); - } - throw new IllegalStateException("Unsupported native structure placement in an affected " - + "frequency-override or datapack-scope graph: " - + placement.getClass().getName()); - } - - private static Object declaredFieldValue( - Class declaringType, - Object target, - Class fieldType - ) { - Field match = null; - for (Field field : declaringType.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != fieldType) { - continue; - } - if (match != null) { - throw new IllegalStateException("Ambiguous " + fieldType.getName() + " field on " - + declaringType.getName()); - } - match = field; - } - if (match == null) { - throw new IllegalStateException("Missing " + fieldType.getName() + " field on " - + declaringType.getName()); - } - try { - match.setAccessible(true); - return match.get(target); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not read " + fieldType.getName() + " field on " - + declaringType.getName(), e); - } - } - - static final class StructureSetKeyIndex { - private final Map, ResourceKey> - keysByEntries; - - private StructureSetKeyIndex( - Map, ResourceKey> - keysByEntries - ) { - this.keysByEntries = keysByEntries; - } - - private static StructureSetKeyIndex create( - Iterable> registeredStructureSets - ) { - Map, ResourceKey> keys = - new IdentityHashMap<>(); - for (Holder holder : registeredStructureSets) { - Optional> key = holder.unwrapKey(); - if (key.isEmpty()) { - continue; - } - List entries = holder.value().structures(); - ResourceKey previous = keys.put(entries, key.get()); - if (previous != null && !previous.equals(key.get())) { - throw new IllegalStateException( - "Multiple registered structure sets share the same entry list"); - } - } - return new StructureSetKeyIndex(keys); - } - - private Optional> resolve(Holder holder) { - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - return key; - } - return Optional.ofNullable(keysByEntries.get(holder.value().structures())); - } - } - - record Selection( - List> structureSets, - int retainedManagedSets, - int excludedManagedSets - ) { - } - - private static final class ReboundStructureSetHolder extends Holder.Reference { - private ReboundStructureSetHolder(ResourceKey key) { - super(Type.STAND_ALONE, new HolderOwner<>() { - }, key, null); - } - - private void bind(StructureSet structureSet) { - bindValue(structureSet); - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/ImportedFeatureStage.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/ImportedFeatureStage.java deleted file mode 100644 index 73bbcfc8c..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/ImportedFeatureStage.java +++ /dev/null @@ -1,440 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.nativegen.NativeGenerationWriteGuard; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeFeatureGenerationPolicy; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.decoration.IrisDecorationStep; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.structure.nativegen.IrisImportedFeatureControl; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import art.arcane.iris.spi.IrisLogging; -import it.unimi.dsi.fastutil.ints.IntArraySet; -import it.unimi.dsi.fastutil.ints.IntSet; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.FeatureSorter; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.IntBinaryOperator; - -/** - * Bukkit twin of the modded imported-feature stage. Same control, same semantics, same seeds: with - * {@code importedFeatures.enabled} the vanilla placed-feature pass runs over Iris terrain, and with the - * control off nothing here allocates or runs. - * - *

Iris does its own native structure pass and calls the delegate with {@code addVanillaDecorations=false}, - * so the vanilla feature half never runs on its own. This reproduces that half only - structures are never - * placed twice. - * - *

Threading: called from {@code applyBiomeDecoration} on the worldgen thread that owns the chunk. The - * FEATURES chunk step is not parallel-safe. - */ -final class ImportedFeatureStage { - private static final String CYCLE_MARKER = "Feature order cycle found"; - - private final Engine engine; - private final ConcurrentHashMap runtimeStates = new ConcurrentHashMap<>(); - - ImportedFeatureStage(Engine engine) { - this.engine = engine; - } - - /** - * Generation settings for one biome holder, mapping Iris custom biomes onto their vanilla derivative. - * Pass-through while the control is off, which is what keeps {@code BiomeFilter} behaving exactly as it - * does today. - */ - BiomeGenerationSettings generationSettings(Holder biome) { - RuntimeFeatureState state = currentState(); - FeatureTable table = state == null ? null : state.featureTable(); - if (table == null) { - return null; - } - Holder mapped = table.derivatives().get(holderKey(biome)); - return mapped == null ? null : mapped.value().getGenerationSettings(); - } - - /** - * Builds the table on the first decorated chunk, or leaves the stage inert. A feature-order cycle is - * reported once here and degrades to features-off; it never reaches chunk generation as a crash. - */ - void prepare(WorldGenLevel level) { - IrisDimension dimension = engine.getDimension(); - int runtimeId = engine.getCacheID(); - if (settled(runtimeStates.get(runtimeId), dimension)) { - return; - } - synchronized (this) { - if (settled(runtimeStates.get(runtimeId), dimension)) { - return; - } - runtimeStates.put(runtimeId, build(level, dimension)); - } - } - - void evictRuntime(int runtimeId) { - runtimeStates.remove(runtimeId); - } - - private RuntimeFeatureState currentState() { - IrisDimension dimension = engine.getDimension(); - RuntimeFeatureState state = runtimeStates.get(engine.getCacheID()); - return settled(state, dimension) ? state : null; - } - - private static boolean settled(RuntimeFeatureState state, IrisDimension dimension) { - return state != null && state.dimension() == dimension; - } - - private RuntimeFeatureState build(WorldGenLevel level, IrisDimension dimension) { - IrisImportedFeatureControl control; - try { - control = NativeFeatureGenerationPolicy.control(engine); - } catch (RuntimeException error) { - IrisLogging.error("Iris could not read importedFeatures for this dimension; features off: " - + error); - return new RuntimeFeatureState(dimension, null); - } - if (!control.shouldGenerateFeatures()) { - return new RuntimeFeatureState(dimension, null); - } - FeatureTable built; - try { - built = buildTable(level, control, dimension); - } catch (Throwable error) { - IrisLogging.error("Iris importedFeatures is off for " + dimensionKey() - + ": feature table construction failed: " + error); - return new RuntimeFeatureState(dimension, null); - } - if (built == null) { - return new RuntimeFeatureState(dimension, null); - } - IrisLogging.info("Iris importedFeatures on for " + dimensionKey() + ": " + built.biomes().size() - + " biomes, " + built.steps().size() + " steps, " + built.derivatives().size() - + " custom-biome derivative maps"); - return new RuntimeFeatureState(dimension, built); - } - - private FeatureTable buildTable(WorldGenLevel level, IrisImportedFeatureControl control, - IrisDimension dimension) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.BIOME); - Set visibleKeys = visibleBiomeKeys(); - // Registry-ordered walk, never a hash-ordered set: FeatureSorter's cycle detection walks this list and - // an unordered walk makes detection depend on JVM hash order. - List> biomes = new ArrayList<>(); - Map> byKey = new HashMap<>(); - registry.listElements().forEach((Holder.Reference reference) -> { - String key = holderKey(reference); - if (key != null && visibleKeys.contains(key)) { - biomes.add(reference); - byKey.put(key, reference); - } - }); - if (biomes.isEmpty()) { - IrisLogging.error("Iris importedFeatures is on but " + dimensionKey() - + " exposes no registered biomes; features off"); - return null; - } - Map> derivatives = customBiomeDerivatives(registry, byKey); - List steps; - try { - steps = FeatureSorter.buildFeaturesPerStep(biomes, - (Holder biome) -> settingsFor(biome, derivatives).features(), true); - } catch (IllegalStateException error) { - String message = error.getMessage(); - if (message == null || !message.contains(CYCLE_MARKER)) { - throw error; - } - IrisLogging.error("Iris importedFeatures is off for " + dimensionKey() - + ": the registered placed features cannot be ordered. " + message - + ". Remove or reorder the conflicting content, or leave importedFeatures.enabled false."); - return null; - } - boolean filtered = control.getDisabled() != null && !control.getDisabled().isEmpty(); - return new FeatureTable(dimension, control, List.copyOf(biomes), Set.copyOf(biomes), - Map.copyOf(byKey), steps, Map.copyOf(derivatives), filtered); - } - - /** - * Every biome key Iris can write into a chunk section: the structure derivative, the raw derivative, every - * scatter entry, and every generated custom biome. - */ - private Set visibleBiomeKeys() { - Set keys = new LinkedHashSet<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - addKey(keys, irisBiome.getStructureDerivativeKey()); - addKey(keys, irisBiome.getDerivativeKey()); - addKey(keys, irisBiome.getVanillaDerivativeKey()); - for (String scatter : irisBiome.getBiomeScatter()) { - addKey(keys, scatter); - } - for (String scatter : irisBiome.getBiomeSkyScatter()) { - addKey(keys, scatter); - } - if (!irisBiome.isCustom()) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - addKey(keys, engine.getData().customBiomeResourceKey(engine.getDimension(), customBiome)); - } - } - return keys; - } - - /** - * Maps every generated Iris custom biome key onto the registry holder of its Iris biome's vanilla - * derivative. The custom biome's own datapack JSON carries no features by design. - */ - private Map> customBiomeDerivatives(Registry registry, - Map> byKey) { - Map> derivatives = new HashMap<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - if (!irisBiome.isCustom()) { - continue; - } - String derivativeKey = normalizeKey(irisBiome.getVanillaDerivativeKey()); - // Resolve from the registry, not only from the visible biome set: a sea or shore biome's structure - // derivative is rewritten away from its vanilla derivative, so the derivative whose features we - // want is not always a biome Iris can emit. - Holder derivative = byKey.get(derivativeKey); - if (derivative == null) { - derivative = resolveHolder(registry, derivativeKey); - } - if (derivative == null) { - IrisLogging.warn("Iris importedFeatures: vanilla derivative " + derivativeKey + " of biome " - + irisBiome.getLoadKey() - + " is not registered; its custom biomes generate no imported features"); - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - derivatives.put( - engine.getData().customBiomeResourceKey(engine.getDimension(), customBiome), - derivative - ); - } - } - return derivatives; - } - - private static Holder resolveHolder(Registry registry, String key) { - if (registry == null || key == null || key.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return null; - } - return registry.get(identifier).>map((Holder.Reference reference) -> reference) - .orElse(null); - } - - private static BiomeGenerationSettings settingsFor(Holder biome, - Map> derivatives) { - Holder mapped = derivatives.get(holderKey(biome)); - return mapped == null - ? biome.value().getGenerationSettings() - : mapped.value().getGenerationSettings(); - } - - /** - * Runs the vanilla placed-feature pass for one chunk on the calling worldgen thread. A no-op while the - * control is disabled or the table degraded. - */ - void run(WorldGenLevel level, ChunkAccess chunk, ChunkGenerator owner) { - RuntimeFeatureState state = currentState(); - FeatureTable table = state == null ? null : state.featureTable(); - if (table == null) { - return; - } - ChunkPos centerPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(centerPos, level.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry featureRegistry = level.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); - List steps = table.steps(); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ()); - IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); - int staticMinY = engine.getMinHeight(); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - Set> chunkBiomes = chunkBiomes(level, sectionPos, table, stackContext); - IntBinaryOperator surfaceFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, false) + staticMinY + 1; - IntBinaryOperator floorFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, true) + staticMinY + 1; - WorldGenLevel placementLevel = staticObjects.isEmpty() && stackContext == null - ? level - : NativeStructureWorldgenAccess.create( - level, centerPos, - surfaceFirstFreeY, - floorFirstFreeY, - stackContext != null, - NativeGenerationWriteGuard.protectedPositions(staticObjects, stackContext, staticMinY)); - - try { - for (int stepIndex = 0; stepIndex < steps.size(); stepIndex++) { - if (!table.control().shouldGenerateStep(IrisDecorationStep.byOrdinal(stepIndex))) { - continue; - } - placeStep(placementLevel, table, steps.get(stepIndex), featureRegistry, chunkBiomes, owner, - random, decorationSeed, origin, stepIndex); - } - } catch (Throwable error) { - throw new IllegalStateException("Iris imported feature placement failed for chunk " - + centerPos.x() + "," + centerPos.z(), error); - } finally { - level.setCurrentlyGenerating(null); - } - } - - private void placeStep(WorldGenLevel level, FeatureTable table, FeatureSorter.StepFeatureData stepData, - Registry featureRegistry, Set> chunkBiomes, - ChunkGenerator owner, WorldgenRandom random, long decorationSeed, - BlockPos origin, int stepIndex) { - IntSet stepFeatures = new IntArraySet(); - for (Holder biome : chunkBiomes) { - List> biomeFeatures = settingsFor(biome, table.derivatives()).features(); - if (stepIndex >= biomeFeatures.size()) { - continue; - } - for (Holder feature : biomeFeatures.get(stepIndex)) { - stepFeatures.add(stepData.indexMapping().applyAsInt(feature.value())); - } - } - if (stepFeatures.isEmpty()) { - return; - } - // Sorted global indices: identical ordering to vanilla, and each feature's seed comes from its own - // global index, so denying one feature never shifts another. - int[] featureIndices = stepFeatures.toIntArray(); - Arrays.sort(featureIndices); - for (int globalIndex : featureIndices) { - PlacedFeature feature = stepData.features().get(globalIndex); - if (table.filtered()) { - Identifier featureId = featureRegistry.getKey(feature); - if (featureId != null && !table.control().shouldGenerate(featureId.toString())) { - continue; - } - } - random.setFeatureSeed(decorationSeed, globalIndex, stepIndex); - level.setCurrentlyGenerating(() -> describeFeature(featureRegistry, feature)); - feature.placeWithBiomeCheck(level, owner, random, origin); - } - } - - private static String describeFeature(Registry registry, PlacedFeature feature) { - Identifier id = registry.getKey(feature); - return id == null ? feature.toString() : id.toString(); - } - - private Set> chunkBiomes( - WorldGenLevel level, - SectionPos sectionPos, - FeatureTable table, - DimensionStackContext stackContext - ) { - List> collected = new ArrayList<>(); - ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach((ChunkPos chunkPos) -> { - ChunkAccess neighbour = level.getChunk(chunkPos.x(), chunkPos.z()); - LevelChunkSection[] sections = neighbour.getSections(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - if (stackContext == null) { - section.getBiomes().getAll(collected::add); - continue; - } - int sectionMinimumY = neighbour.getSectionYFromSectionIndex(sectionIndex) << 4; - for (int quartY = 0; quartY < 4; quartY++) { - int internalY = sectionMinimumY + (quartY << 2) - level.getMinY(); - for (int quartX = 0; quartX < 4; quartX++) { - int blockX = chunkPos.getMinBlockX() + (quartX << 2); - for (int quartZ = 0; quartZ < 4; quartZ++) { - int blockZ = chunkPos.getMinBlockZ() + (quartZ << 2); - DimensionStackLayout.Layer layer = stackContext.getLayerAt( - blockX, internalY, blockZ); - if (layer.terrainContext().isSelfReferencing()) { - collected.add(section.getBiomes().get(quartX, quartY, quartZ)); - } - } - } - } - } - }); - Set> present = new LinkedHashSet<>(); - for (Holder biome : collected) { - if (table.biomeSet().contains(biome)) { - present.add(biome); - continue; - } - String key = holderKey(biome); - Holder canonical = key == null ? null : table.byKey().get(key); - if (canonical != null) { - present.add(canonical); - } - } - return present; - } - - private static void addKey(Set keys, String key) { - String normalized = normalizeKey(key); - if (normalized != null) { - keys.add(normalized); - } - } - - private String dimensionKey() { - IrisDimension dimension = engine.getDimension(); - return dimension == null ? "" : dimension.getLoadKey(); - } - - private static String holderKey(Holder holder) { - return holder.unwrapKey() - .map(key -> key.identifier().toString().toLowerCase(Locale.ROOT)) - .orElse(null); - } - - private static String normalizeKey(String key) { - Identifier identifier = key == null ? null : Identifier.tryParse(key); - return identifier == null ? null : identifier.toString().toLowerCase(Locale.ROOT); - } - - private record FeatureTable(IrisDimension dimension, IrisImportedFeatureControl control, - List> biomes, Set> biomeSet, - Map> byKey, - List steps, - Map> derivatives, boolean filtered) { - } - - private record RuntimeFeatureState(IrisDimension dimension, FeatureTable featureTable) { - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGenerator.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGenerator.java deleted file mode 100644 index c5421e98c..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGenerator.java +++ /dev/null @@ -1,1643 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.nativegen.NativeTransitionColumn; -import art.arcane.iris.nativegen.NativeTerrainHeightCache; -import art.arcane.iris.world.history.TerrainBoundarySignature; -import java.util.Optional; -import art.arcane.iris.nativegen.NativeGenerationWriteGuard; -import java.util.function.LongPredicate; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.EngineLifecycleTasks; -import art.arcane.iris.generation.runtime.GenerationSessionException; -import art.arcane.iris.generation.runtime.GenerationSessionLease; -import art.arcane.iris.structure.placement.IrisStructureLocator; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.world.history.NativeTerrainReceipt; -import art.arcane.iris.world.history.NativeBiomeSpawnSelection; -import art.arcane.iris.world.history.SavedTerrainChunk; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataType; -import art.arcane.iris.platform.generation.BukkitChunkGenerator; -import art.arcane.iris.studio.generation.JigsawStudioGenerator; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import art.arcane.iris.nativegen.NativeStructureGenerationException; -import art.arcane.iris.nativegen.NativeStructureStartInjector; -import art.arcane.iris.nativegen.NativeStructureReferenceEnvelope; -import art.arcane.iris.nativegen.NativeStructureLocateResults; -import art.arcane.iris.nativegen.NativeStructureLocatePersistence; -import art.arcane.iris.nativegen.NativeStructureOwnershipRecovery; -import art.arcane.iris.nativegen.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.NativeStructureReferenceRepair; -import art.arcane.iris.nativegen.NativeStructureSurfaceFitter; -import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator; -import art.arcane.iris.nativegen.NativeStructureVegetationClearer; -import art.arcane.iris.nativegen.NativeStructureVerticalPlacer; -import art.arcane.iris.nativegen.NativeStructureVanillaLocator; -import art.arcane.iris.nativegen.NativeStructureVolumeIndex; -import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.iris.platform.reflect.WrappedField; -import art.arcane.iris.platform.reflect.WrappedReturningMethod; -import art.arcane.iris.generation.context.IrisContext; -import art.arcane.volmlib.util.math.RNG; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.util.random.Weighted; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.LegacyRandomSource; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.craftbukkit.generator.CustomChunkGenerator; -import org.bukkit.block.data.BlockData; -import org.spigotmc.SpigotWorldConfig; - -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalInt; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CompletionException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; - -public class IrisChunkGenerator extends CustomChunkGenerator implements LongPredicate { - private static final Set AUTHORING_HEIGHTMAPS = Set.of( - Heightmap.Types.WORLD_SURFACE_WG, - Heightmap.Types.OCEAN_FLOOR_WG, - Heightmap.Types.WORLD_SURFACE, - Heightmap.Types.OCEAN_FLOOR, - Heightmap.Types.MOTION_BLOCKING, - Heightmap.Types.MOTION_BLOCKING_NO_LEAVES); - private static final WrappedField BIOME_SOURCE; - private static final WrappedReturningMethod SET_HEIGHT; - private static final Runnable NO_OP = () -> { - }; - private final ChunkGenerator delegate; - private final Engine engine; - private final CustomBiomeSource customBiomeSource; - private final ServerLevel runtimeLevel; - private final @Nullable BukkitChunkGenerator platformGenerator; - private final int runtimeMinY; - private final int runtimeHeight; - private final int runtimeSeaLevel; - private final ConcurrentHashMap> mergedSpawnTables = new ConcurrentHashMap<>(); - private final ImportedFeatureStage importedFeatures; - private final NativeTerrainHeightCache terrainHeights = new NativeTerrainHeightCache(); - private final AtomicReference retainedStudioStructureState = new AtomicReference<>(); - private volatile ReachableStructureCache reachableStructureCache; - private volatile StructureStepCache structureStepCache; - - public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { - this(delegate, engine, world, new CustomBiomeSource(seed, engine, world)); - } - - private IrisChunkGenerator(ChunkGenerator delegate, Engine engine, World world, CustomBiomeSource customBiomeSource) { - super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), world.getGenerator()); - this.delegate = delegate; - this.engine = engine; - this.customBiomeSource = customBiomeSource; - this.importedFeatures = new ImportedFeatureStage(engine); - ServerLevel level = ((CraftWorld) world).getHandle(); - this.runtimeLevel = level; - this.platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator - ? bukkitGenerator - : null; - this.runtimeMinY = level.getMinY(); - this.runtimeHeight = level.getHeight(); - this.runtimeSeaLevel = runtimeMinY + engine.getDimension().getFluidHeight(); - if (engine instanceof IrisEngine irisEngine) { - irisEngine.addGenerationRuntimeRetirementListener(this::evictRuntimeCaches); - } - installNativeStructureVolumeIndex(level); - } - - private void evictRuntimeCaches(int runtimeId) { - terrainHeights.evictRuntime(runtimeId); - importedFeatures.evictRuntime(runtimeId); - mergedSpawnTables.keySet().removeIf(key -> key.runtimeId() == runtimeId); - ReachableStructureCache reachable = reachableStructureCache; - if (reachable != null && reachable.runtimeId() == runtimeId) { - reachableStructureCache = null; - } - StructureStepCache steps = structureStepCache; - if (steps != null && steps.runtimeId() == runtimeId) { - structureStepCache = null; - } - } - - private void installNativeStructureVolumeIndex(ServerLevel level) { - WeakReference levelReference = new WeakReference<>(level); - WeakReference generatorReference = new WeakReference<>(this); - WeakReference biomeSourceReference = new WeakReference<>(customBiomeSource); - NativeStructureVolumeIndex.install(engine, new NativeStructureVolumeIndex.Context( - level.registryAccess(), - level.getServer().getStructureManager(), - level.dimension(), - LevelHeightAccessor.create(runtimeMinY, runtimeHeight), - generatorReference::get, - biomeSourceReference::get, - () -> { - ServerLevel active = levelReference.get(); - return active == null ? null : active.getChunkSource().getGeneratorState(); - })); - } - - @Override - public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return null; - } - if (level != runtimeLevel || level.getChunkSource().getGenerator() != this) { - return null; - } - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - SectionPos.blockToSectionCoord(pos.getX()), - SectionPos.blockToSectionCoord(pos.getZ()), - "bukkit_nms_structure_locate"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_structure_locate"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - HolderSet reachable = filterReachableStructures(level, holders); - NativeStructureVanillaLocator.Candidate nativeCandidate = - reachable == null || reachable.size() == 0 ? null - : NativeStructureVanillaLocator.predict( - level, reachable, pos, radius, findUnexplored); - return findNearestIrisStructure( - level, holders, pos, Math.max(0, radius), - findUnexplored, nativeCandidate); - } - } - - private Pair> findNearestIrisStructure(ServerLevel level, - HolderSet holders, - BlockPos pos, int radius, - boolean findUnexplored, - NativeStructureVanillaLocator.Candidate nativeCandidate) { - Pair> nativeLocated = - nativeCandidate == null ? null : nativeCandidate.result(); - Runnable nativeReference = () -> { - if (nativeCandidate != null) { - nativeCandidate.reference(level.structureManager()); - } - }; - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List searches = new ArrayList<>(holders.size()); - NativeStructureLocatePersistence.ProbeBudget budget = NativeStructureLocatePersistence.probeBudget(); - for (Holder holder : holders) { - Object id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure locate received an unregistered structure holder"); - } - String structureId = id.toString(); - if (!IrisStructureLocator.hasNativePlacement(engine, structureId)) { - continue; - } - NativeStructureLocatePersistence.Probe probe = NativeStructureLocatePersistence.probe( - level, holder.value(), findUnexplored, budget); - searches.add(new IrisNativeLocateSearch( - holder, structureId, NativeStructureLocatePersistence.search( - engine, structureId, pos.getX(), pos.getZ(), radius, probe))); - } - searches.sort(Comparator.comparing(IrisNativeLocateSearch::structureId)); - for (int attempt = 0; attempt < NativeStructureLocatePersistence.MAX_SELECTED_CANDIDATE_RETRIES; attempt++) { - IrisNativeLocateSearch bestSearch = null; - IrisStructureLocator.LocateResult bestResult = null; - long bestDistance = Long.MAX_VALUE; - for (IrisNativeLocateSearch search : searches) { - IrisStructureLocator.LocateResult result = search.search().predict(); - if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) { - throw new IllegalStateException("Iris structure locate reached its safety limit for " - + search.structureId() + " within " + radius + " placement rings"); - } - if (!result.found()) { - continue; - } - long dx = (long) result.originX() - pos.getX(); - long dz = (long) result.originZ() - pos.getZ(); - long distance = dx * dx + dz * dz; - if (distance < bestDistance) { - bestDistance = distance; - bestSearch = search; - bestResult = result; - } - } - if (bestSearch == null) { - return NativeStructureLocateResults.selectAndReference( - pos, null, () -> { }, nativeLocated, nativeReference); - } - Pair> predicted = Pair.of( - new BlockPos(bestResult.originX(), bestResult.baseY(), bestResult.originZ()), - bestSearch.holder()); - if (NativeStructureLocateResults.nearest(pos, predicted, nativeLocated) != predicted) { - return NativeStructureLocateResults.selectAndReference( - pos, predicted, () -> { }, nativeLocated, nativeReference); - } - NativeStructureLocatePersistence.VerifiedStart verified = - bestSearch.search().verify(bestResult); - if (verified == null) { - bestSearch.search().reject(bestResult); - continue; - } - BlockPos located = new BlockPos( - bestResult.originX(), verified.ownership().locatorY(), - bestResult.originZ()); - Pair> irisLocated = Pair.of(located, bestSearch.holder()); - IrisNativeLocateSearch selectedSearch = bestSearch; - NativeStructureLocatePersistence.VerifiedStart selectedStart = verified; - return NativeStructureLocateResults.selectAndReference( - pos, irisLocated, () -> selectedSearch.search().reference(selectedStart), - nativeLocated, nativeReference); - } - throw new IllegalStateException("Iris structure locate rejected too many selected candidates within " - + radius + " placement rings"); - } - - private HolderSet filterReachableStructures(ServerLevel level, HolderSet holders) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List candidates = new ArrayList<>(holders.size()); - for (Holder holder : holders) { - Object id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure filtering received an unregistered structure holder"); - } - String key = id.toString(); - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, - key, NativeStructureVegetationClearer.isUndergroundStep(holder.value().step())); - if (!decision.generate()) { - continue; - } - candidates.add(new NativeLocateCandidate(holder, key)); - } - if (candidates.isEmpty()) { - return HolderSet.direct(List.of()); - } - Set reachable = reachableStructureKeys(level); - List> kept = new ArrayList<>(candidates.size()); - for (NativeLocateCandidate candidate : candidates) { - if (reachable.contains(candidate.key())) { - kept.add(candidate.holder()); - } - } - if (kept.size() == holders.size()) { - return holders; - } - return HolderSet.direct(kept); - } - - private Set reachableStructureKeys(ServerLevel level) { - IrisDimension dimension = engine.getDimension(); - int runtimeId = engine.getCacheID(); - ReachableStructureCache cached = reachableStructureCache; - if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) { - return cached.keys(); - } - synchronized (this) { - cached = reachableStructureCache; - if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) { - return cached.keys(); - } - Set reachable = Set.copyOf( - VanillaStructureBiomes.reachableStructureKeys(level, customBiomeSource)); - reachableStructureCache = new ReachableStructureCache(dimension, runtimeId, reachable); - return reachable; - } - } - - @Override - protected MapCodec codec() { - return MapCodec.unit(null); - } - - @Override - public ChunkGenerator getDelegate() { - if (delegate instanceof CustomChunkGenerator chunkGenerator) - return chunkGenerator.getDelegate(); - return delegate; - } - - @Override - public int getMinY() { - return runtimeMinY; - } - - @Override - public int getSeaLevel() { - return runtimeSeaLevel; - } - - @Override - public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey levelKey) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return; - } - if (runtimeLevel.getChunkSource().getGenerator() != this - || runtimeLevel.getChunkSource().getGeneratorState() != structureState) { - return; - } - ChunkPos chunkPos = access.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_structures"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_structures"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_structures"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!allowsRoutedDiscreteGeneration(access, ChunkStatus.STRUCTURE_STARTS)) { - return; - } - Map previousStarts = new HashMap<>(access.getAllStarts()); - super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey); - Map configuredStarts = NativeStructureStartInjector.inject( - new NativeStructureStartInjector.InjectionContext( - engine, - registryAccess, - structureState, - structureManager, - access, - templateManager, - levelKey, - this, - customBiomeSource - )); - adjustGeneratedStructures( - registryAccess, access, previousStarts, configuredStarts, templateManager); - if (route != null) { - access.persistentDataContainer.set(new NamespacedKey("iris", "structure_activation"), - PersistentDataType.LONG, route.activation().activationId()); - access.markUnsaved(); - } - } - } - - private void adjustGeneratedStructures(RegistryAccess registryAccess, ChunkAccess access, - Map previousStarts, - Map configuredStarts, - StructureTemplateManager templateManager) { - Registry registry = registryAccess.lookupOrThrow(Registries.STRUCTURE); - ChunkPos chunkPos = access.getPos(); - for (Map.Entry entry : access.getAllStarts().entrySet()) { - Structure structure = entry.getKey(); - StructureStart start = entry.getValue(); - if (!start.isValid() || previousStarts.get(structure) == start) { - continue; - } - Identifier id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - BoundingBox footprint = start.getBoundingBox(); - if (!engine.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - access.setStartForStructure(structure, StructureStart.INVALID_START); - if (configuredStarts.containsKey(structure)) { - NativeStructureOwnershipStore.discard( - engine, structureId, chunkPos.x(), chunkPos.z()); - } - continue; - } - if (configuredStarts.containsKey(structure)) { - continue; - } - boolean undergroundStep = NativeStructureVegetationClearer.isUndergroundStep(structure.step()); - IrisNativeStructureDecision decision; - try { - decision = NativeStructureGenerationPolicy.resolve(engine, - structureId, undergroundStep); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "policy resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - if (!decision.generate()) { - access.setStartForStructure(structure, StructureStart.INVALID_START); - continue; - } - try { - NativeStructureVerticalPlacer.applyVerticalPlacement( - start, - structureId, - decision.yShift(), - getSeaLevel(), - access.getMinY(), - access.getMinY() + access.getHeight(), - undergroundStep, - decision.preserveSourceY(), - decision.yBand(), - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - StructureStart wrapped = NativeStructureReferenceEnvelope.wrapForPublication( - start, structure, start.getReferences(), - NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()), - structureId); - access.setStartForStructure(structure, wrapped); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vertical adjustment", structureId, chunkPos.x(), chunkPos.z(), error); - } - } - } - - @Override - public ChunkGeneratorStructureState createState(HolderLookup holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) { - return delegate.createState(holderlookup, randomstate, i, conf); - } - - void retainStudioStructureState( - ServerLevel level, - ChunkMap chunkMap, - ChunkGeneratorStructureState structureState - ) { - requireCurrentStructureOwner(level, chunkMap); - StudioStructureState retained = new StudioStructureState( - level, - chunkMap, - Objects.requireNonNull(structureState, "Studio native structure state")); - if (!retainedStudioStructureState.compareAndSet(null, retained)) { - throw new IllegalStateException("Studio native structure state is already retained."); - } - } - - StudioStructureState retainedStudioStructureState(ServerLevel level, ChunkMap chunkMap) { - StudioStructureState retained = retainedStudioStructureState.get(); - if (retained == null) { - return null; - } - requireCurrentStructureOwner(level, chunkMap); - if (retained.level() != level || retained.chunkMap() != chunkMap) { - throw new IllegalStateException("Retained Studio native structure state belongs to another world runtime."); - } - if (level.getChunkSource().getGeneratorState() != retained.structureState()) { - throw new IllegalStateException("Studio native structure state is no longer current."); - } - return retained; - } - - void claimStudioStructureState(StudioStructureState retained) { - if (!retainedStudioStructureState.compareAndSet(retained, null)) { - throw new IllegalStateException("Studio native structure state changed before activation began."); - } - } - - void abandonStudioStructureState() { - retainedStudioStructureState.set(null); - } - - CompletableFuture initializeAndPublishStructureState( - ChunkGeneratorStructureState structureState, - StructureStatePublisher publisher - ) { - return startStructureStateBootstrap( - structureState, - NO_OP, - () -> publishStructureState(publisher)); - } - - CompletableFuture activateStudioStructureState(StudioStructureState retained) { - Objects.requireNonNull(retained, "Retained Studio native structure state"); - return startStructureStateBootstrap( - retained.structureState(), - () -> { - StudioStructureState current = retainedStudioStructureState( - retained.level(), retained.chunkMap()); - if (current != retained) { - throw new IllegalStateException("Studio native structure state changed before activation."); - } - claimStudioStructureState(retained); - }, - NO_OP); - } - - private CompletableFuture startStructureStateBootstrap( - ChunkGeneratorStructureState structureState, - Runnable claim, - Runnable activation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - throw new IllegalStateException("Native structure bootstrap requires an IrisEngine runtime."); - } - AtomicReference> registeredCompletion = new AtomicReference<>(); - CompletableFuture completion = irisEngine.startNativeStructureBootstrap( - claim, - () -> { - CompletableFuture rings = initializeStructureState(structureState); - registeredCompletion.set(rings); - return rings; - }, - () -> { - CompletableFuture rings = Objects.requireNonNull( - registeredCompletion.get(), - "Registered native structure ring completion"); - if (rings.isCompletedExceptionally()) { - throw new IllegalStateException( - "Minecraft native structure ring bootstrap failed before activation."); - } - activation.run(); - }); - completion.whenComplete((ignored, failure) -> { - if (failure != null) { - Throwable cause = failure instanceof CompletionException && failure.getCause() != null - ? failure.getCause() - : failure; - IrisLogging.reportError("Native structure ring bootstrap failed for world '" - + runtimeLevel.getWorld().getName() + "'.", cause); - } - }); - return completion; - } - - private CompletableFuture initializeStructureState(ChunkGeneratorStructureState structureState) { - Map ringPositions = structureRingPositions(structureState); - structureState.ensureStructuresGenerated(); - return structureRingCompletion(ringPositions); - } - - private Map structureRingPositions(ChunkGeneratorStructureState structureState) { - try { - Field field = structureRingPositionsField(); - field.setAccessible(true); - Object value = field.get(structureState); - if (!(value instanceof Map ringPositions)) { - throw new IllegalStateException("Minecraft native structure ring state is unavailable."); - } - return ringPositions; - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not bind Minecraft native structure ring completions.", e); - } - } - - private CompletableFuture structureRingCompletion(Map ringPositions) { - List> futures = new ArrayList<>(ringPositions.size()); - for (Object candidate : ringPositions.values()) { - if (candidate instanceof CompletableFuture future) { - futures.add(future); - } else { - throw new IllegalStateException( - "Minecraft native structure ring completion is not a future."); - } - } - CompletableFuture[] completions = futures.toArray(new CompletableFuture[0]); - return CompletableFuture.allOf(completions); - } - - private Field structureRingPositionsField() { - List candidates = new ArrayList<>(1); - for (Field field : ChunkGeneratorStructureState.class.getDeclaredFields()) { - if (!Map.class.isAssignableFrom(field.getType())) { - continue; - } - Type genericType = field.getGenericType(); - if (!(genericType instanceof ParameterizedType parameterizedType)) { - continue; - } - Type[] arguments = parameterizedType.getActualTypeArguments(); - if (arguments.length == 2 - && arguments[1].getTypeName().contains(CompletableFuture.class.getName())) { - candidates.add(field); - } - } - if (candidates.size() != 1) { - throw new IllegalStateException("Expected one Minecraft native structure ring-future map, found " - + candidates.size() + "."); - } - return candidates.getFirst(); - } - - private void requireCurrentStructureOwner(ServerLevel level, ChunkMap chunkMap) { - if (runtimeLevel != level - || level.getChunkSource().chunkMap != chunkMap - || level.getChunkSource().getGenerator() != this) { - throw new IllegalStateException("Iris native structure state no longer belongs to the active world runtime."); - } - } - - private void publishStructureState(StructureStatePublisher publisher) { - try { - publisher.publish(); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not publish Minecraft native structure state.", e); - } - } - - @Override - public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return; - } - if (runtimeLevel.getChunkSource().getGenerator() != this) { - return; - } - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_references"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_references"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_references"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - NativeStructureReferenceRepair.createReferences( - engine, generatoraccessseed, structuremanager, ichunkaccess); - } - } - - @Override - public CompletableFuture createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_biomes"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_biomes"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_biomes"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - customBiomeSource.prepareVisibleBiomeBatch(); - IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State(); - ichunkaccess.fillBiomesFromNoise( - (x, y, z, sampler) -> customBiomeSource.getVisibleNoiseBiomeWithActiveGenerationLease( - x, y, z, sampler, resolverState), - randomstate.sampler()); - return CompletableFuture.completedFuture(ichunkaccess); - } - } - - @Override - public void buildSurface(WorldGenRegion regionlimitedworldaccess, StructureManager structuremanager, RandomState randomstate, ChunkAccess ichunkaccess) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_build_surface"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_build_surface"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_build_surface"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!engine.getComplex().allowsMantleChunkWrite(chunkPos.x(), chunkPos.z())) { - return; - } - delegate.buildSurface(regionlimitedworldaccess, structuremanager, randomstate, ichunkaccess); - } - } - - @Override - public void applyCarvers(WorldGenRegion regionlimitedworldaccess, long seed, RandomState randomstate, BiomeManager biomemanager, StructureManager structuremanager, ChunkAccess ichunkaccess) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_apply_carvers"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_apply_carvers"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_apply_carvers"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!engine.getComplex().allowsMantleChunkWrite(chunkPos.x(), chunkPos.z())) { - return; - } - delegate.applyCarvers(regionlimitedworldaccess, seed, randomstate, biomemanager, structuremanager, ichunkaccess); - } - } - - @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState randomstate, StructureManager structuremanager, ChunkAccess ichunkaccess) { - ChunkPos chunkPos = ichunkaccess.getPos(); - BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_chunk_pipeline"); - GenerationHistoryRuntimeRouter.RuntimeRoute route; - try { - route = openHistoryRoute(chunkPos.x(), chunkPos.z(), "bukkit_nms_chunk_pipeline"); - } catch (RuntimeException | Error failure) { - stage.close(); - throw failure; - } - GenerationSessionLease lease; - try { - lease = requireGenerationLease("bukkit_nms_chunk_pipeline"); - } catch (RuntimeException | Error failure) { - closeHistoryRoute(route, failure); - stage.close(); - throw failure; - } - try { - CompletableFuture delegatePipeline; - try (GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - delegatePipeline = delegate.fillFromNoise( - blender, randomstate, structuremanager, ichunkaccess); - } - CompletableFuture pipeline = delegatePipeline - .thenApply(filled -> { - try (GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = - openHistoryRuntimeScope(route); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - claimGeneratedSemantics(route, filled, engine.getMinHeight()); - persistNaturalTerrain(filled, route); - primeWorldgenHeightmaps(filled); - return filled; - } - }); - CompletableFuture completion = new CompletableFuture<>(); - pipeline.whenComplete((filled, failure) -> { - boolean cancelled = isCancellationFailure(failure); - Throwable completionFailure = closeNoisePipelineResources( - failure, lease, route, stage); - if (completionFailure == null) { - completion.complete(filled); - } else if (cancelled) { - completion.cancel(false); - } else { - completion.completeExceptionally(completionFailure); - } - }); - lease.detachThread(); - if (route != null) { - route.detachThread(); - } - return completion; - } catch (RuntimeException | Error failure) { - lease.close(); - closeHistoryRoute(route, failure); - stage.close(); - throw failure; - } - } - - private static void persistNaturalTerrain(ChunkAccess chunk, GenerationHistoryRuntimeRouter.RuntimeRoute route) { - if (route == null || route.naturalTerrain().isEmpty()) { - return; - } - try { - SavedTerrainChunk terrain = route.naturalTerrain().orElseThrow(); - chunk.persistentDataContainer.set(new NamespacedKey("iris", "natural_terrain"), PersistentDataType.BYTE_ARRAY, - NativeTerrainReceipt.encode(terrain, route.activation().activationId(), route.epoch().epochId())); - } catch (IOException failure) { - throw new IllegalStateException("Unable to persist natural terrain for chunk " + chunk.getPos(), failure); - } - } - - private static Throwable closeNoisePipelineResources( - Throwable failure, - GenerationSessionLease lease, - GenerationHistoryRuntimeRouter.RuntimeRoute route, - BukkitChunkGenerator.GenerationStagePermit stage - ) { - Throwable result = failure; - try { - lease.close(); - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - try { - if (route != null) { - route.close(); - } - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - try { - stage.close(); - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - return result; - } - - private static void closeHistoryRoute( - GenerationHistoryRuntimeRouter.RuntimeRoute route, - Throwable failure - ) { - if (route == null) { - return; - } - try { - route.close(); - } catch (Throwable closeFailure) { - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - } - } - - private static Throwable appendFailure(Throwable failure, Throwable closeFailure) { - if (failure == null) { - return closeFailure; - } - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - return failure; - } - - private static boolean isCancellationFailure(Throwable failure) { - Throwable current = failure; - while (current instanceof CompletionException && current.getCause() != null) { - current = current.getCause(); - } - return current instanceof CancellationException; - } - - private void primeWorldgenHeightmaps(ChunkAccess chunkAccess) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - primeAuthoringHeightmaps(chunkAccess); - return; - } - WorldgenTerrainHeightmaps.primeTerrain(chunkAccess, worldgenSurfaceHeight(), worldgenFloorHeight()); - } - - static void primeAuthoringHeightmaps(ChunkAccess chunkAccess) { - Heightmap.primeHeightmaps(chunkAccess, AUTHORING_HEIGHTMAPS); - } - - private IntBinaryOperator worldgenSurfaceHeight() { - return (x, z) -> engine.getHeight(x, z, false) + runtimeMinY + 1; - } - - private IntBinaryOperator worldgenFloorHeight() { - return (x, z) -> engine.getHeight(x, z, true) + runtimeMinY + 1; - } - - private IntBinaryOperator hostWorldgenSurfaceHeight() { - return (x, z) -> Engine.hostHeight(engine, x, z, false) + runtimeMinY + 1; - } - - private IntBinaryOperator hostWorldgenFloorHeight() { - return (x, z) -> Engine.hostHeight(engine, x, z, true) + runtimeMinY + 1; - } - - @Override - public WeightedList getMobsAt(Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { - return EngineLifecycleTasks.call(engine, "bukkit_nms_mob_spawns", - () -> getAdmittedMobsAt(holder, structuremanager, enumcreaturetype, blockposition), - WeightedList.of(List.of())); - } - - private WeightedList getAdmittedMobsAt( - Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { - NativeBiomeSpawnSelection selection = NativeBiomeSpawnSelection.at( - engine, blockposition.getX(), blockposition.getY(), blockposition.getZ(), - holder.unwrapKey().map(key -> key.identifier().toString()).orElse("")); - if (selection.mode() == NativeBiomeSpawnSelection.Mode.LOADING) { - return WeightedList.of(List.of()); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - blockposition.getX(), blockposition.getZ(), "bukkit_nms_mob_spawns")) { - return getMobsAtWithActiveRuntime(holder, structuremanager, enumcreaturetype, blockposition, selection); - } - } - - private WeightedList getMobsAtWithActiveRuntime( - Holder holder, - StructureManager structuremanager, - MobCategory enumcreaturetype, - BlockPos blockposition, - NativeBiomeSpawnSelection selection - ) { - Holder vanillaSpawnBiome = switch (selection.mode()) { - case RETAINED -> customBiomeSource.getRetainedVanillaSpawnBiome(selection.derivativeKey()); - case CURRENT -> customBiomeSource.getVanillaSpawnBiome(holder); - case NONE, LOADING -> null; - }; - if (vanillaSpawnBiome == null) { - return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition); - } - - WeightedList vanillaSpawns = vanillaSpawnBiome.value().getMobSettings().getMobs(enumcreaturetype); - WeightedList resolvedSpawns = delegate.getMobsAt( - vanillaSpawnBiome, structuremanager, enumcreaturetype, blockposition); - if (resolvedSpawns != vanillaSpawns) { - return resolvedSpawns; - } - - WeightedList explicitSpawns = holder.value().getMobSettings().getMobs(enumcreaturetype); - if (explicitSpawns.isEmpty()) { - return vanillaSpawns; - } - if (vanillaSpawns.isEmpty()) { - return explicitSpawns; - } - - int spawnRuntimeId = engine.getCacheID(); - SpawnTableKey key = new SpawnTableKey(spawnRuntimeId, holder.value(), vanillaSpawnBiome.value(), enumcreaturetype); - return mergedSpawnTables.computeIfAbsent(key, ignored -> mergeSpawnTables(vanillaSpawns, explicitSpawns)); - } - - @Override - public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true); - } - - @Override - public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition) { - delegate.addDebugScreenInfo(list, randomstate, blockposition); - } - - @Override - public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_biome_decoration"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_biome_decoration"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_biome_decoration"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - boolean flatStudioTerrain = platformGenerator != null && platformGenerator.usesFlatStudioTerrain(); - if (!flatStudioTerrain && !ichunkaccess.getPersistedStatus().isOrAfter(ChunkStatus.FEATURES)) { - placeVanillaStructures(generatoraccessseed, ichunkaccess, structuremanager); - } - if (allowsRoutedDiscreteGeneration(ichunkaccess, ChunkStatus.FEATURES) - && NativeGenerationWriteGuard.allowsDecoration(engine, generatoraccessseed, chunkPos)) { - if (!flatStudioTerrain) { - // Bind-time equivalent for Bukkit: the table is built on the first decorated chunk, which is where a - // feature-order cycle is reported once and degraded to features-off. - importedFeatures.prepare(generatoraccessseed); - } - addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); - if (!flatStudioTerrain) { - // Vanilla's placed-feature pass, on THIS thread. The delegate is still called with - // addVanillaDecorations=false below, so the vanilla half never runs twice. Inert unless the - // dimension set importedFeatures.enabled. - importedFeatures.run(generatoraccessseed, ichunkaccess, this); - } - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false); - } - claimGeneratedSemantics(route, ichunkaccess, engine.getMinHeight()); - } - } - - /** - * Iris custom biomes carry no features in their datapack JSON by design; when importedFeatures is on they - * inherit the generation settings of the vanilla biome their Iris biome derives from. This is the gate - * {@code BiomeFilter} consults, so it has to agree with the feature pass. With the control off this is - * exactly the inherited behaviour. - */ - @Override - public BiomeGenerationSettings getBiomeGenerationSettings(Holder holder) { - BiomeGenerationSettings imported = importedFeatures.generationSettings(holder); - return imported == null ? super.getBiomeGenerationSettings(holder) : imported; - } - - private void placeVanillaStructures(WorldGenLevel world, ChunkAccess chunk, StructureManager structureManager) { - if (!structureManager.shouldGenerateStructures()) { - ChunkPos disabledChunk = chunk.getPos(); - throw new IllegalStateException("Iris cannot generate native structures in chunk " - + disabledChunk.x() + "," + disabledChunk.z() - + " because structure generation is disabled outside the pack; enable native structure generation " - + "and deny families through importedStructures.disabled or complete keys through importedStructures.disabledExact"); - } - ChunkPos chunkPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(chunkPos, world.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry registry = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List> byStep = structuresByStep(registry); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decoSeed = random.setDecorationSeed(world.getSeed(), origin.getX(), origin.getZ()); - BoundingBox area = writableArea(chunk); - int steps = GenerationStep.Decoration.values().length; - List placementGroups = new ArrayList<>(); - List heightmapStarts = new ArrayList<>(); - List vegetationTargets = new ArrayList<>(); - List terrainTargets = new ArrayList<>(); - for (int step = 0; step < steps; step++) { - int index = 0; - for (Structure structure : byStep.get(step)) { - Object id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - try { - IrisNativeStructureDecision sourceDecision = NativeStructureGenerationPolicy.resolve(engine, - structureId, NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - List starts = structureManager.startsForStructure(sectionPos, structure); - List resolvedPlacements = new ArrayList<>(starts.size()); - for (StructureStart start : starts) { - BoundingBox footprint = start.getBoundingBox(); - if (!isHistoricalStructureStart(world, start) - && !engine.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - continue; - } - NativeStructureOwnershipRecord ownership = - NativeStructureOwnershipRecovery.resolve( - engine, world.getLevel(), structureId, structure, start); - IrisNativeStructureDecision decision = - ownership == null ? sourceDecision : ownership.restoredDecision(); - if (!decision.generate()) { - continue; - } - resolvedPlacements.add(new NativePlacement(start, decision)); - heightmapStarts.add(start); - terrainTargets.add(new NativeStructureTerrainIntegrator.TerrainTarget( - structureId, start, - NativeStructureTerrainIntegrator.resolveNativeTerrain( - start, decision.terrain()))); - vegetationTargets.add(start); - } - if (!resolvedPlacements.isEmpty()) { - placementGroups.add(new NativePlacementGroup( - structureId, index, step, List.copyOf(resolvedPlacements))); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - index++; - } - } - try { - WorldgenTerrainHeightmaps.primeStructurePlacement( - world, chunkPos, heightmapStarts, - hostWorldgenSurfaceHeight(), hostWorldgenFloorHeight()); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "heightmap priming", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); - Predicate protectedPosition = NativeGenerationWriteGuard.protectedPositions( - staticObjects, engine.getDimensionStackContext(), engine.getMinHeight()); - WorldGenLevel boundedWorld = NativeStructureWorldgenAccess.create( - world, chunkPos, hostWorldgenSurfaceHeight(), hostWorldgenFloorHeight(), - engine.getDimensionStackContext() != null, - protectedPosition); - try { - NativeStructureVegetationClearer.clearIntersectingVegetation( - boundedWorld, chunk, area, vegetationTargets); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vegetation cleanup", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - NativeStructureSurfaceFitter.SurfaceTerrainPlan surfaceTerrainPlan; - try { - surfaceTerrainPlan = NativeStructureSurfaceFitter.prepareSurfaceStructures( - boundedWorld, area, terrainTargets, - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - surfaceTerrainPlan.primeHeightmaps(chunk); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain integration", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - try { - NativeStructurePostProcessor.prepareTerrain( - boundedWorld, area, terrainTargets, this::resolvePaletteBlock); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain preparation", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - for (NativePlacementGroup group : placementGroups) { - random.setFeatureSeed(decoSeed, group.featureIndex(), group.step()); - try { - for (NativePlacement placement : group.placements()) { - placeVanillaStructure(boundedWorld, structureManager, random, area, chunkPos, - group.structureId(), placement.start(), placement.decision()); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "placement", group.structureId(), chunkPos.x(), chunkPos.z(), error); - } - } - try { - NativeStructureSurfaceFitter.repairVacuumFoundations( - boundedWorld, area, surfaceTerrainPlan); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "foundation repair", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - } - - private boolean isHistoricalStructureStart(WorldGenLevel world, StructureStart start) { - ChunkPos origin = start.getChunkPos(); - if (!engine.getComplex().allowsMantleChunkWrite(origin.x(), origin.z())) { - return true; - } - ChunkAccess source = world.getChunk(origin.x(), origin.z(), ChunkStatus.EMPTY, false); - if (source == null) { - return false; - } - long activation = source.persistentDataContainer.getOrDefault( - new NamespacedKey("iris", "structure_activation"), PersistentDataType.LONG, 0L); - return NativeGenerationWriteGuard.isHistoricalStructure(engine, activation); - } - - private static String nativeStructureBatchContext(List placementGroups) { - if (placementGroups.isEmpty()) { - return ""; - } - StringBuilder context = new StringBuilder("["); - for (int i = 0; i < placementGroups.size(); i++) { - if (i > 0) { - context.append(", "); - } - context.append(placementGroups.get(i).structureId()); - } - return context.append(']').toString(); - } - - private void placeVanillaStructure(WorldGenLevel world, StructureManager structureManager, WorldgenRandom random, - BoundingBox area, ChunkPos chunkPos, String structureId, StructureStart start, - IrisNativeStructureDecision decision) { - WorldGenLevel boundedWorld = world instanceof NativeStructureWorldgenAccess ? world : NativeStructureWorldgenAccess.create( - world, - chunkPos, - hostWorldgenSurfaceHeight(), - hostWorldgenFloorHeight(), - engine.getDimensionStackContext() != null, - NativeGenerationWriteGuard.protectedPositions( - engine.getDimension().getStaticObjectLayer(engine.getData()), - engine.getDimensionStackContext(), engine.getMinHeight())); - world.setCurrentlyGenerating(() -> "Iris native structure " + structureId); - try { - NativeStructurePostProcessor.place(boundedWorld, structureManager, this, random, area, chunkPos, - structureId, start, decision, this::resolvePaletteBlock, - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - } finally { - world.setCurrentlyGenerating(null); - } - } - - private List> structuresByStep(Registry registry) { - int runtimeId = engine.getCacheID(); - StructureStepCache cached = structureStepCache; - if (cached != null && cached.runtimeId() == runtimeId && cached.registry() == registry) { - return cached.structures(); - } - synchronized (this) { - cached = structureStepCache; - if (cached != null && cached.runtimeId() == runtimeId && cached.registry() == registry) { - return cached.structures(); - } - int steps = GenerationStep.Decoration.values().length; - List> grouped = new ArrayList<>(steps); - for (int step = 0; step < steps; step++) { - grouped.add(new ArrayList<>()); - } - for (Structure structure : registry) { - grouped.get(structure.step().ordinal()).add(structure); - } - for (int step = 0; step < steps; step++) { - grouped.set(step, List.copyOf(grouped.get(step))); - } - List> resolved = List.copyOf(grouped); - structureStepCache = new StructureStepCache(runtimeId, registry, resolved); - return resolved; - } - } - - private BlockState resolvePaletteBlock(IrisMaterialPalette palette, RNG rng, int x, int y, int z) { - PlatformBlockState platformState = palette.get(rng, x, y, z, engine.getData()); - if (platformState == null || !(platformState.nativeHandle() instanceof BlockData blockData)) { - throw new IllegalStateException("Configured native structure palette did not resolve a Bukkit block at " - + x + "," + y + "," + z); - } - if (blockData instanceof IrisCustomData customData) { - blockData = customData.getBase(); - } - if (blockData instanceof CraftBlockData craftBlockData) { - return craftBlockData.getState(); - } - throw new IllegalStateException("Configured native structure palette resolved unsupported Bukkit block data " - + blockData.getClass().getName() + " at " + x + "," + y + "," + z); - } - - private BoundingBox writableArea(ChunkAccess chunk) { - ChunkPos cp = chunk.getPos(); - int i = cp.getMinBlockX(); - int j = cp.getMinBlockZ(); - int minY = chunk.getMinY() + 1; - int maxY = chunk.getMinY() + chunk.getHeight() - 1; - return new BoundingBox(i, minY, j, i + 15, maxY, j + 15); - } - - @Override - public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) { - ChunkPos chunkPos = chunkAccess.getPos(); - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - chunkPos.getMinBlockX(), chunkPos.getMinBlockZ(), "bukkit_nms_heightmaps"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_heightmaps"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY()); - BlockPos blockPos = sectionPos.origin(); - - primeWorldgenHeightmaps(chunkAccess); - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return; - } - - Heightmap motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING); - Heightmap motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES); - int minHeight = engine.getMinHeight(); - - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - int wX = x + blockPos.getX(); - int wZ = z + blockPos.getZ(); - - int terrainTop = engine.getHeight(wX, wZ, false) + minHeight + 1; - SET_HEIGHT.invoke(motion, x, z, terrainTop); - SET_HEIGHT.invoke(motionNoLeaves, x, z, terrainTop); - } - } - - Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS); - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris heightmap generation could not acquire its engine runtime.", e); - } - } - - @Override - public void spawnOriginalMobs(WorldGenRegion region) { - ChunkPos center = region.getCenter(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_spawn_original_mobs"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - center.x(), center.z(), "bukkit_nms_spawn_original_mobs"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_spawn_original_mobs"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!allowsRoutedDiscreteGeneration(region.getChunk(center.x(), center.z()), ChunkStatus.SPAWN)) { - return; - } - Holder visibleBiome; - if (engine.getDimensionStackContext() == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } else { - visibleBiome = customBiomeSource.getVisibleSurfaceBiome( - center.getMinBlockX() + 8, - center.getMinBlockZ() + 8); - if (visibleBiome == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } - } - Holder vanillaBiome = customBiomeSource.getVanillaSpawnBiome(visibleBiome); - WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(RandomSupport.generateUniqueSeed())); - random.setDecorationSeed(region.getSeed(), center.getMinBlockX(), center.getMinBlockZ()); - NaturalSpawner.spawnMobsForChunkGeneration( - region, vanillaBiome == null ? visibleBiome : vanillaBiome, center, random); - } - } - - @Override - public boolean test(long packedChunk) { - return !engine.isClosing() && !engine.isClosed() - && engine.getComplex().allowsMantleChunkWrite(ChunkPos.getX(packedChunk), ChunkPos.getZ(packedChunk)); - } - - private boolean allowsRoutedDiscreteGeneration(ChunkAccess chunk, ChunkStatus stage) { - if (chunk.getPersistedStatus().isOrAfter(stage)) { - return false; - } - ChunkPos chunkPos = chunk.getPos(); - return NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, stage) - || engine.getComplex().allowsNewGenerationChunk(chunkPos.x(), chunkPos.z()); - } - - private static WeightedList mergeSpawnTables( - WeightedList vanillaSpawns, - WeightedList explicitSpawns) { - List> entries = new ArrayList<>( - vanillaSpawns.unwrap().size() + explicitSpawns.unwrap().size()); - Set> explicitTypes = new HashSet<>(); - for (Weighted entry : explicitSpawns.unwrap()) { - explicitTypes.add(entry.value().type()); - } - for (Weighted entry : vanillaSpawns.unwrap()) { - if (!explicitTypes.contains(entry.value().type())) { - entries.add(entry); - } - } - entries.addAll(explicitSpawns.unwrap()); - return WeightedList.of(entries); - } - - @Override - public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { - return delegate.getSpawnHeight(levelheightaccessor); - } - - @Override - public int getGenDepth() { - return runtimeHeight; - } - - @Override - public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return platformGenerator.getAuthoringBaseHeight(); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - i, j, "bukkit_nms_base_height"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_base_height"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - NativeTerrainHeightCache.Query query = new NativeTerrainHeightCache.Query( - engine.getCacheID(), i, j, heightmap_type, - levelheightaccessor.getMinY(), levelheightaccessor.getHeight()); - OptionalInt resolved = terrainHeights.resolvedHeight(query, - () -> resolvedBaseHeight(i, j, heightmap_type, levelheightaccessor)); - if (resolved.isPresent()) { - return resolved.getAsInt(); - } - boolean ignoreFluid = !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState()); - int height = engine.getDimensionStackContext() == null - ? engine.getHeight(i, j, ignoreFluid) - : Engine.hostHeight(engine, i, j, ignoreFluid); - return levelheightaccessor.getMinY() + height + 1; - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base height query could not acquire its engine runtime.", e); - } - } - - private OptionalInt resolvedBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor) { - Optional resolved = engine.getComplex().resolvedTerrainColumn(x, z); - return resolved.isPresent() - ? OptionalInt.of(NativeTransitionColumn.height(resolved.get(), type, heightAccessor)) - : OptionalInt.empty(); - } - - @Override - public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; - int floorIndex = platformGenerator.getAuthoringFloorY() - levelheightaccessor.getMinY(); - BlockState floor = platformGenerator.isJigsawStudioActive() && JigsawStudioGenerator.isLightFloor(i, j) - ? Blocks.SMOOTH_STONE.defaultBlockState() - : Blocks.POLISHED_DEEPSLATE.defaultBlockState(); - for (int index = 0; index < column.length; index++) { - column[index] = index == floorIndex - ? floor - : Blocks.AIR.defaultBlockState(); - } - return new NoiseColumn(levelheightaccessor.getMinY(), column); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - i, j, "bukkit_nms_base_column"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_base_column"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - Optional resolved = engine.getComplex().resolvedTerrainColumn(i, j); - if (resolved.isPresent()) { - return NativeTransitionColumn.column(resolved.get(), levelheightaccessor); - } - boolean dimensionStack = engine.getDimensionStackContext() != null; - int block = dimensionStack - ? Engine.hostHeight(engine, i, j, true) - : engine.getHeight(i, j, true); - int water = dimensionStack - ? Engine.hostHeight(engine, i, j, false) - : engine.getHeight(i, j, false); - BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; - for (int k = 0; k < column.length; k++) { - if (k <= block) { - column[k] = Blocks.STONE.defaultBlockState(); - } else if (k <= water) { - column[k] = Blocks.WATER.defaultBlockState(); - } else { - column[k] = Blocks.AIR.defaultBlockState(); - } - } - return new NoiseColumn(levelheightaccessor.getMinY(), column); - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base column query could not acquire its engine runtime.", e); - } - } - - private GenerationSessionLease requireGenerationLease(String operation) { - try { - return engine.acquireGenerationLease(operation); - } catch (GenerationSessionException exception) { - throw new IllegalStateException("Iris " + operation + " could not acquire its engine runtime.", exception); - } - } - - private GenerationHistoryRuntimeRouter.RuntimeRoute openHistoryRoute( - int chunkX, - int chunkZ, - String operation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); - if (router == null) { - if (platformGenerator != null && platformGenerator.getGenerationHistory() != null) { - throw new IllegalStateException("Iris " + operation - + " has no generation-history runtime router."); - } - return null; - } - try { - return router.openRoute(chunkX, chunkZ); - } catch (IOException failure) { - throw new IllegalStateException("Iris " + operation - + " could not route generation history for chunk " - + chunkX + "," + chunkZ + ".", failure); - } - } - - private GenerationHistoryRuntimeRouter.CoordinateScope openHistoryCoordinateScope( - int blockX, - int blockZ, - String operation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(blockX, blockZ); - } catch (IOException failure) { - throw new IllegalStateException("Iris " + operation - + " could not route generation history at " - + blockX + "," + blockZ + ".", failure); - } - } - - private static GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope openHistoryRuntimeScope( - GenerationHistoryRuntimeRouter.RuntimeRoute route - ) { - return route == null ? null : route.openRuntimeScope(); - } - - private static void claimGeneratedSemantics( - GenerationHistoryRuntimeRouter.RuntimeRoute route, - ChunkAccess chunk, - int minimumY - ) { - if (route == null) { - return; - } - try { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - route.claimGeneratedSemantics((x, y, z) -> { - BlockState state = chunk.getBlockState(position.set(x, minimumY + y, z)); - return state.isAir() || state.liquid(); - }); - } catch (IOException failure) { - throw new IllegalStateException("Iris could not durably claim generated chunk semantics.", failure); - } - } - - private BukkitChunkGenerator.GenerationStagePermit requireGenerationStage(String operation) { - return platformGenerator == null - ? BukkitChunkGenerator.GenerationStagePermit.noop() - : platformGenerator.acquireGenerationStage(operation); - } - - @Override - public Optional getTypeNameForDataFixer() { - return delegate.getTypeNameForDataFixer(); - } - - @Override - public void validate() { - delegate.validate(); - } - - static { - List biomeSources = new ArrayList<>(1); - for (Field field : ChunkGenerator.class.getDeclaredFields()) { - if (!field.getType().equals(BiomeSource.class)) - continue; - biomeSources.add(field); - } - if (biomeSources.size() != 1) - throw new IllegalStateException("Expected exactly one BiomeSource field in ChunkGenerator, found " - + biomeSources.size() + " " + biomeSources.stream().map(Field::getName).toList()); - Field biomeSource = biomeSources.getFirst(); - - List setHeights = new ArrayList<>(1); - for (Method method : Heightmap.class.getDeclaredMethods()) { - Class[] types = method.getParameterTypes(); - if (!method.getName().equals("setHeight") - || !Arrays.equals(types, new Class[]{int.class, int.class, int.class}) - || !method.getReturnType().equals(void.class)) - continue; - setHeights.add(method); - } - if (setHeights.size() != 1) - throw new IllegalStateException("Expected exactly one Heightmap.setHeight(int,int,int) method, found " - + setHeights.size()); - Method setHeight = setHeights.getFirst(); - - BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName()); - SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes()); - } - - private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) { - try { - BIOME_SOURCE.set(generator, source); - if (generator instanceof CustomChunkGenerator custom) - BIOME_SOURCE.set(custom.getDelegate(), source); - - return generator; - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private record SpawnTableKey(int runtimeId, Biome biome, Biome vanillaBiome, MobCategory category) { - } - - private record ReachableStructureCache(IrisDimension dimension, int runtimeId, Set keys) { - } - - private record StructureStepCache(int runtimeId, Registry registry, - List> structures) { - } - - record StudioStructureState( - ServerLevel level, - ChunkMap chunkMap, - ChunkGeneratorStructureState structureState - ) { - } - - @FunctionalInterface - interface StructureStatePublisher { - void publish() throws IllegalAccessException; - } - - private record NativePlacement(StructureStart start, IrisNativeStructureDecision decision) { - } - - private record NativePlacementGroup(String structureId, int featureIndex, int step, - List placements) { - } - - private record NativeLocateCandidate(Holder holder, String key) { - } - - private record IrisNativeLocateSearch(Holder holder, String structureId, - NativeStructureLocatePersistence.Search search) { - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.java deleted file mode 100644 index eb2e6fcfd..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.java +++ /dev/null @@ -1,1644 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.world.history.SavedTerrainChunk; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; -import art.arcane.iris.platform.bukkit.nms.DatapackStructureScopeResult; -import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.platform.bukkit.nms.INMSBinding; -import art.arcane.iris.platform.bukkit.nms.MinecraftVersion; -import art.arcane.iris.platform.bukkit.nms.ServerShutdownBoundary; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.Pair; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.generation.cache.AtomicCache; -import art.arcane.iris.generation.chunk.TerrainChunk; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; -import art.arcane.iris.nativegen.NativeStructureVolumeIndex; -import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; -import art.arcane.iris.platform.generation.BukkitChunkGenerator; -import art.arcane.iris.platform.generation.PlatformChunkGenerator; -import art.arcane.iris.nativegen.NativeStructureFactory; -import art.arcane.iris.nativegen.NativeStructureGenerationException; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.hunk.Hunk; -import art.arcane.iris.generation.chunk.ChunkDataHunkHolder; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformGenerationRegistry; -import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.volmlib.util.json.JSONObject; -import art.arcane.volmlib.util.mantle.runtime.Mantle; -import art.arcane.volmlib.util.matter.Matter; -import art.arcane.volmlib.util.math.Vector3d; -import art.arcane.volmlib.util.scheduling.FoliaScheduler; -import art.arcane.volmlib.util.matter.MatterBiomeInject; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.world.storage.region.NBTWorld; -import art.arcane.volmlib.util.nbt.mca.palette.MCABiomeContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAChunkBiomeContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAGlobalPalette; -import art.arcane.volmlib.util.nbt.mca.palette.MCAIdMap; -import art.arcane.volmlib.util.nbt.mca.palette.MCAIdMapper; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPalette; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPaletteAccess; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPalettedContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAWrappedPalettedContainer; -import art.arcane.volmlib.util.nbt.tag.CompoundTag; -import art.arcane.iris.world.task.J; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.IdMapper; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.Registry; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.ByteTag; -import net.minecraft.nbt.CollectionTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.EndTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.ShortTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.commands.data.BlockDataAccessor; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.tags.TagKey; -import net.minecraft.world.attribute.EnvironmentAttributes; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.item.component.CustomData; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Biomes; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -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.level.block.state.properties.Property; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.structure.StructureCheck; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.TreeFeature; -import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Difficulty; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.CraftChunk; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.CraftBlockState; -import org.bukkit.craftbukkit.block.CraftBlockStates; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.craftbukkit.generator.CraftChunkData; -import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; -import org.bukkit.entity.Entity; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.awt.Color; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicInteger; - -public class NMSBinding implements INMSBinding { - private final KMap baseBiomeCache = new KMap<>(); - private volatile DataVersion dataVersion; - private final BlockData AIR = Material.AIR.createBlockData(); - private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final NmsWorldLifecycle worldLifecycle = new NmsWorldLifecycle(); - private final AtomicCache> registryCache = new AtomicCache<>(); - private final AtomicCache> globalCache = new AtomicCache<>(); - private final AtomicCache registryAccess = new AtomicCache<>(); - private final AtomicCache byIdRef = new AtomicCache<>(); - - @Override - public PlatformGenerationRegistry generationRegistry() { - return new NmsGenerationRegistry( - this::registry, - "bukkit-generation-registry-v1", - "bukkit-v26_2_R1-generated-registry-json-v1" - ); - } - - private static Object getFor(Class type, Object source) { - Object o = fieldFor(type, source); - - if (o != null) { - return o; - } - - o = invokeFor(type, source); - - if (o != null) { - return o; - } - - throw new IllegalStateException("Iris cannot resolve a " + type.getName() - + " from " + source.getClass().getName() + " on this server version"); - } - - private static Object invokeFor(Class returns, Object in) { - for (Method i : in.getClass().getMethods()) { - if (i.getReturnType().equals(returns)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); - return i.invoke(in); - } catch (ReflectiveOperationException | RuntimeException e) { - throw new IllegalStateException("Iris failed to invoke " + in.getClass().getName() + "." - + i.getName() + "() for " + returns.getName(), e); - } - } - } - - return null; - } - - private static Object fieldFor(Class returns, Object in) { - for (Class sourceType = in.getClass(); sourceType != null; sourceType = sourceType.getSuperclass()) { - Object o = fieldForClass(returns, sourceType, in); - - if (o != null) { - return o; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - private static T fieldForClass(Class returnType, Class sourceType, Object in) { - for (Field i : sourceType.getDeclaredFields()) { - if (i.getType().equals(returnType)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); - return (T) i.get(in); - } catch (IllegalAccessException | RuntimeException e) { - throw new IllegalStateException("Iris failed to read " + sourceType.getName() + "." - + i.getName() + " for " + returnType.getName(), e); - } - } - } - return null; - } - - private static Class getClassType(Class type, int ordinal) { - return type.getDeclaredClasses()[ordinal]; - } - - @Override - public boolean hasTile(Material material) { - return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material)); - } - - @Override - public String getEntitySpawnCategory(String key) { - Identifier identifier = Identifier.parse(key); - if (!BuiltInRegistries.ENTITY_TYPE.containsKey(identifier)) { - throw new IllegalArgumentException("Unknown native entity type: " + key); - } - EntityType type = BuiltInRegistries.ENTITY_TYPE.getValue(identifier); - return type.getCategory().getSerializedName().toLowerCase(Locale.ROOT); - } - - @Override - public boolean hasTile(Location l) { - return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null; - } - - @Override - @SuppressWarnings("unchecked") - public KMap serializeTile(Location location) { - BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); - - if (e == null) { - return null; - } - - net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry()); - return (KMap) convertFromTag(tag, 0, 64); - } - - @Contract(value = "null, _, _ -> null", pure = true) - private Object convertFromTag(Tag tag, int depth, int maxDepth) { - if (tag == null || depth > maxDepth) return null; - return switch (tag) { - case CollectionTag collection -> { - KList list = new KList<>(); - - for (Object i : collection) { - if (i instanceof Tag t) - list.add(convertFromTag(t, depth + 1, maxDepth)); - else list.add(i); - } - yield list; - } - case net.minecraft.nbt.CompoundTag compound -> { - KMap map = new KMap<>(); - - for (String key : compound.keySet()) { - var child = compound.get(key); - if (child == null) continue; - var value = convertFromTag(child, depth + 1, maxDepth); - if (value == null) continue; - map.put(key, value); - } - yield map; - } - case NumericTag numeric -> numeric.box(); - default -> tag.asString().orElse(null); - }; - } - - @Override - public void deserializeTile(KMap map, Location pos) { - if (map == null || pos == null || pos.getWorld() == null) { - return; - } - - Tag converted = convertToTag(map, 0, 64); - if (!(converted instanceof net.minecraft.nbt.CompoundTag tag)) { - return; - } - - ServerLevel level = ((CraftWorld) pos.getWorld()).getHandle(); - BlockPos blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); - int chunkX = pos.getBlockX() >> 4; - int chunkZ = pos.getBlockZ() >> 4; - if (J.isOwnedByCurrentRegion(pos.getWorld(), chunkX, chunkZ)) { - merge(level, blockPos, tag); - return; - } - if (!J.runAt(pos, () -> merge(level, blockPos, tag))) { - IrisLogging.warn("[NMS] Failed to schedule tile deserialize at " + blockPos + " in world " + pos.getWorld().getName()); - } - } - - private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) { - if (level == null || blockPos == null || tag == null) { - return; - } - - try { - var blockEntity = level.getBlockEntity(blockPos); - if (blockEntity == null) { - IrisLogging.warn("[NMS] BlockEntity not found at " + blockPos); - var state = level.getBlockState(blockPos); - if (!state.hasBlockEntity()) { - return; - } - - blockEntity = ((EntityBlock) state.getBlock()) - .newBlockEntity(blockPos, state); - } - - var accessor = new BlockDataAccessor(blockEntity, blockPos); - accessor.setData(accessor.getData().merge(tag)); - } catch (Throwable e) { - IrisLogging.warn("[NMS] Failed to merge tile data at " + blockPos + ": " + e.getMessage()); - IrisLogging.reportError(e); - } - } - - private Tag convertToTag(Object object, int depth, int maxDepth) { - if (object == null || depth > maxDepth) return EndTag.INSTANCE; - return switch (object) { - case Map map -> { - var tag = new net.minecraft.nbt.CompoundTag(); - for (var i : map.entrySet()) { - tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth)); - } - yield tag; - } - case List list -> { - var tag = new ListTag(); - for (var i : list) { - tag.add(convertToTag(i, depth + 1, maxDepth)); - } - yield tag; - } - case Byte number -> ByteTag.valueOf(number); - case Short number -> ShortTag.valueOf(number); - case Integer number -> IntTag.valueOf(number); - case Long number -> LongTag.valueOf(number); - case Float number -> FloatTag.valueOf(number); - case Double number -> DoubleTag.valueOf(number); - case String string -> StringTag.valueOf(string); - default -> EndTag.INSTANCE; - }; - } - - @Override - public CompoundTag serializeEntity(Entity location) { - return null;// TODO: - } - - @Override - public Entity deserializeEntity(CompoundTag s, Location newPosition) { - return null;// TODO: - } - - @Override - public boolean supportsCustomHeight() { - return true; - } - - private RegistryAccess registry() { - RegistryAccess access = registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); - - if (access == null) { - throw new IllegalStateException("Iris cannot resolve the Minecraft registry access on this server version"); - } - - return access; - } - - private Registry getCustomBiomeRegistry() { - return registry().lookup(Registries.BIOME).orElseThrow(() -> new IllegalStateException( - "Iris cannot resolve the Minecraft biome registry on this server version")); - } - - private Registry getBlockRegistry() { - return registry().lookup(Registries.BLOCK).orElse(null); - } - - @Override - public Object getBiomeBaseFromId(int id) { - Object raw = getCustomBiomeRegistry().get(id); - if (raw instanceof java.util.Optional opt) { - raw = opt.orElse(null); - } - if (raw instanceof Holder holder) { - raw = holder.value(); - } - return raw; - } - - @Override - public int getMinHeight(World world) { - return world.getMinHeight(); - } - - @Override - public boolean supportsCustomBiomes() { - return true; - } - - @Override - public boolean supportsIrisWorldGeneration() { - return true; - } - - @Override - public int getTrueBiomeBaseId(Object biomeBase) { - return getCustomBiomeRegistry().getId(((Holder) biomeBase).value()); - } - - @Override - public Object getTrueBiomeBase(Location location) { - return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); - } - - @Override - public String getTrueBiomeBaseKey(Location location) { - return getKeyForBiomeBase(getTrueBiomeBase(location)); - } - - @Override - public Object getCustomBiomeBaseFor(String mckey) { - return getCustomBiomeRegistry().getValue(net.minecraft.resources.Identifier.parse(mckey)); - } - - @Override - public Object getCustomBiomeBaseHolderFor(String mckey) { - return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(mckey)))).orElse(null); - } - - public int getBiomeBaseIdForKey(String key) { - return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(key)).map(Holder::value).orElse(null)); - } - - @Override - public String getKeyForBiomeBase(Object biomeBase) { - net.minecraft.world.level.biome.Biome biome; - if (biomeBase instanceof Holder holder) { - Object value = holder.value(); - if (!(value instanceof net.minecraft.world.level.biome.Biome held)) { - throw new IllegalArgumentException("Iris cannot read a biome key from holder value " - + (value == null ? "null" : value.getClass().getName())); - } - biome = held; - } else if (biomeBase instanceof net.minecraft.world.level.biome.Biome direct) { - biome = direct; - } else { - throw new IllegalArgumentException("Iris cannot read a biome key from " - + (biomeBase == null ? "null" : biomeBase.getClass().getName())); - } - - Identifier key = getCustomBiomeRegistry().getKey(biome); - if (key == null) { - throw new IllegalStateException("Iris found no registry key for biome " + biome); - } - return key.getPath(); // something, not something:something - } - - @Override - public Object getBiomeBase(World world, Biome biome) { - return biomeToBiomeBase(((CraftWorld) world).getHandle() - .registryAccess().lookup(Registries.BIOME).orElse(null), biome); - } - - @Override - public Object getBiomeBase(Object registry, Biome biome) { - Object v = baseBiomeCache.get(biome); - - if (v != null) { - return v; - } - //noinspection unchecked - v = biomeToBiomeBase((Registry) registry, biome); - if (v == null) { - // Ok so there is this new biome name called "CUSTOM" in Paper's new releases. - // But, this does NOT exist within CraftBukkit which makes it return an error. - // So, we will just return the ID that the plains biome returns instead. - //noinspection unchecked - return biomeToBiomeBase((Registry) registry, Biome.PLAINS); - } - baseBiomeCache.put(biome, v); - return v; - } - - @Override - public KList getBiomes() { - KList biomes = new KList<>(); - for (Biome biome : org.bukkit.Registry.BIOME) { - biomes.add(biome); - } - return biomes; - } - - @Override - public KList getStructureKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.STRUCTURE).keySet().forEach(k -> keys.add(k.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to read registered structure keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getJigsawStructureKeys() { - KList keys = new KList<>(); - try { - Registry structures = registry().lookupOrThrow(Registries.STRUCTURE); - for (Map.Entry, Structure> entry : structures.entrySet()) { - if (entry.getValue() instanceof JigsawStructure) { - keys.add(entry.getKey().identifier().toString()); - } - } - } catch (RuntimeException e) { - throw new IllegalStateException( - "Iris failed to read registered jigsaw structure keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getTemplatePoolKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.TEMPLATE_POOL).keySet() - .forEach(key -> keys.add(key.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException( - "Iris failed to read registered template pool keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public JigsawSourceMetadata getJigsawSourceMetadata(String structureKey) { - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Registry structures = registry().lookupOrThrow(Registries.STRUCTURE); - Structure structure = structures.getValue(identifier); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.sourceMetadata( - registry(), server.getStructureManager(), jigsaw); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve live jigsaw metadata for registered structure '" - + structureKey + "'", error); - } - } - - @Override - public int getTemplatePoolHorizontalSpan(String templatePoolKey) { - try { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.templatePoolHorizontalSpan( - registry(), server.getStructureManager(), templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the live horizontal span for registered " - + "template pool '" + templatePoolKey + "'", error); - } - } - - @Override - public int getJigsawStartPoolHorizontalSpan(String structureKey, String templatePoolKey) { - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Structure structure = registry().lookupOrThrow(Registries.STRUCTURE).getValue(identifier); - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.jigsawStartPoolHorizontalSpan( - registry(), server.getStructureManager(), jigsaw, templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the effective start-pool span for registered " - + "jigsaw structure '" + structureKey + "' and pool '" + templatePoolKey + "'", error); - } - } - - @Override - public KList getStructureSetKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.STRUCTURE_SET).keySet().forEach(k -> keys.add(k.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to read registered structure-set keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getReachableStructureKeys(World world) { - KList keys = new KList<>(); - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource(); - keys.addAll(VanillaStructureBiomes.reachableStructureKeys(level, source)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve reachable structures for Bukkit world '" - + (world == null ? "" : world.getName()) + "'", e); - } - return keys; - } - - @Override - public KList getStructureBiomeKeys(String structureKey) { - KList keys = new KList<>(); - try { - RegistryAccess access = registry(); - if (access == null) { - throw new IllegalStateException("Minecraft registry access is unavailable"); - } - keys.addAll(VanillaStructureBiomes.structureBiomeKeys(access, structureKey)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve biome keys for registered structure '" - + structureKey + "'", e); - } - return keys; - } - - @Override - public KList getPossibleBiomeKeys(World world) { - KList keys = new KList<>(); - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource(); - keys.addAll(VanillaStructureBiomes.possibleBiomeKeys(source)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve possible structure biome keys for Bukkit world '" - + (world == null ? "" : world.getName()) + "'", e); - } - return keys; - } - - @Override - public KList getObjectFeatureKeys() { - KList keys = new KList<>(); - try { - Registry> reg = registry().lookupOrThrow(Registries.CONFIGURED_FEATURE); - for (Identifier id : reg.keySet()) { - ConfiguredFeature cf = reg.getValue(id); - if (cf == null) { - continue; - } - String group = classifyFeature(cf.feature()); - if (group == null) { - continue; - } - keys.add(group + "|" + id); - } - } catch (Throwable e) { - IrisLogging.reportError(e); - } - return keys; - } - - private static String classifyFeature(Feature feature) { - if (feature instanceof TreeFeature) { - return "trees"; - } - if (feature instanceof FallenTreeFeature) { - return "fallen_trees"; - } - if (feature instanceof AbstractHugeMushroomFeature) { - return "mushrooms"; - } - return null; - } - - @Override - public boolean placeFeature(World world, int x, int y, int z, String featureKey, long seed) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry> reg = registry().lookupOrThrow(Registries.CONFIGURED_FEATURE); - ConfiguredFeature cf = reg.getValue(Identifier.parse(featureKey)); - if (cf == null) { - return false; - } - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - return cf.place(level, generator, random, new BlockPos(x, y, z)); - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public int[] placeStructure(World world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry reg = registry().lookupOrThrow(Registries.STRUCTURE); - net.minecraft.world.level.levelgen.structure.Structure structure = reg.getValue(Identifier.parse(structureKey)); - if (structure == null) { - return null; - } - net.minecraft.core.Holder holder = reg.wrapAsHolder(structure); - net.minecraft.world.level.levelgen.RandomState randomState = level.getChunkSource().randomState(); - net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager templateManager = level.getStructureManager(); - net.minecraft.world.level.StructureManager structureManager = level.structureManager(); - net.minecraft.world.level.biome.BiomeSource biomeSource = generator.getBiomeSource(); - net.minecraft.world.level.ChunkPos chunkPos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ); - - net.minecraft.world.level.levelgen.structure.StructureStart start = structure.generate( - holder, - level.dimension(), - level.registryAccess(), - generator, - biomeSource, - randomState, - templateManager, - seed, - chunkPos, - 0, - level, - biome -> true); - - if (start == null || !start.isValid()) { - return null; - } - - BoundingBox box = start.getBoundingBox(); - int spanX = box.maxX() - box.minX() + 1; - int spanY = box.maxY() - box.minY() + 1; - int spanZ = box.maxZ() - box.minZ() + 1; - if (maxSpan > 0 && (spanX > maxSpan || spanZ > maxSpan || spanY > maxSpan)) { - return null; - } - - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - int minCX = box.minX() >> 4; - int maxCX = box.maxX() >> 4; - int minCZ = box.minZ() >> 4; - int maxCZ = box.maxZ() >> 4; - for (int cx = minCX; cx <= maxCX; cx++) { - for (int cz = minCZ; cz <= maxCZ; cz++) { - level.getChunk(cx, cz); - net.minecraft.world.level.ChunkPos cp = new net.minecraft.world.level.ChunkPos(cx, cz); - BoundingBox chunkBox = new BoundingBox( - cp.getMinBlockX(), box.minY(), cp.getMinBlockZ(), - cp.getMaxBlockX(), box.maxY(), cp.getMaxBlockZ()); - start.placeInChunk(level, structureManager, generator, random, chunkBox, cp); - } - } - return new int[]{box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()}; - } catch (RuntimeException e) { - throw NativeStructureGenerationException.failure( - "capture placement", structureKey, chunkX, chunkZ, e); - } - } - - @Override - public boolean supportsStructureCapture() { - return true; - } - - @Override - public KList nativeStructureVolumes(Engine engine, int minX, int minZ, int maxX, int maxZ) { - return NativeStructureVolumeIndex.volumes(engine, minX, minZ, maxX, maxZ); - } - - @Override - public void invalidateNativeStructureVolumeIndex(Engine engine) { - NativeStructureVolumeIndex.invalidate(engine); - } - - @Override - public int getBiomeId(Biome biome) { - for (World i : Bukkit.getWorlds()) { - if (i.getEnvironment().equals(World.Environment.NORMAL)) { - Registry registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null); - if (registry == null) { - continue; - } - Object baseRaw = getBiomeBase(registry, biome); - net.minecraft.world.level.biome.Biome base; - if (baseRaw instanceof Holder holder) { - Object value = holder.value(); - if (!(value instanceof net.minecraft.world.level.biome.Biome resolved)) { - continue; - } - base = resolved; - } else if (baseRaw instanceof net.minecraft.world.level.biome.Biome resolved) { - base = resolved; - } else { - continue; - } - return registry.getId(base); - } - } - - List biomes = new ArrayList<>(); - for (Biome entry : org.bukkit.Registry.BIOME) { - biomes.add(entry); - } - int index = biomes.indexOf(biome); - return Math.max(index, 0); - } - - private MCAIdMap getBiomeMapping() { - return biomeMapCache.aquire(() -> new MCAIdMap<>() { - @NotNull - @Override - public Iterator iterator() { - return getCustomBiomeRegistry().iterator(); - } - - @Override - public int getId(net.minecraft.world.level.biome.Biome paramT) { - return getCustomBiomeRegistry().getId(paramT); - } - - @Override - public net.minecraft.world.level.biome.Biome byId(int paramInt) { - return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt); - } - }); - } - - @NotNull - private MCABiomeContainer getBiomeContainerInterface(MCAIdMap biomeMapping, MCAChunkBiomeContainer base) { - return new MCABiomeContainer() { - @Override - public int[] getData() { - return base.writeBiomes(); - } - - @Override - public void setBiome(int x, int y, int z, int id) { - base.setBiome(x, y, z, biomeMapping.byId(id)); - } - - @Override - public int getBiome(int x, int y, int z) { - return biomeMapping.getId(base.getBiome(x, y, z)); - } - }; - } - - @Override - public MCABiomeContainer newBiomeContainer(int min, int max) { - MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max); - return getBiomeContainerInterface(getBiomeMapping(), base); - } - - @Override - public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) { - MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data); - return getBiomeContainerInterface(getBiomeMapping(), base); - } - - @Override - public int countCustomBiomes() { - AtomicInteger a = new AtomicInteger(0); - - getCustomBiomeRegistry().keySet().forEach((i) -> { - if (i.getNamespace().equals("minecraft")) { - return; - } - - a.incrementAndGet(); - IrisLogging.debug("Custom Biome: " + i); - }); - - return a.get(); - } - - public boolean supportsDataPacks() { - return true; - } - - public void setBiomes(int cx, int cz, World world, Hunk biomes) { - LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz); - biomes.iterateSync((x, y, z, b) -> c.setNoiseBiome(x, y, z, (Holder) b)); - c.markUnsaved(); - } - - @Override - public MCAPaletteAccess createPalette() { - MCAIdMapper registry = registryCache.aquireNastyPrint(() -> { - Field cf = IdMapper.class.getDeclaredField("tToId"); - Field df = IdMapper.class.getDeclaredField("idToT"); - Field bf = IdMapper.class.getDeclaredField("nextId"); - cf.setAccessible(true); - df.setAccessible(true); - bf.setAccessible(true); - IdMapper blockData = Block.BLOCK_STATE_REGISTRY; - int b = bf.getInt(blockData); - Object2IntMap c = (Object2IntMap) cf.get(blockData); - List d = (List) df.get(blockData); - return new MCAIdMapper(c, d, b); - }); - if (registry == null) { - throw new IllegalStateException("Iris cannot mirror the Minecraft block state id map on this server version"); - } - MCAPalette global = globalCache.aquireNastyPrint(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState())); - if (global == null) { - throw new IllegalStateException("Iris cannot build the global block state palette on this server version"); - } - java.util.Map, BlockState> innerDecodeCache = new java.util.concurrent.ConcurrentHashMap<>(64); - java.util.Map outerDecodeCache = new java.util.concurrent.ConcurrentHashMap<>(64); - MCAPalettedContainer container = new MCAPalettedContainer<>(global, registry, - i -> innerDecodeCache.computeIfAbsent(i, t -> ((CraftBlockData) NBTWorld.getBlockData(t)).getState()), - i -> NBTWorld.getCompound(CraftBlockData.createData(i)), - ((CraftBlockData) AIR).getState()); - return new MCAWrappedPalettedContainer<>(container, - i -> NBTWorld.getCompound(CraftBlockData.createData(i)), - i -> outerDecodeCache.computeIfAbsent(i, t -> ((CraftBlockData) NBTWorld.getBlockData(t)).getState())); - } - - @Override - public void injectBiomesFromMantle(Chunk e, Mantle mantle) { - ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL); - AtomicInteger c = new AtomicInteger(); - AtomicInteger r = new AtomicInteger(); - mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> { - if (b != null) { - if (b.isCustom()) { - chunk.setNoiseBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get()); - c.getAndIncrement(); - } else { - chunk.setNoiseBiome(x, y, z, getCustomBiomeRegistry() - .get(net.minecraft.resources.Identifier.parse(b.getBiomeKey())) - .orElseGet(() -> getCustomBiomeRegistry() - .get(net.minecraft.resources.Identifier.parse("minecraft:plains")) - .orElseThrow())); - r.getAndIncrement(); - } - } - }); - } - - @Override - public boolean applyChunkBlocks(Chunk bukkitChunk, TerrainChunk data) { - if (!(data.getChunkData() instanceof CraftChunkData chunkData)) { - return false; - } - - try { - ServerLevel level = ((CraftWorld) bukkitChunk.getWorld()).getHandle(); - LevelChunk chunk = level.getChunk(bukkitChunk.getX(), bukkitChunk.getZ()); - ChunkAccess source = chunkData.getHandle(); - removeBlockEntities(chunk); - - int minY = level.getMinY(); - int baseX = chunk.getPos().getMinBlockX(); - int baseZ = chunk.getPos().getMinBlockZ(); - for (int i = 0; i < chunk.getSectionsCount(); i++) { - LevelChunkSection target = chunk.getSection(i); - LevelChunkSection from = source.getSection(i); - if (from.hasOnlyAir() && target.hasOnlyAir()) { - continue; - } - - int sectionBaseY = minY + (i << 4); - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - BlockState state = from.getBlockState(x, y, z); - target.setBlockState(x, y, z, state, false); - if (state.hasBlockEntity() && state.getBlock() instanceof EntityBlock entityBlock) { - BlockPos pos = new BlockPos(baseX + x, sectionBaseY + y, baseZ + z); - BlockEntity entity = entityBlock.newBlockEntity(pos, state); - if (entity != null) { - chunk.setBlockEntity(entity); - } - } - } - } - } - } - - finishChunkRewrite(level, chunk); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, Hunk data) { - if (!(chunkData instanceof CraftChunkData craftChunkData)) { - return false; - } - - try { - ChunkAccess access = craftChunkData.getHandle(); - int minY = craftChunkData.getMinHeight(); - int height = craftChunkData.getMaxHeight() - minY; - int accessMinY = access.getMinY(); - int accessMaxY = accessMinY + access.getHeight(); - int yStart = Math.max(0, accessMinY - minY); - int yEnd = Math.min(height, accessMaxY - minY); - int baseX = access.getPos().getMinBlockX(); - int baseZ = access.getPos().getMinBlockZ(); - ChunkDataHunkHolder holder = data instanceof ChunkDataHunkHolder chunkDataHolder ? chunkDataHolder : null; - for (int z = 0; z < 16; z++) { - for (int y = yStart; y < yEnd; y++) { - int blockY = y + minY; - int sectionIndex = (blockY - accessMinY) >> 4; - LevelChunkSection section = access.getSection(sectionIndex); - int sectionY = blockY & 15; - for (int x = 0; x < 16; x++) { - PlatformBlockState platformState = holder == null ? data.getRaw(x, y, z) : holder.getStoredRaw(x, y, z); - if (platformState == null) { - continue; - } - - BlockData blockData = (BlockData) platformState.nativeHandle(); - if (blockData instanceof IrisCustomData customData) { - blockData = customData.getBase(); - } - if (!(blockData instanceof CraftBlockData craftBlockData)) { - return false; - } - - BlockState state = craftBlockData.getState(); - BlockState oldState = section.setBlockState(x, sectionY, z, state, false); - if (state.hasBlockEntity()) { - BlockPos pos = new BlockPos(baseX + x, blockY, baseZ + z); - BlockEntity entity = ((EntityBlock) state.getBlock()).newBlockEntity(pos, state); - if (entity == null) { - access.removeBlockEntity(pos); - } else { - access.setBlockEntity(entity); - } - } else if (oldState != null && oldState.hasBlockEntity()) { - access.removeBlockEntity(new BlockPos(baseX + x, blockY, baseZ + z)); - } - } - } - } - - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean forceEvictChunk(World world, int chunkX, int chunkZ) { - try { - if (!world.isChunkLoaded(chunkX, chunkZ)) { - return true; - } - return world.unloadChunk(chunkX, chunkZ, true); - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public CompletableFuture flushSavedTerrainCapture(World world) { - return NmsSavedTerrainCapture.flush(world); - } - - @Override - public CompletableFuture captureSavedTerrainChunk(World world, int chunkX, int chunkZ, - int minimumY, int height) { - return NmsSavedTerrainCapture.capture(world, chunkX, chunkZ, minimumY, height); - } - - @Override - public boolean saveAndUnloadChunk(World world, int x, int z) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkHolderManager chm = level.moonrise$getChunkTaskScheduler().chunkHolderManager; - chm.processTicketUpdates(); - NewChunkHolder holder = chm.getChunkHolder(x, z); - if (holder != null) { - holder.save(false); - } - chm.processUnloads(); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean pollChunkTask(World world) { - if (FoliaScheduler.isRegionizedRuntime(Bukkit.getServer())) { - return false; - } - ServerLevel level = ((CraftWorld) world).getHandle(); - if (Thread.currentThread() != level.getServer().getRunningThread()) { - throw new IllegalStateException("Native chunk tasks require the server lifecycle thread"); - } - return level.getChunkSource().pollTask(); - } - - @Override - public void flushChunkIO(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - MoonriseRegionFileIO.flush(level); - } - - @Override - public void reconcileNativeStructurePois(Chunk chunk) { - NativeStructurePoiUpdates.reconcile((LevelChunk) ((CraftChunk) chunk).getHandle(ChunkStatus.FULL)); - } - - @Override - public boolean clearChunkBlocks(Chunk bukkitChunk) { - try { - ServerLevel level = ((CraftWorld) bukkitChunk.getWorld()).getHandle(); - LevelChunk chunk = level.getChunk(bukkitChunk.getX(), bukkitChunk.getZ()); - removeBlockEntities(chunk); - - BlockState air = ((CraftBlockData) AIR).getState(); - for (int i = 0; i < chunk.getSectionsCount(); i++) { - LevelChunkSection section = chunk.getSection(i); - if (section.hasOnlyAir()) { - continue; - } - - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - section.setBlockState(x, y, z, air, false); - } - } - } - } - - finishChunkRewrite(level, chunk); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - private void removeBlockEntities(LevelChunk chunk) { - for (BlockPos pos : new ArrayList<>(chunk.getBlockEntities().keySet())) { - chunk.removeBlockEntity(pos); - } - } - - private void finishChunkRewrite(ServerLevel level, LevelChunk chunk) { - Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); - chunk.markUnsaved(); - ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) level.getChunkSource().getLightEngine(); - lightEngine.starlight$serverRelightChunks(List.of(chunk.getPos()), p -> { - }, c -> { - }); - } - - public ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException { - if (customNbt != null && !customNbt.isEmpty()) { - net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack); - - try { - net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString()); - tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag()); - s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); - } catch (CommandSyntaxException var5) { - throw new IllegalArgumentException(var5); - } - - return CraftItemStack.asBukkitCopy(s); - } else { - return itemStack; - } - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - validateDimensionContract(engine, world, level); - - ChunkMap chunkMap = level.getChunkSource().chunkMap; - Field worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); - worldGenContextField.setAccessible(true); - WorldGenContext worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); - - IrisChunkGenerator irisGenerator = new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world); - WorldGenContext newContext = new WorldGenContext( - worldGenContext.level(), irisGenerator, - worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener()); - - worldGenContextField.set(chunkMap, newContext); - net.minecraft.world.level.chunk.ChunkGenerator activeGenerator = level.getChunkSource().getGenerator(); - if (activeGenerator != irisGenerator) { - throw new IllegalStateException("Iris generator injection did not become the active Paper chunk generator; active=" - + activeGenerator.getClass().getName()); - } - retargetStructureCheck(level, irisGenerator); - } - - @Override - public DatapackStructureScopeResult scopeDatapackStructures( - World world, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures - ) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkMap chunkMap = level.getChunkSource().chunkMap; - ChunkGeneratorStructureState currentState = level.getChunkSource().getGeneratorState(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - ChunkGeneratorStructureState scopedState = createStructureState(level, generator, currentState); - Registry structureSetRegistry = - level.registryAccess().lookupOrThrow(Registries.STRUCTURE_SET); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( - scopedState.possibleStructureSets(), - scopeIndex, - declaredSources, - importedStructures, - DatapackStructureStateFilter.keyIndex( - structureSetRegistry.listElements().toList())); - - Field possibleSetsField = getField(scopedState.getClass(), List.class); - possibleSetsField.setAccessible(true); - possibleSetsField.set(scopedState, selection.structureSets()); - - Field stateField = getField(chunkMap.getClass(), ChunkGeneratorStructureState.class); - stateField.setAccessible(true); - BukkitChunkGenerator platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator - ? bukkitGenerator - : null; - boolean studioBootstrap = platformGenerator != null - && platformGenerator.isStudioEntryBootstrapActive(); - boolean authoringStudio = platformGenerator != null - && platformGenerator.isAuthoringStudio(); - if (authoringStudio) { - requireIrisGenerator(generator); - if (currentState.possibleStructureSets().isEmpty()) { - currentState.ensureStructuresGenerated(); - } else { - ChunkGeneratorStructureState bootstrapState = createStructureState(level, generator, currentState); - Field bootstrapSetsField = getField(bootstrapState.getClass(), List.class); - bootstrapSetsField.setAccessible(true); - bootstrapSetsField.set(bootstrapState, List.of()); - bootstrapState.ensureStructuresGenerated(); - stateField.set(chunkMap, bootstrapState); - } - } else if (studioBootstrap) { - IrisChunkGenerator irisGenerator = requireIrisGenerator(generator); - irisGenerator.retainStudioStructureState(level, chunkMap, scopedState); - try { - stateField.set(chunkMap, scopedState); - } catch (IllegalAccessException | RuntimeException | Error failure) { - irisGenerator.abandonStudioStructureState(); - throw failure; - } - } else { - initializeAndPublishStructureState( - generator, - scopedState, - () -> stateField.set(chunkMap, scopedState)); - } - return new DatapackStructureScopeResult( - selection.retainedManagedSets(), - selection.excludedManagedSets()); - } - - @Override - public CompletableFuture completeStudioStructureBootstrap(World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkMap chunkMap = level.getChunkSource().chunkMap; - IrisChunkGenerator generator = requireIrisGenerator(level.getChunkSource().getGenerator()); - IrisChunkGenerator.StudioStructureState retained = - generator.retainedStudioStructureState(level, chunkMap); - if (retained == null) { - return CompletableFuture.completedFuture(null); - } - return generator.activateStudioStructureState(retained); - } - - @Override - public void abandonStudioStructureBootstrap(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (generator instanceof IrisChunkGenerator irisGenerator) { - irisGenerator.abandonStudioStructureState(); - } - } - - private ChunkGeneratorStructureState createStructureState( - ServerLevel level, - net.minecraft.world.level.chunk.ChunkGenerator generator, - ChunkGeneratorStructureState currentState - ) { - return generator.createState( - level.registryAccess().lookupOrThrow(Registries.STRUCTURE_SET), - currentState.randomState(), - currentState.getLevelSeed(), - level.spigotConfig); - } - - private void initializeAndPublishStructureState( - net.minecraft.world.level.chunk.ChunkGenerator generator, - ChunkGeneratorStructureState structureState, - IrisChunkGenerator.StructureStatePublisher publisher - ) throws IllegalAccessException { - if (generator instanceof IrisChunkGenerator irisGenerator) { - irisGenerator.initializeAndPublishStructureState(structureState, publisher); - return; - } - structureState.ensureStructuresGenerated(); - publisher.publish(); - } - - private IrisChunkGenerator requireIrisGenerator( - net.minecraft.world.level.chunk.ChunkGenerator generator - ) { - if (generator instanceof IrisChunkGenerator irisGenerator) { - return irisGenerator; - } - throw new IllegalStateException("Studio native structure state is not owned by the active Iris generator."); - } - - private void validateDimensionContract(Engine engine, World world, ServerLevel level) { - DimensionType actualType = level.dimensionType(); - String actualTypeKey = level.dimensionTypeRegistration().unwrapKey() - .map(key -> key.identifier().toString()) - .orElse(""); - IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(engine.getDimension(), "iris"); - IrisDimensionRuntimeContract actual = new IrisDimensionRuntimeContract( - actualTypeKey, - actualType.minY(), - actualType.height(), - actualType.logicalHeight()); - String runtimeName = "Bukkit world '" + world.getName() + "'"; - expected.requireExact(runtimeName, actual); - expected.requireHeight(runtimeName, level.getMinY(), level.getHeight()); - expected.requireHeight(runtimeName, world.getMinHeight(), world.getMaxHeight() - world.getMinHeight()); - IrisLogging.debug("Loaded world " + world.getName() + " with exact Iris dimension type " + actualTypeKey); - } - - private static void retargetStructureCheck(ServerLevel level, IrisChunkGenerator generator) throws NoSuchFieldException, IllegalAccessException { - Field structureCheckField = getField(level.getClass(), StructureCheck.class); - structureCheckField.setAccessible(true); - Object structureCheck = structureCheckField.get(level); - if (structureCheck == null) { - return; - } - Field generatorField = getField(structureCheck.getClass(), net.minecraft.world.level.chunk.ChunkGenerator.class); - generatorField.setAccessible(true); - generatorField.set(structureCheck, generator); - Field biomeSourceField = getField(structureCheck.getClass(), BiomeSource.class); - biomeSourceField.setAccessible(true); - biomeSourceField.set(structureCheck, generator.getBiomeSource()); - } - - public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { - if (entity == null) { - return null; - } - - try { - // Registry lookup instead of an EntityType static-field scan: 26.2 moved the constants - // to a separate EntityTypes holder class that 26.1.2 does not have, while the registry - // resolves identically on both. ENTITY_TYPE is a DefaultedRegistry, so guard containsKey - // to avoid silently resolving unknown keys to the default entry. - Identifier key = Identifier.fromNamespaceAndPath(entity.getKey().getNamespace(), entity.getKey().getKey()); - if (!BuiltInRegistries.ENTITY_TYPE.containsKey(key)) { - return null; - } - EntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(key); - if (entityType == null) { - return null; - } - return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); - } catch (Throwable e) { - IrisLogging.error("Unable to get entity dimensions for " + entity + "!"); - IrisLogging.reportError(e); - return null; - } - } - - - @Override - public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { - if (location == null || location.getWorld() == null || type == null || type.getEntityClass() == null) { - return null; - } - CraftWorld world = (CraftWorld) location.getWorld(); - if (world.getDifficulty() == Difficulty.PEACEFUL) { - EntityType nativeType = BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse(type.getKey().toString())); - if (nativeType == null || !nativeType.isAllowedInPeaceful()) { - return null; - } - } - return world.spawn(location, type.getEntityClass(), null, reason); - } - - @Override - public Color getBiomeColor(Location location, BiomeColor type) { - ServerLevel reader = ((CraftWorld) location.getWorld()).getHandle(); - var pos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - var holder = reader.getBiome(pos); - var biome = holder.value(); - if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null)); - - var attributes = reader.environmentAttributes(); - int rgba = switch (type) { - case FOG -> attributes.getValue(EnvironmentAttributes.FOG_COLOR, pos); - case WATER -> biome.getWaterColor(); - case WATER_FOG -> attributes.getValue(EnvironmentAttributes.WATER_FOG_COLOR, pos); - case SKY -> attributes.getValue(EnvironmentAttributes.SKY_COLOR, pos); - case FOLIAGE -> biome.getFoliageColor(); - case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ()); - }; - if (rgba == 0) { - if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().foliageColorOverride().isEmpty()) - return null; - if (BiomeColor.GRASS == type && biome.getSpecialEffects().grassColorOverride().isEmpty()) - return null; - } - return new Color(rgba, true); - } - - private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { - try { - for (Field f : clazz.getDeclaredFields()) { - if (f.getType().equals(fieldType)) - return f; - } - throw new NoSuchFieldException(fieldType.getName()); - } catch (NoSuchFieldException var4) { - Class superClass = clazz.getSuperclass(); - if (superClass == null) { - throw var4; - } else { - return getField(superClass, fieldType); - } - } - } - - public static Holder biomeToBiomeBase(Registry registry, Biome biome) { - if (registry == null || biome == null) { - return null; - } - - NamespacedKey biomeKey = resolveBiomeKey(biome); - if (biomeKey == null) { - return null; - } - - ResourceKey key = ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biomeKey)); - return registry.get(key).orElse(null); - } - - private static NamespacedKey resolveBiomeKey(Biome biome) { - Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull", new Class[0]); - if (keyOrNullValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow", new Class[0]); - if (keyOrThrowValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - Object keyValue = invokeNoThrow(biome, "getKey", new Class[0]); - if (keyValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - return null; - } - - private static Object invokeNoThrow(Object target, String methodName, Class[] parameterTypes, Object... args) { - if (target == null) { - return null; - } - - try { - Method method = target.getClass().getMethod(methodName, parameterTypes); - return method.invoke(target, args); - } catch (Throwable ignored) { - return null; - } - } - - @Override - public DataVersion getDataVersion() { - DataVersion cached = dataVersion; - if (cached == null) { - MinecraftVersion detected = MinecraftVersion.detect(Bukkit.getServer()); - cached = detected != null && detected.isSameRelease(26, 1, 2) ? DataVersion.V26_1_2 : DataVersion.V26_2; - dataVersion = cached; - } - return cached; - } - - @Override - public int getSpawnChunkCount(World world) { - return 0; - } - - @Override - public boolean missingDimensionTypes(String... keys) { - var type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); - return !Arrays.stream(keys) - .map(key -> Identifier.fromNamespaceAndPath("iris", key)) - .allMatch(type::containsKey); - } - - @Override - public boolean injectBukkit() { - return worldLifecycle.injectBukkit(); - } - - @Override - public void ensureServerLevelInjection() { - worldLifecycle.ensureServerLevelInjection(); - } - - @Override - public void uninjectBukkit() { - worldLifecycle.uninjectBukkit(); - } - - @Override - public boolean isServerStopping() { - return worldLifecycle.isServerStopping(); - } - - @Override - public ServerShutdownBoundary createServerShutdownBoundary() { - return worldLifecycle.createServerShutdownBoundary(); - } - - @Override - public void deferPluginClassLoaderClose() { - worldLifecycle.deferPluginClassLoaderClose(); - } - - @Override - public void releasePluginClassLoaderClose() { - worldLifecycle.releasePluginClassLoaderClose(); - } - - @Override - public KMap> getBlockProperties() { - KMap> states = new KMap<>(); - - for (var block : registry().lookupOrThrow(Registries.BLOCK)) { - var state = block.defaultBlockState(); - if (state == null) state = block.getStateDefinition().any(); - final var finalState = state; - - states.put(CraftMagicNumbers.getMaterial(block), block.getStateDefinition() - .getProperties() - .stream() - .map(p -> createProperty(p, finalState)) - .toList()); - } - return states; - } - - private > BlockProperty createProperty(Property property, BlockState state) { - return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName); - } - - @Override - public Object createRuntimeLevelStem(Object registryAccess, ChunkGenerator raw) { - if (!(registryAccess instanceof RegistryAccess access)) { - throw new IllegalStateException("Runtime LevelStem creation requires a RegistryAccess instance."); - } - if (!(raw instanceof PlatformChunkGenerator generator)) { - throw new IllegalStateException("Generator is not platform chunk generator!"); - } - - Identifier dimensionKey = Identifier.fromNamespaceAndPath("iris", generator.getTarget().getDimension().getDimensionTypeKey()); - Holder.Reference dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE) - .getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); - return new LevelStem(dimensionType, chunkGenerator(access)); - } - - private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { - var settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - return new FlatLevelSource(settings); - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdates.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdates.java deleted file mode 100644 index 45b4c87ab..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdates.java +++ /dev/null @@ -1,142 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import ca.spottedleaf.moonrise.common.util.TickThread; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.SectionPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.entity.ai.village.poi.PoiRecord; -import net.minecraft.world.entity.ai.village.poi.PoiSection; -import net.minecraft.world.entity.ai.village.poi.PoiType; -import net.minecraft.world.entity.ai.village.poi.PoiTypes; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.ProtoChunk; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataType; - -import java.util.Arrays; -import java.util.Optional; - -final class NativeStructurePoiUpdates { - private static final NamespacedKey DIRTY_SECTIONS = new NamespacedKey("iris", "native_poi_sections"); - - private final int skipPoiFlag; - - NativeStructurePoiUpdates(int skipPoiFlag) { - this.skipPoiFlag = skipPoiFlag; - } - - static NativeStructurePoiUpdates create() { - try { - return new NativeStructurePoiUpdates(Block.class.getField("UPDATE_SKIP_POI").getInt(null)); - } catch (NoSuchFieldException e) { - return null; - } catch (IllegalAccessException e) { - throw new ExceptionInInitializerError(e); - } - } - - boolean setBlock(WorldGenLevel world, BlockPos position, BlockState state, int flags, int limit) { - if ((flags & skipPoiFlag) != 0) { - return world.setBlock(position, state, flags, limit); - } - ChunkAccess chunk = world.getChunk(position); - if (!(chunk instanceof ProtoChunk)) { - return world.setBlock(position, state, flags, limit); - } - if (!(chunk instanceof ImposterProtoChunk) - && !PoiTypes.forState(chunk.getBlockState(position)).equals(PoiTypes.forState(state))) { - markSection(chunk, chunk.getSectionIndex(position.getY())); - } - return world.setBlock(position, state, flags | skipPoiFlag, limit); - } - - static void reconcile(LevelChunk chunk) { - long[] sections = chunk.persistentDataContainer.get(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY); - if (sections == null) { - return; - } - validateSections(chunk, sections); - ServerLevel world = (ServerLevel) chunk.getLevel(); - TickThread.ensureTickThread(world, chunk.getPos(), "Native structure POIs require the chunk owning thread"); - PoiManager manager = world.getPoiManager(); - for (int word = 0; word < sections.length; word++) { - long remaining = sections[word]; - while (remaining != 0) { - int index = (word << 6) + Long.numberOfTrailingZeros(remaining); - reconcileSection(chunk, manager, index); - remaining &= remaining - 1; - } - } - chunk.persistentDataContainer.remove(DIRTY_SECTIONS); - chunk.markUnsaved(); - } - - private static void markSection(ChunkAccess chunk, int index) { - long[] sections = chunk.persistentDataContainer.get(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY); - int word = index >>> 6; - long bit = 1L << (index & 63); - if (sections == null) { - sections = new long[word + 1]; - } else { - validateSections(chunk, sections); - if (word < sections.length && (sections[word] & bit) != 0) { - return; - } - if (word >= sections.length) { - sections = Arrays.copyOf(sections, word + 1); - } - } - sections[word] |= bit; - chunk.persistentDataContainer.set(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY, sections); - } - - private static void validateSections(ChunkAccess chunk, long[] sections) { - int count = chunk.getSectionsCount(); - int words = (count + 63) >>> 6; - if (sections.length > words - || sections.length == words && (count & 63) != 0 - && (sections[words - 1] >>> (count & 63)) != 0) { - throw new IllegalArgumentException("Native POI repair sections exceed chunk height at " + chunk.getPos()); - } - } - - private static void reconcileSection(LevelChunk chunk, PoiManager manager, int index) { - SectionPos sectionPosition = SectionPos.of(chunk.getPos(), chunk.getSectionYFromSectionIndex(index)); - Optional existing = manager.getOrLoad(sectionPosition.asLong()); - if (existing.isPresent()) { - for (PoiRecord record : existing.get().getRecords(type -> true, PoiManager.Occupancy.ANY).toList()) { - if (!PoiTypes.forState(chunk.getBlockState(record.getPos())).equals(Optional.of(record.getPoiType()))) { - manager.remove(record.getPos()); - } - } - } - LevelChunkSection section = chunk.getSection(index); - if (!section.maybeHas(PoiTypes::hasPoi)) { - return; - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - Optional> type = PoiTypes.forState(section.getBlockState(x, y, z)); - if (type.isEmpty()) { - continue; - } - position.set(sectionPosition.minBlockX() + x, sectionPosition.minBlockY() + y, - sectionPosition.minBlockZ() + z); - if (!manager.getType(position).equals(type)) { - manager.add(position.immutable(), type.get()); - } - } - } - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccess.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccess.java deleted file mode 100644 index 13eb3291a..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccess.java +++ /dev/null @@ -1,606 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.QuartPos; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.attribute.EnvironmentAttributeReader; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.WorldGenLevel; -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.BlockEntityType; -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.EmptyLevelChunk; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.entity.EntityTypeTest; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.levelgen.Heightmap; -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.storage.LevelData; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.ticks.LevelTickAccess; -import net.minecraft.world.ticks.ScheduledTick; -import org.bukkit.event.entity.CreatureSpawnEvent; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; -import java.util.function.Supplier; - -final class NativeStructureWorldgenAccess implements WorldGenLevel { - private static final int WRITE_RADIUS = 1; - private static final NativeStructurePoiUpdates POI_UPDATES = NativeStructurePoiUpdates.create(); - - private final WorldGenLevel delegate; - private final ChunkPos generationCenter; - private final IntBinaryOperator surfaceFirstFreeY; - private final IntBinaryOperator floorFirstFreeY; - private final boolean virtualizeHeights; - private final Predicate protectedPosition; - private final Holder fallbackBiome; - private final BiomeManager biomeManager; - private final AABB generationBounds; - private final LevelTickAccess blockTicks; - private final LevelTickAccess fluidTicks; - private final Long2LongOpenHashMap terrainHeights; - private final Long2ObjectOpenHashMap outsideChunks; - - private NativeStructureWorldgenAccess(WorldGenLevel delegate, Boundary boundary) { - this.delegate = Objects.requireNonNull(delegate, "Native structure world access requires a delegate"); - this.generationCenter = Objects.requireNonNull( - boundary.generationCenter(), "Native structure world access requires a generation center"); - this.surfaceFirstFreeY = Objects.requireNonNull( - boundary.surfaceFirstFreeY(), "Native structure world access requires a surface resolver"); - this.floorFirstFreeY = Objects.requireNonNull( - boundary.floorFirstFreeY(), "Native structure world access requires an ocean-floor resolver"); - this.virtualizeHeights = boundary.virtualizeHeights(); - this.protectedPosition = Objects.requireNonNull( - boundary.protectedPosition(), "World access requires a protected-position predicate"); - BlockPos biomeSample = generationCenter.getMiddleBlockPosition(delegate.getSeaLevel()); - this.fallbackBiome = delegate.getBiome(biomeSample); - this.biomeManager = delegate.getBiomeManager().withDifferentSource(this); - this.generationBounds = new AABB( - generationCenter.getMinBlockX() - 16, - delegate.getMinY(), - generationCenter.getMinBlockZ() - 16, - generationCenter.getMaxBlockX() + 17, - delegate.getMinY() + delegate.getHeight(), - generationCenter.getMaxBlockZ() + 17); - this.blockTicks = new BoundedTickAccess<>(delegate.getBlockTicks(), this::isWritable); - this.fluidTicks = new BoundedTickAccess<>(delegate.getFluidTicks(), this::isWritable); - this.terrainHeights = new Long2LongOpenHashMap(); - this.terrainHeights.defaultReturnValue(Long.MIN_VALUE); - this.outsideChunks = new Long2ObjectOpenHashMap<>(); - } - - static NativeStructureWorldgenAccess create(WorldGenLevel delegate, ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - return new NativeStructureWorldgenAccess(delegate, new Boundary( - generationCenter, surfaceFirstFreeY, floorFirstFreeY, virtualizeHeights, protectedPosition)); - } - - @Override - public long getSeed() { - return delegate.getSeed(); - } - - @Override - public boolean ensureCanWrite(BlockPos position) { - return isWritable(position) && delegate.ensureCanWrite(position); - } - - @Override - public void setCurrentlyGenerating(Supplier description) { - delegate.setCurrentlyGenerating(description); - } - - @Override - public ServerLevel getLevel() { - return delegate.getLevel(); - } - - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos position) { - if (isReadable(position)) { - return delegate.getCurrentDifficultyAt(position); - } - return delegate.getCurrentDifficultyAt(generationCenter.getMiddleBlockPosition( - Mth.clamp(position.getY(), getMinY(), getMaxY() - 1))); - } - - @Override - public long nextSubTickCount() { - return delegate.nextSubTickCount(); - } - - @Override - public LevelTickAccess getBlockTicks() { - return blockTicks; - } - - @Override - public LevelTickAccess getFluidTicks() { - return fluidTicks; - } - - @Override - public LevelData getLevelData() { - return delegate.getLevelData(); - } - - @Override - public MinecraftServer getServer() { - return delegate.getServer(); - } - - @Override - public ChunkSource getChunkSource() { - return delegate.getChunkSource(); - } - - @Override - public RandomSource getRandom() { - return delegate.getRandom(); - } - - @Override - public void updateNeighborsAt(BlockPos position, Block block) { - if (isWritableNeighbourhood(position)) { - delegate.updateNeighborsAt(position, block); - } - } - - @Override - public void neighborShapeChanged(Direction direction, BlockPos position, - BlockPos neighbourPosition, BlockState neighbourState, - int updateFlags, int updateLimit) { - if (isWritable(position) && isWritable(neighbourPosition)) { - delegate.neighborShapeChanged( - direction, position, neighbourPosition, neighbourState, updateFlags, updateLimit); - } - } - - @Override - public void playSound(Entity source, BlockPos position, SoundEvent sound, - SoundSource soundSource, float volume, float pitch) { - if (isWritable(position)) { - delegate.playSound(source, position, sound, soundSource, volume, pitch); - } - } - - @Override - public void addParticle(ParticleOptions particle, double x, double y, double z, - double velocityX, double velocityY, double velocityZ) { - if (isWritable(BlockPos.containing(x, y, z))) { - delegate.addParticle(particle, x, y, z, velocityX, velocityY, velocityZ); - } - } - - @Override - public void levelEvent(Entity source, int eventId, BlockPos position, int data) { - if (isWritable(position)) { - delegate.levelEvent(source, eventId, position, data); - } - } - - @Override - public void gameEvent(Holder event, Vec3 position, GameEvent.Context context) { - if (isWritable(BlockPos.containing(position))) { - delegate.gameEvent(event, position, context); - } - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus status, boolean create) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ, status, create); - } - return create ? outsideChunk(chunkX, chunkZ) : null; - } - - @Override - public ChunkAccess getChunkIfLoadedImmediately(int chunkX, int chunkZ) { - if (!isInsideGenerationRegion(chunkX, chunkZ)) { - return null; - } - return delegate.getChunkIfLoadedImmediately(chunkX, chunkZ); - } - - @Override - public boolean hasChunk(int chunkX, int chunkZ) { - return isInsideGenerationRegion(chunkX, chunkZ) && delegate.hasChunk(chunkX, chunkZ); - } - - @Override - public int getHeight(Heightmap.Types type, int x, int z) { - if (!virtualizeHeights && isInsideGenerationRegion(x >> 4, z >> 4)) { - return delegate.getHeight(type, x, z); - } - return height(type, x, z); - } - - @Override - public int getSkyDarken() { - return delegate.getSkyDarken(); - } - - @Override - public BiomeManager getBiomeManager() { - return biomeManager; - } - - @Override - public Holder getNoiseBiome(int quartX, int quartY, int quartZ) { - return getUncachedNoiseBiome(quartX, quartY, quartZ); - } - - @Override - public Holder getUncachedNoiseBiome(int quartX, int quartY, int quartZ) { - if (isInsideGenerationRegion(QuartPos.toSection(quartX), QuartPos.toSection(quartZ))) { - return delegate.getUncachedNoiseBiome(quartX, quartY, quartZ); - } - return fallbackBiome; - } - - @Override - public boolean isClientSide() { - return delegate.isClientSide(); - } - - @Override - public int getSeaLevel() { - return delegate.getSeaLevel(); - } - - @Override - public DimensionType dimensionType() { - return delegate.dimensionType(); - } - - @Override - public int getMinY() { - return delegate.getMinY(); - } - - @Override - public int getHeight() { - return delegate.getHeight(); - } - - @Override - public RegistryAccess registryAccess() { - return delegate.registryAccess(); - } - - @Override - public FeatureFlagSet enabledFeatures() { - return delegate.enabledFeatures(); - } - - @Override - public EnvironmentAttributeReader environmentAttributes() { - return delegate.environmentAttributes(); - } - - @Override - public LevelLightEngine getLightEngine() { - return delegate.getLightEngine(); - } - - @Override - public int getBrightness(LightLayer layer, BlockPos position) { - if (isReadable(position)) { - return delegate.getBrightness(layer, position); - } - return layer == LightLayer.SKY && canSeeSky(position) ? 15 : 0; - } - - @Override - public int getRawBrightness(BlockPos position, int ambientDarkening) { - if (isReadable(position)) { - return delegate.getRawBrightness(position, ambientDarkening); - } - return Math.max(0, getBrightness(LightLayer.SKY, position) - ambientDarkening); - } - - @Override - public boolean canSeeSky(BlockPos position) { - if (isReadable(position)) { - return delegate.canSeeSky(position); - } - return position.getY() >= height(Heightmap.Types.WORLD_SURFACE_WG, position.getX(), position.getZ()); - } - - @Override - public WorldBorder getWorldBorder() { - return delegate.getWorldBorder(); - } - - @Override - public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunkForCollisions(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public BlockEntity getBlockEntity(BlockPos position) { - return isWritable(position) ? delegate.getBlockEntity(position) : null; - } - - @Override - public Optional getBlockEntity( - BlockPos position, BlockEntityType type) { - return isWritable(position) ? delegate.getBlockEntity(position, type) : Optional.empty(); - } - - @Override - public BlockState getBlockState(BlockPos position) { - return isReadable(position) ? delegate.getBlockState(position) : terrainState(position); - } - - @Override - public BlockState getBlockStateIfLoaded(BlockPos position) { - return isReadable(position) ? delegate.getBlockStateIfLoaded(position) : null; - } - - @Override - public FluidState getFluidState(BlockPos position) { - return isReadable(position) ? delegate.getFluidState(position) : terrainState(position).getFluidState(); - } - - @Override - public FluidState getFluidIfLoaded(BlockPos position) { - return isReadable(position) ? delegate.getFluidIfLoaded(position) : null; - } - - @Override - public List getEntities(Entity source, AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(source, boundedArea, predicate); - } - - @Override - public List getEntities( - EntityTypeTest type, - AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(type, boundedArea, predicate); - } - - @Override - public List players() { - return delegate.players(); - } - - @Override - public boolean isStateAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getBlockState(position)); - } - - @Override - public boolean isFluidAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getFluidState(position)); - } - - @Override - public BlockPos getHeightmapPos(Heightmap.Types type, BlockPos position) { - return position.atY(getHeight(type, position.getX(), position.getZ())); - } - - @Override - public boolean setBlock(BlockPos position, BlockState state, int updateFlags, int updateLimit) { - return isWritable(position) - && (POI_UPDATES != null - ? POI_UPDATES.setBlock(delegate, position, state, updateFlags, updateLimit) - : delegate.setBlock(position, state, updateFlags, updateLimit)); - } - - @Override - public boolean removeBlock(BlockPos position, boolean move) { - if (!isWritable(position)) { - return false; - } - return POI_UPDATES != null && delegate instanceof WorldGenRegion - ? setBlock(position, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL, Block.UPDATE_LIMIT) - : delegate.removeBlock(position, move); - } - - @Override - public boolean destroyBlock(BlockPos position, boolean drop, Entity source, int updateLimit) { - if (!isWritable(position)) { - return false; - } - return POI_UPDATES != null && delegate instanceof WorldGenRegion - ? !delegate.getBlockState(position).isAir() - && setBlock(position, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL, updateLimit) - : delegate.destroyBlock(position, drop, source, updateLimit); - } - - @Override - public boolean addFreshEntity(Entity entity) { - return isWritable(entity.blockPosition()) && delegate.addFreshEntity(entity); - } - - @Override - public boolean addFreshEntity(Entity entity, CreatureSpawnEvent.SpawnReason reason) { - return isWritable(entity.blockPosition()) && delegate.addFreshEntity(entity, reason); - } - - private boolean isReadable(BlockPos position) { - return isInsideGenerationRegion(position.getX() >> 4, position.getZ() >> 4) - && !isOutsideBuildHeight(position); - } - - private boolean isWritable(BlockPos position) { - return isReadable(position) && !protectedPosition.test(position); - } - - private boolean isWritableNeighbourhood(BlockPos position) { - return isWritable(position) - && isWritable(position.north()) - && isWritable(position.south()) - && isWritable(position.east()) - && isWritable(position.west()) - && isWritable(position.above()) - && isWritable(position.below()); - } - - private AABB boundedArea(AABB area) { - double minX = Math.max(area.minX, generationBounds.minX); - double minY = Math.max(area.minY, generationBounds.minY); - double minZ = Math.max(area.minZ, generationBounds.minZ); - double maxX = Math.min(area.maxX, generationBounds.maxX); - double maxY = Math.min(area.maxY, generationBounds.maxY); - double maxZ = Math.min(area.maxZ, generationBounds.maxZ); - if (minX >= maxX || minY >= maxY || minZ >= maxZ) { - return null; - } - if (minX == area.minX && minY == area.minY && minZ == area.minZ - && maxX == area.maxX && maxY == area.maxY && maxZ == area.maxZ) { - return area; - } - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - boolean isInsideGenerationRegion(int chunkX, int chunkZ) { - return Math.abs(chunkX - generationCenter.x()) <= WRITE_RADIUS - && Math.abs(chunkZ - generationCenter.z()) <= WRITE_RADIUS; - } - - @Override - public boolean isOutsideBuildHeight(BlockPos position) { - return position.getY() < getMinY() || position.getY() >= getMaxY(); - } - - private BlockState terrainState(BlockPos position) { - if (isOutsideBuildHeight(position)) { - return Blocks.VOID_AIR.defaultBlockState(); - } - long heights = heights(position.getX(), position.getZ()); - int surface = (int) (heights >> 32); - int floor = (int) heights; - if (position.getY() < floor) { - return Blocks.STONE.defaultBlockState(); - } - if (position.getY() < surface) { - return Blocks.WATER.defaultBlockState(); - } - return Blocks.AIR.defaultBlockState(); - } - - private int height(Heightmap.Types type, int x, int z) { - long heights = heights(x, z); - if (type == Heightmap.Types.OCEAN_FLOOR || type == Heightmap.Types.OCEAN_FLOOR_WG) { - return (int) heights; - } - return (int) (heights >> 32); - } - - private long heights(int x, int z) { - long key = ((long) x << 32) ^ (z & 0xffffffffL); - long cached = terrainHeights.get(key); - if (cached != Long.MIN_VALUE) { - return cached; - } - int floor = Mth.clamp(floorFirstFreeY.applyAsInt(x, z), getMinY(), getMaxY()); - int surface = Mth.clamp(surfaceFirstFreeY.applyAsInt(x, z), floor, getMaxY()); - long heights = ((long) surface << 32) | (floor & 0xffffffffL); - terrainHeights.put(key, heights); - return heights; - } - - private ChunkAccess outsideChunk(int chunkX, int chunkZ) { - long key = ChunkPos.pack(chunkX, chunkZ); - ChunkAccess cached = outsideChunks.get(key); - if (cached != null) { - return cached; - } - EmptyLevelChunk chunk = new EmptyLevelChunk(getLevel(), new ChunkPos(chunkX, chunkZ), fallbackBiome); - WorldgenTerrainHeightmaps.primeTerrain(chunk, surfaceFirstFreeY, floorFirstFreeY); - outsideChunks.put(key, chunk); - return chunk; - } - - private record Boundary(ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - } - - private static final class BoundedTickAccess implements LevelTickAccess { - private final LevelTickAccess delegate; - private final Predicate writable; - - private BoundedTickAccess(LevelTickAccess delegate, Predicate writable) { - this.delegate = delegate; - this.writable = writable; - } - - @Override - public void schedule(ScheduledTick tick) { - if (writable.test(tick.pos())) { - delegate.schedule(tick); - } - } - - @Override - public boolean hasScheduledTick(BlockPos position, T type) { - return writable.test(position) && delegate.hasScheduledTick(position, type); - } - - @Override - public int count() { - return delegate.count(); - } - - @Override - public boolean willTickThisTick(BlockPos position, T type) { - return writable.test(position) && delegate.willTickThisTick(position, type); - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationHooks.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationHooks.java deleted file mode 100644 index 151563fff..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationHooks.java +++ /dev/null @@ -1,61 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.platform.agent.Agent; -import it.unimi.dsi.fastutil.shorts.ShortList; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; -import java.util.function.LongPredicate; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.status.ChunkStatus; - -import java.util.List; - -final class NmsGenerationHooks { - private NmsGenerationHooks() { - } - - static void install() { - ByteBuddy buddy = new ByteBuddy(); - for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { - buddy.redefine(clazz) - .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) - .make() - .load(clazz.getClassLoader(), Agent.installed()); - } - buddy.redefine(WorldGenRegion.class) - .visit(Advice.to(WorldGenerationWriteAdvice.class).on(ElementMatchers.named("ensureCanWrite") - .and(ElementMatchers.takesArguments(BlockPos.class)))) - .make() - .load(WorldGenRegion.class.getClassLoader(), Agent.installed()); - } - - private static class WorldGenerationWriteAdvice { - @Advice.OnMethodExit - static void exit(@Advice.This WorldGenRegion region, @Advice.Argument(0) BlockPos position, - @Advice.Return(readOnly = false) boolean allowed) { - if (!allowed) { - return; - } - ChunkGenerator generator = region.getLevel().getChunkSource().getGenerator(); - if (generator instanceof LongPredicate predicate - && generator.getClass().getName().equals("art.arcane.iris.platform.bukkit.nms.v26_2_R1.IrisChunkGenerator") - && region.getChunk(position.getX() >> 4, position.getZ() >> 4) - .getPersistedStatus().isOrAfter(ChunkStatus.FULL)) { - allowed = predicate.test(ChunkPos.pack(position.getX() >> 4, position.getZ() >> 4)); - } - } - } - - private static class ChunkAccessAdvice { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) - static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { - return index >= access.getPostProcessing().length; - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationRegistry.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationRegistry.java deleted file mode 100644 index ef8c5cf12..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsGenerationRegistry.java +++ /dev/null @@ -1,182 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.spi.PlatformGenerationRegistry; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.dimension.DimensionType; - -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.function.Supplier; - -final class NmsGenerationRegistry implements PlatformGenerationRegistry { - private static final String BIOME_REGISTRY = "minecraft:worldgen/biome"; - private static final String STRUCTURE_REGISTRY = "minecraft:worldgen/structure"; - private static final String DIMENSION_TYPE_REGISTRY = "minecraft:dimension_type"; - private static final String BLOCK_REGISTRY = "minecraft:block"; - private static final String ENTITY_TYPE_REGISTRY = "minecraft:entity_type"; - - private final Supplier registryAccess; - private final String runtimeIdentity; - private final String rendererIdentity; - - NmsGenerationRegistry( - Supplier registryAccess, - String runtimeIdentity, - String rendererIdentity - ) { - this.registryAccess = Objects.requireNonNull(registryAccess, "registryAccess"); - this.runtimeIdentity = requireText(runtimeIdentity, "runtimeIdentity"); - this.rendererIdentity = requireText(rendererIdentity, "rendererIdentity"); - } - - @Override - public String runtimeIdentity() { - return runtimeIdentity; - } - - @Override - public String generatedDefinitionRendererIdentity() { - return rendererIdentity; - } - - @Override - public String customBiomeResourceKey(String identitySha256) { - return PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey(identitySha256); - } - - @Override - public List legacyCustomBiomeResourceKeys( - String packName, - String dimensionKey, - String customBiomeId - ) { - return List.of(requireText(dimensionKey, "dimensionKey").toLowerCase(Locale.ROOT) - + ":" + requireText(customBiomeId, "customBiomeId").toLowerCase(Locale.ROOT)); - } - - @Override - public String dimensionTypeResourceKey(String packName, String dimensionKey, String dimensionTypeKey) { - return "iris:" + requireText(dimensionTypeKey, "dimensionTypeKey").toLowerCase(Locale.ROOT); - } - - @Override - public Definition definition(String registryKey, String resourceKey) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> presence(access, Registries.BIOME, registryKey, resourceKey); - case STRUCTURE_REGISTRY -> presence(access, Registries.STRUCTURE, registryKey, resourceKey); - case DIMENSION_TYPE_REGISTRY -> presence( - access, - Registries.DIMENSION_TYPE, - registryKey, - resourceKey - ); - case BLOCK_REGISTRY -> presence(access, Registries.BLOCK, registryKey, resourceKey); - case ENTITY_TYPE_REGISTRY -> presence(access, Registries.ENTITY_TYPE, registryKey, resourceKey); - default -> throw new IllegalArgumentException("Unsupported generation registry: " + registryKey); - }; - } - - @Override - public Definition canonicalDefinition(String registryKey, String resourceKey, String sourceJson) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> canonicalize(access, Biome.DIRECT_CODEC, sourceJson); - case DIMENSION_TYPE_REGISTRY -> canonicalize(access, DimensionType.DIRECT_CODEC, sourceJson); - default -> throw new IllegalArgumentException( - "Registry does not accept generated JSON definitions: " + registryKey - ); - }; - } - - @Override - public Definition generatedDefinition(String registryKey, String resourceKey) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> exactRegistered( - access, - Registries.BIOME, - Biome.DIRECT_CODEC, - resourceKey - ); - case DIMENSION_TYPE_REGISTRY -> exactRegistered( - access, - Registries.DIMENSION_TYPE, - DimensionType.DIRECT_CODEC, - resourceKey - ); - default -> throw new IllegalArgumentException( - "Registry does not contain Iris-generated definitions: " + registryKey - ); - }; - } - - private static Definition presence( - RegistryAccess access, - ResourceKey> registryKey, - String registryResourceKey, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing registry definition " + registryKey.identifier() - + " / " + resourceKey + "."); - } - return Definition.resourceIdentity(registryResourceKey, resourceKey); - } - - private static Definition canonicalize(RegistryAccess access, Codec codec, String sourceJson) { - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement source = JsonParser.parseString(Objects.requireNonNull(sourceJson, "sourceJson")); - T decoded = codec.parse(ops, source).getOrThrow(IllegalArgumentException::new); - JsonElement encoded = codec.encodeStart(ops, decoded).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Definition exactRegistered( - RegistryAccess access, - ResourceKey> registryKey, - Codec codec, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing generated registry definition " - + registryKey.identifier() + " / " + resourceKey + "."); - } - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement encoded = codec.encodeStart(ops, value).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Identifier requireIdentifier(String resourceKey) { - Identifier identifier = Identifier.tryParse(resourceKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registry resource key: " + resourceKey); - } - return identifier; - } - - private static String requireText(String value, String label) { - String required = Objects.requireNonNull(value, label).trim(); - if (required.isEmpty()) { - throw new IllegalArgumentException(label + " must not be blank."); - } - return required; - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCapture.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCapture.java deleted file mode 100644 index 61f21b26c..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCapture.java +++ /dev/null @@ -1,266 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.world.history.NativeTerrainReceipt; -import art.arcane.iris.world.history.SavedTerrainChunk; -import art.arcane.iris.world.task.J; -import ca.spottedleaf.concurrentutil.completable.Completable; -import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.common.PlatformHooks; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.LockSupport; - -final class NmsSavedTerrainCapture { - private static final long WRITE_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(30L); - private static final int SNAPSHOT_BATCH_SIZE = 64; - - private NmsSavedTerrainCapture() { - } - - static CompletableFuture capture(World world, int chunkX, int chunkZ, int minimumY, int height) { - ServerLevel level = ((CraftWorld) world).getHandle(); - CompletableFuture snapshot = new CompletableFuture<>(); - J.runRegionFuture(world, chunkX, chunkZ, () -> { - try { - NewChunkHolder holder = level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ); - ChunkAccess chunk = holder == null ? null : holder.getCurrentChunk(); - if (chunk == null) { - snapshot.complete(new CapturedChunk(new ChunkPos(chunkX, chunkZ), CompletableFuture.completedFuture(null))); - return; - } - String status = BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getPersistedStatus()).toString(); - if (!SavedTerrainChunk.hasTerrain(status)) { - throw new IOException("Native chunk " + chunkX + "," + chunkZ + " has no completed terrain at " + status); - } - snapshot.complete(scheduleSnapshot(level, chunk)); - } catch (Throwable failure) { - snapshot.completeExceptionally(failure); - } - }).whenComplete((ignored, failure) -> { - if (failure != null) { - snapshot.completeExceptionally(failure); - } - }); - return snapshot.thenApplyAsync(captured -> { - try { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - CompoundTag data = awaitSnapshot(level, captured, deadline); - Checkpoint checkpoint = checkpoint(chunkX, chunkZ, data); - awaitWrite(level, checkpoint.chunk(), deadline); - if (data == null) { - return SavedTerrainChunk.read(world.getWorldFolder().toPath(), chunkX, chunkZ, minimumY, height); - } - verifyCheckpoint(world.getWorldFolder().toPath(), checkpoint); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (DataOutputStream output = new DataOutputStream(bytes)) { - NbtIo.write(data, output); - } - return SavedTerrainChunk.readNbt(bytes.toByteArray(), chunkX, chunkZ, minimumY, height); - } catch (IOException failure) { - throw new CompletionException(failure); - } - }); - } - - static CompletableFuture flush(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - List holders = level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolders(); - return CompletableFuture.runAsync(() -> { - try { - flushSnapshots(world, level, holders); - } catch (IOException failure) { - throw new CompletionException(failure); - } - }); - } - - static void flushSnapshots(World world, ServerLevel level, List holders) throws IOException { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - List checkpoints = new ArrayList<>(holders.size()); - for (int offset = 0; offset < holders.size(); offset += SNAPSHOT_BATCH_SIZE) { - int end = Math.min(holders.size(), offset + SNAPSHOT_BATCH_SIZE); - List> snapshots = requestSnapshots(world, level, holders.subList(offset, end)); - awaitSnapshots(snapshots, deadline); - for (CompletableFuture future : snapshots) { - CapturedChunk captured = future.join(); - CompoundTag data = awaitSnapshot(level, captured, deadline); - checkpoints.add(checkpoint(captured.chunk().x(), captured.chunk().z(), data)); - } - for (int index = offset; index < end; index++) { - awaitWrite(level, checkpoints.get(index).chunk(), deadline); - } - } - finishCheckpoint(level, world.getWorldFolder().toPath(), checkpoints); - } - - private static List> requestSnapshots( - World world, ServerLevel level, List holders) { - List> snapshots = new ArrayList<>(holders.size()); - for (NewChunkHolder original : holders) { - ChunkPos position = new ChunkPos(original.chunkX, original.chunkZ); - CompletableFuture snapshot = new CompletableFuture<>(); - snapshots.add(snapshot); - J.runRegionFuture(world, position.x(), position.z(), () -> { - try { - NewChunkHolder holder = level.moonrise$getChunkTaskScheduler().chunkHolderManager - .getChunkHolder(position.x(), position.z()); - ChunkAccess chunk = holder == null ? null : holder.getCurrentChunk(); - snapshot.complete(chunk != null && chunk.isUnsaved() ? scheduleSnapshot(level, chunk) - : new CapturedChunk(position, CompletableFuture.completedFuture(null))); - } catch (Throwable failure) { - snapshot.completeExceptionally(failure); - } - }).whenComplete((ignored, failure) -> { - if (failure != null) { - snapshot.completeExceptionally(failure); - } - }); - } - return snapshots; - } - - private static void awaitSnapshots(List> snapshots, long deadlineNanos) - throws IOException { - try { - CompletableFuture.allOf(snapshots.toArray(CompletableFuture[]::new)) - .get(Math.max(0L, deadlineNanos - System.nanoTime()), TimeUnit.NANOSECONDS); - } catch (InterruptedException failure) { - Thread.currentThread().interrupt(); - throw new IOException("Interrupted while copying terrain checkpoint chunks.", failure); - } catch (ExecutionException | TimeoutException failure) { - throw new IOException("Unable to copy terrain checkpoint chunks.", failure); - } - } - - static void finishCheckpoint(ServerLevel level, Path worldFolder, List checkpoints) throws IOException { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - for (Checkpoint checkpoint : checkpoints) { - awaitWrite(level, checkpoint.chunk(), deadline); - } - MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA); - for (Checkpoint checkpoint : checkpoints) { - verifyCheckpoint(worldFolder, checkpoint); - } - } - - static Checkpoint checkpoint(int chunkX, int chunkZ, CompoundTag data) { - if (data == null) { - return new Checkpoint(new ChunkPos(chunkX, chunkZ), null); - } - byte[] receipt = data.getCompound("ChunkBukkitValues") - .flatMap(values -> values.getByteArray(NativeTerrainReceipt.NBT_KEY)).orElse(null); - long structureActivation = data.getCompound("ChunkBukkitValues").map(values -> values.getLongOr( - NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY, 0)).orElse(0L); - return new Checkpoint(new ChunkPos(chunkX, chunkZ), new Verification( - data.getStringOr("Status", ""), receipt, structureActivation)); - } - - private static void verifyCheckpoint(Path worldFolder, Checkpoint checkpoint) throws IOException { - Verification expected = checkpoint.verification(); - if (expected == null) { - return; - } - SavedTerrainChunk.verifyCheckpoint(worldFolder, checkpoint.chunk().x(), checkpoint.chunk().z(), - expected.status(), expected.receipt(), expected.structureActivation()); - } - - static void awaitWrite(ServerLevel level, ChunkPos chunk, long deadlineNanos) throws IOException { - ChunkTaskScheduler scheduler = level.moonrise$getChunkTaskScheduler(); - while (true) { - checkWriteWait(chunk, deadlineNanos); - if (MoonriseRegionFileIO.getPriority(level, chunk.x(), chunk.z(), - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA) == Priority.COMPLETING) { - return; - } - helpSaveTasks(scheduler); - } - } - - static CapturedChunk scheduleSnapshot(ServerLevel level, ChunkAccess chunk) { - SerializableChunkData snapshot = SerializableChunkData.copyOf(level, chunk); - PlatformHooks.get().chunkSyncSave(level, chunk, snapshot); - Completable nativeData = new Completable<>(); - CompletableFuture data = new CompletableFuture<>(); - PrioritisedExecutor.PrioritisedTask task = level.moonrise$getChunkTaskScheduler().saveExecutor - .createTask(() -> serializeSnapshot(snapshot, nativeData, data)); - task.queue(); - ChunkPos position = chunk.getPos(); - MoonriseRegionFileIO.scheduleSave(level, position.x(), position.z(), nativeData, task, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL); - return new CapturedChunk(position, data); - } - - static CompoundTag awaitSnapshot(ServerLevel level, CapturedChunk captured, long deadlineNanos) throws IOException { - ChunkTaskScheduler scheduler = level.moonrise$getChunkTaskScheduler(); - while (!captured.data().isDone()) { - checkWriteWait(captured.chunk(), deadlineNanos); - helpSaveTasks(scheduler); - } - try { - return captured.data().join(); - } catch (CompletionException failure) { - throw new IOException("Unable to serialize terrain checkpoint chunk " + captured.chunk(), failure.getCause()); - } - } - - private static void serializeSnapshot(SerializableChunkData snapshot, Completable nativeData, - CompletableFuture data) { - try { - CompoundTag serialized = snapshot.write(); - nativeData.complete(serialized); - data.complete(serialized); - } catch (Throwable failure) { - nativeData.completeExceptionally(failure); - data.completeExceptionally(failure); - } - } - - private static void checkWriteWait(ChunkPos chunk, long deadlineNanos) throws IOException { - if (Thread.currentThread().isInterrupted()) { - throw new IOException("Interrupted while saving terrain checkpoint chunk " + chunk); - } - if (System.nanoTime() - deadlineNanos >= 0L) { - throw new IOException("Timed out saving terrain checkpoint chunk " + chunk); - } - } - - private static void helpSaveTasks(ChunkTaskScheduler scheduler) { - boolean helped = scheduler.saveExecutor.executeTask(); - helped |= scheduler.compressionExecutor.executeTask(); - if (!helped) { - LockSupport.parkNanos(1_000_000L); - } - } - - record CapturedChunk(ChunkPos chunk, CompletableFuture data) { - } - - record Checkpoint(ChunkPos chunk, Verification verification) { - } - - record Verification(String status, byte[] receipt, long structureActivation) { - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java deleted file mode 100644 index 818f8b6c1..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java +++ /dev/null @@ -1,403 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.platform.bukkit.nms.ServerShutdownBoundary; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.localization.C; -import art.arcane.iris.platform.agent.Agent; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.agent.builder.ResettableClassFileTransformer; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.matcher.ElementMatchers; -import net.bytebuddy.utility.JavaModule; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.storage.LevelStorageSource; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.plugin.Plugin; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicBoolean; - -final class NmsWorldLifecycle { - private final AtomicBoolean injected = new AtomicBoolean(); - private volatile ResettableClassFileTransformer levelStorageAccessTransformer; - private volatile ResettableClassFileTransformer serverLevelTransformer; - private volatile ResettableClassFileTransformer pluginClassLoaderTransformer; - private boolean pluginClassLoaderCloseDeferred; - - public boolean injectBukkit() { - synchronized (injected) { - if (injected.get()) { - return true; - } - try { - IrisLogging.info("Injecting Bukkit"); - Agent.requireClassLoaderCloseDeferral(); - Class loaderCloseType = getClass().getClassLoader().getClass() - .getMethod("close").getDeclaringClass(); - PluginClassLoaderInjectionListener loaderListener = new PluginClassLoaderInjectionListener(); - pluginClassLoaderTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .with(AgentBuilder.RedefinitionStrategy.Listener.ErrorEscalating.FAIL_FAST) - .with(loaderListener) - .type(ElementMatchers.is(loaderCloseType)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(PluginClassLoaderCloseAdvice.class) - .on(ElementMatchers.named("close") - .and(ElementMatchers.takesArguments(0)) - .and(ElementMatchers.returns(void.class))))) - .installOn(Agent.getInstrumentation()); - loaderListener.requireInstalled(); - levelStorageAccessTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .type(ElementMatchers.is(LevelStorageSource.LevelStorageAccess.class)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(LevelStorageAccessAdvice.class).on(ElementMatchers.isConstructor() - .and(ElementMatchers.takesArguments(4)) - .and(ElementMatchers.takesArgument(0, LevelStorageSource.class)) - .and(ElementMatchers.takesArgument(1, String.class)) - .and(ElementMatchers.takesArgument(2, Path.class)) - .and(ElementMatchers.takesArgument(3, ResourceKey.class))))) - .installOn(Agent.getInstrumentation()); - serverLevelTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .type(ElementMatchers.is(ServerLevel.class)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor() - .and(ElementMatchers.takesArgument(0, MinecraftServer.class)) - .and(ElementMatchers.takesArgument(5, LevelStem.class))))) - .installOn(Agent.getInstrumentation()); - NmsGenerationHooks.install(); - - injected.set(true); - return true; - } catch (Throwable e) { - IrisLogging.reportError(C.RED + "Failed to inject Bukkit", e); - ResettableClassFileTransformer partialServerLevel = serverLevelTransformer; - ResettableClassFileTransformer partialStorageAccess = levelStorageAccessTransformer; - ResettableClassFileTransformer partialPluginClassLoader = pluginClassLoaderTransformer; - serverLevelTransformer = null; - levelStorageAccessTransformer = null; - pluginClassLoaderTransformer = null; - for (ResettableClassFileTransformer partial : new ResettableClassFileTransformer[]{ - partialServerLevel, - partialStorageAccess, - partialPluginClassLoader - }) { - if (partial == null) { - continue; - } - try { - partial.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION); - } catch (Throwable cleanupFailure) { - IrisLogging.reportError("Failed to remove partial Bukkit lifecycle injection", cleanupFailure); - } - } - return false; - } - } - } - - public void ensureServerLevelInjection() { - if (!injected.get()) { - throw new IllegalStateException("Iris world lifecycle injection is unavailable. Fix the Java Agent or Code Injection startup failure and restart before creating worlds."); - } - try { - Agent.getInstrumentation().retransformClasses( - LevelStorageSource.LevelStorageAccess.class, - ServerLevel.class - ); - } catch (Throwable e) { - throw new IllegalStateException("Failed to re-apply Bukkit world lifecycle injection. World creation was stopped before loading the world.", e); - } - } - - public void uninjectBukkit() { - synchronized (injected) { - ResettableClassFileTransformer activeServerLevel = serverLevelTransformer; - ResettableClassFileTransformer activeStorageAccess = levelStorageAccessTransformer; - serverLevelTransformer = null; - levelStorageAccessTransformer = null; - injected.set(false); - for (ResettableClassFileTransformer transformer : new ResettableClassFileTransformer[]{ - activeServerLevel, - activeStorageAccess - }) { - if (transformer == null) { - continue; - } - try { - transformer.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION); - } catch (Throwable e) { - IrisLogging.reportError(C.RED + "Failed to remove Bukkit world lifecycle injection", e); - } - } - } - } - - public boolean isServerStopping() { - return ((CraftServer) Bukkit.getServer()).getServer().hasStopped(); - } - - public ServerShutdownBoundary createServerShutdownBoundary() { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - return new ServerShutdownBoundary( - () -> server.hasFullyShutdown, - server.getRunningThread() - ); - } - - public void deferPluginClassLoaderClose() { - synchronized (injected) { - if (pluginClassLoaderTransformer == null || pluginClassLoaderCloseDeferred) { - return; - } - Agent.retainClassLoader(getClass().getClassLoader()); - pluginClassLoaderCloseDeferred = true; - } - } - - public void releasePluginClassLoaderClose() { - ResettableClassFileTransformer transformer; - boolean releaseLoader; - synchronized (injected) { - transformer = pluginClassLoaderTransformer; - pluginClassLoaderTransformer = null; - releaseLoader = pluginClassLoaderCloseDeferred; - pluginClassLoaderCloseDeferred = false; - } - try { - if (transformer != null - && !transformer.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)) { - throw new IllegalStateException("Failed to remove Iris plugin class loader lifecycle injection"); - } - } finally { - if (releaseLoader) { - Agent.releaseClassLoader(getClass().getClassLoader()); - } - } - } - - private static final class PluginClassLoaderInjectionListener extends AgentBuilder.Listener.Adapter { - private volatile boolean transformed; - private volatile Throwable failure; - - @Override - public void onTransformation(TypeDescription type, ClassLoader loader, JavaModule module, - boolean loaded, DynamicType dynamicType) { - transformed = true; - } - - @Override - public void onError(String typeName, ClassLoader loader, JavaModule module, - boolean loaded, Throwable throwable) { - failure = throwable; - } - - private void requireInstalled() { - if (!transformed || failure != null) { - throw new IllegalStateException("Iris plugin class loader lifecycle injection failed", failure); - } - } - } - - private static class PluginClassLoaderCloseAdvice { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) - static boolean enter(@Advice.This ClassLoader loader) throws ReflectiveOperationException { - Class installer = Class.forName( - "art.arcane.iris.platform.agent.Installer", true, ClassLoader.getSystemClassLoader()); - return Boolean.TRUE.equals(installer.getMethod("deferClassLoaderClose", ClassLoader.class) - .invoke(null, loader)); - } - } - - private static class LevelStorageAccessAdvice { - @Advice.OnMethodEnter - static void enter( - @Advice.Argument(1) String levelId, - @Advice.Argument(value = 3, readOnly = false) ResourceKey dimensionType - ) { - if (levelId == null || levelId.isBlank()) { - return; - } - - ClassLoader pluginClassLoader; - Class generatorType; - Class stagingType; - try { - Plugin irisPlugin = Bukkit.getPluginManager().getPlugin("Iris"); - if (irisPlugin == null) { - return; - } - pluginClassLoader = irisPlugin.getClass().getClassLoader(); - generatorType = Class.forName("art.arcane.iris.platform.generation.PlatformChunkGenerator", true, pluginClassLoader); - stagingType = Class.forName("art.arcane.iris.world.lifecycle.WorldLifecycleStaging", true, pluginClassLoader); - } catch (Throwable ignored) { - return; - } - - Object generator; - try { - generator = stagingType - .getDeclaredMethod("peekStemGenerator", String.class) - .invoke(null, levelId); - } catch (Throwable e) { - throw new RuntimeException("Iris failed to inspect the staged world generator", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - if (generator == null || !generatorType.isInstance(generator)) { - return; - } - - try { - Object target = generatorType.getMethod("getTarget").invoke(generator); - if (target == null) { - throw new IllegalStateException("Iris generator has no engine target."); - } - Object world = target.getClass().getMethod("getWorld").invoke(target); - if (world == null) { - throw new IllegalStateException("Iris generator target has no world identity."); - } - Object rawIdentity = world.getClass().getMethod("identity").invoke(world); - String worldIdentity = rawIdentity == null ? "" : rawIdentity.toString().trim(); - Identifier worldIdentifier = Identifier.parse(worldIdentity); - if (!"iris".equals(worldIdentifier.getNamespace()) - && !"minecraft".equals(worldIdentifier.getNamespace())) { - throw new IllegalStateException( - "Iris generator has an unmanaged world identity: " + worldIdentity); - } - dimensionType = ResourceKey.create(Registries.LEVEL_STEM, worldIdentifier); - } catch (Throwable e) { - throw new RuntimeException("Iris failed to bind the staged world storage identity", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - } - } - - private static class ServerLevelAdvice { - @Advice.OnMethodEnter - static void enter( - @Advice.Argument(0) MinecraftServer server, - @Advice.Argument(value = 4, readOnly = false) ResourceKey dimensionKey, - @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, - @Advice.AllArguments Object[] constructorArguments - ) { - if (dimensionKey == null) - return; - - // This advice is inlined into every ServerLevel construction on the server. Until a - // world is proven Iris-owned, every failure must fail OPEN (keep the vanilla stem): - // Iris being unloaded or half-loaded must never break other plugins' world creation. - String levelId; - ClassLoader pluginClassLoader; - Class generatorType; - Class stagingType; - try { - levelId = dimensionKey.identifier().getPath(); - if (levelId == null || levelId.isBlank()) { - return; - } - - Plugin irisPlugin = Bukkit.getPluginManager().getPlugin("Iris"); - if (irisPlugin == null) { - return; - } - pluginClassLoader = irisPlugin.getClass().getClassLoader(); - generatorType = Class.forName("art.arcane.iris.platform.generation.PlatformChunkGenerator", true, pluginClassLoader); - stagingType = Class.forName("art.arcane.iris.world.lifecycle.WorldLifecycleStaging", true, pluginClassLoader); - } catch (Throwable ignored) { - // Iris absent or half-loaded: fail OPEN for a world we cannot prove ours. - return; - } - - // From here Iris is present and its classes resolve; a failure resolving the - // staged generator must fail LOUD — silently handing a possibly-staged Iris world - // the vanilla stem corrupts generation. - ChunkGenerator gen = null; - try { - ChunkGenerator constructorGenerator = null; - for (Object argument : constructorArguments) { - if (argument instanceof ChunkGenerator candidate) { - constructorGenerator = candidate; - break; - } - } - Object generator = generatorType.isInstance(constructorGenerator) ? constructorGenerator : null; - if (generator == null) { - generator = stagingType - .getDeclaredMethod("consumeStemGenerator", String.class) - .invoke(null, levelId); - } - if (generator instanceof ChunkGenerator owned && owned.getClass().getPackageName().startsWith("art.arcane.iris")) { - gen = owned; - } - } catch (Throwable e) { - throw new RuntimeException("Iris failed to resolve the staged world generator", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - if (gen == null) { - return; - } - - // Past the ownership gate the world is Iris-owned; silently handing back the - // vanilla stem would corrupt generation, so failures from here rethrow. - try { - Object bindings = Class.forName("art.arcane.iris.platform.bukkit.nms.INMS", true, pluginClassLoader) - .getDeclaredMethod("get") - .invoke(null); - if (bindings == null) { - throw new IllegalStateException("Iris failed to resolve an INMSBinding instance."); - } - - Method stemMethod = null; - for (Method candidate : bindings.getClass().getMethods()) { - if (candidate.getName().equals("createRuntimeLevelStem") && candidate.getParameterCount() == 2) { - stemMethod = candidate; - break; - } - } - if (stemMethod == null) { - throw new IllegalStateException("Iris binding is missing createRuntimeLevelStem."); - } - Object target = generatorType.getMethod("getTarget").invoke(gen); - if (target == null) { - throw new IllegalStateException("Iris generator has no engine target."); - } - Object world = target.getClass().getMethod("getWorld").invoke(target); - if (world == null) { - throw new IllegalStateException("Iris generator target has no world identity."); - } - Object rawIdentity = world.getClass().getMethod("identity").invoke(world); - String worldIdentity = rawIdentity == null ? "" : rawIdentity.toString().trim(); - Identifier worldIdentifier = Identifier.parse(worldIdentity); - if (!"iris".equals(worldIdentifier.getNamespace()) - && !"minecraft".equals(worldIdentifier.getNamespace())) { - throw new IllegalStateException( - "Iris generator has an unmanaged world identity: " + worldIdentity); - } - Object resolvedStem = stemMethod.invoke(bindings, server.registryAccess(), gen); - if (!(resolvedStem instanceof LevelStem runtimeStem)) { - throw new IllegalStateException("Iris runtime LevelStem binding returned " + (resolvedStem == null ? "null" : resolvedStem.getClass().getName()) + "."); - } - dimensionKey = ResourceKey.create(Registries.DIMENSION, worldIdentifier); - levelStem = runtimeStem; - } catch (Throwable e) { - throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/VanillaStructureBiomes.java b/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/VanillaStructureBiomes.java deleted file mode 100644 index a10ae10ef..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/VanillaStructureBiomes.java +++ /dev/null @@ -1,80 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.nativegen.NativeStructureGenerationKeys; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.levelgen.structure.Structure; - -import java.util.LinkedHashSet; -import java.util.Optional; -import java.util.Set; - -final class VanillaStructureBiomes { - private VanillaStructureBiomes() { - } - - static Set possibleBiomeKeys(BiomeSource source) { - if (source == null) { - throw new IllegalStateException("Minecraft chunk generator has no biome source"); - } - Set keys = new LinkedHashSet<>(); - Set> possibleBiomes = source instanceof CustomBiomeSource customBiomeSource - ? customBiomeSource.possibleStructureBiomes() - : source.possibleBiomes(); - for (Holder holder : possibleBiomes) { - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - if (keys.isEmpty()) { - throw new IllegalStateException("Minecraft biome source exposes no registered possible biomes"); - } - return keys; - } - - static Set structureBiomeKeys(RegistryAccess access, String structureKey) { - if (access == null) { - throw new IllegalStateException("Minecraft registry access is unavailable"); - } - if (structureKey == null || structureKey.isBlank()) { - throw new IllegalArgumentException("Registered structure key must not be blank"); - } - Set keys = new LinkedHashSet<>(); - Registry registry = access.lookupOrThrow(Registries.STRUCTURE); - Structure structure = registry.getValue(Identifier.parse(structureKey)); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - boolean hasFilterEntries = false; - for (Holder holder : structure.biomes()) { - hasFilterEntries = true; - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - // An empty biome filter is legal datapack content (opt-in structures whose tags only - // reference absent modded biomes); the structure is unreachable, not an error state. - if (keys.isEmpty() && hasFilterEntries) { - throw new IllegalStateException("Registered structure '" + structureKey - + "' has biome filter entries but none resolve to registered biome keys"); - } - return keys; - } - - static Set reachableStructureKeys(ServerLevel level, BiomeSource source) { - if (level == null) { - throw new IllegalStateException("Minecraft server level is unavailable"); - } - Set possible = possibleBiomeKeys(source); - return NativeStructureGenerationKeys.reachable(level, possible); - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java index 8a0b176c9..bb54918a3 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java @@ -1,6 +1,9 @@ package art.arcane.iris.nativegen; -import art.arcane.iris.platform.bukkit.nms.v26_2_R1.IrisChunkGenerator; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeChunkGenerator; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeTerrainColumns; +import art.arcane.iris.platform.bukkit.nms.BukkitTerrainColumnPolicy; + import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.GenerationSessionLease; @@ -100,14 +103,13 @@ public final class IrisChunkGeneratorHeightCacheTest { when(engine.getCacheID()).thenReturn(1); when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop()); when(complex.resolvedTerrainColumn(anyInt(), anyInt())).thenReturn(Optional.of(signature())); - IrisChunkGenerator generator = mock(IrisChunkGenerator.class, CALLS_REAL_METHODS); - set(generator, "engine", engine); - set(generator, "terrainHeights", new NativeTerrainHeightCache()); + NativeChunkGenerator generator = mock(NativeChunkGenerator.class, CALLS_REAL_METHODS); + set(generator, "terrainColumns", new NativeTerrainColumns(new BukkitTerrainColumnPolicy(engine, null))); return new Fixture(generator, engine, complex); } - private static void set(IrisChunkGenerator generator, String name, Object value) throws Exception { - Field field = IrisChunkGenerator.class.getDeclaredField(name); + private static void set(NativeChunkGenerator generator, String name, Object value) throws Exception { + Field field = NativeChunkGenerator.class.getDeclaredField(name); field.setAccessible(true); field.set(generator, value); } @@ -125,6 +127,6 @@ public final class IrisChunkGeneratorHeightCacheTest { new TerrainBoundarySignature.BiomeEncoding(List.of("minecraft:plains"), new short[]{0})), geometry); } - private record Fixture(IrisChunkGenerator generator, Engine engine, IrisComplex complex) { + private record Fixture(NativeChunkGenerator generator, Engine engine, IrisComplex complex) { } } diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java index 1080273f1..448436814 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.nativegen; +import art.arcane.iris.structure.nativegen.GenerationWritePolicy; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeGenerationWriteGuard; + import art.arcane.iris.generation.runtime.DimensionStackContext; import art.arcane.iris.generation.runtime.DimensionStackLayout; import art.arcane.iris.generation.runtime.IrisComplex; @@ -45,11 +49,11 @@ public final class NativeGenerationWriteGuardTest { DimensionStackContext stack = mock(DimensionStackContext.class); when(objects.contains(-3, 74, -7)).thenReturn(true); - Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(objects, stack, -64); + Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, stack, -64)); assertTrue(protectedPositions.test(new BlockPos(-3, 10, -7))); verifyNoInteractions(stack); - assertFalse(NativeGenerationWriteGuard.protectedPositions(objects, null, -64) + assertFalse(NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, null, -64)) .test(new BlockPos(-3, 11, -7))); } @@ -65,7 +69,7 @@ public final class NativeGenerationWriteGuardTest { when(negative.isHostFeatureProtectedY(64)).thenReturn(true); when(positive.isHostFeatureProtectedY(65)).thenReturn(true); - Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(objects, stack, -64); + Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, stack, -64)); assertTrue(protectedPositions.test(new BlockPos(-1, 0, -1))); assertFalse(protectedPositions.test(new BlockPos(-1, 1, -1))); @@ -85,10 +89,10 @@ public final class NativeGenerationWriteGuardTest { when(router.history()).thenReturn(history); when(history.activeActivation()).thenReturn(active); when(active.activationId()).thenReturn(4L); - assertTrue(NativeGenerationWriteGuard.isHistoricalStructure(engine, 3)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 0)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 4)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 5)); + assertTrue(GenerationWritePolicy.isHistoricalStructure(engine, 3)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 0)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 4)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 5)); } @Test @@ -100,11 +104,11 @@ public final class NativeGenerationWriteGuardTest { ChunkAccess partial = mock(ChunkAccess.class); when(partial.getPersistedStatus()).thenReturn(ChunkStatus.NOISE); when(region.getChunk(anyInt(), anyInt())).thenReturn(partial); - assertTrue(NativeGenerationWriteGuard.allowsDecoration(engine, region, new ChunkPos(0, 0))); + assertTrue(NativeGenerationWriteGuard.allowsDecoration(engine.getComplex()::allowsMantleChunkWrite, region, new ChunkPos(0, 0))); ChunkAccess complete = mock(ChunkAccess.class); when(complete.getPersistedStatus()).thenReturn(ChunkStatus.FULL); when(region.getChunk(1, -1)).thenReturn(complete); - assertFalse(NativeGenerationWriteGuard.allowsDecoration(engine, region, new ChunkPos(0, 0))); + assertFalse(NativeGenerationWriteGuard.allowsDecoration(engine.getComplex()::allowsMantleChunkWrite, region, new ChunkPos(0, 0))); } @Test @@ -115,11 +119,11 @@ public final class NativeGenerationWriteGuardTest { ChunkAccess chunk = mock(ChunkAccess.class); when(chunk.getPos()).thenReturn(new ChunkPos(-2, 3)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.NOISE); - assertTrue(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.FEATURES)); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.NOISE)); + assertTrue(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.FEATURES)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.NOISE)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.FEATURES); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.FEATURES)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.FEATURES)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.FULL); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.SPAWN)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.SPAWN)); } } diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java index 3a4b97404..5261842a1 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java @@ -1,5 +1,19 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureStartInjector; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureFactory; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeJigsawMetadata; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.ForcedStructureChunkGenerator; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTemplatePoolBounds; + import art.arcane.iris.structure.jigsaw.IrisJigsawConfiguration; import art.arcane.iris.structure.placement.IrisStructureTerrain; import com.mojang.datafixers.util.Either; @@ -108,10 +122,10 @@ public class NativeStructureFactoryTest { distance.putInt("vertical", 64); axisSpecific.put("max_distance_from_center", distance); - assertEquals(80, NativeStructureFactory.sourceDistance(scalar, "horizontal")); - assertEquals(80, NativeStructureFactory.sourceDistance(scalar, "vertical")); - assertEquals(96, NativeStructureFactory.sourceDistance(axisSpecific, "horizontal")); - assertEquals(64, NativeStructureFactory.sourceDistance(axisSpecific, "vertical")); + assertEquals(80, NativeJigsawMetadata.sourceDistance(scalar, "horizontal")); + assertEquals(80, NativeJigsawMetadata.sourceDistance(scalar, "vertical")); + assertEquals(96, NativeJigsawMetadata.sourceDistance(axisSpecific, "horizontal")); + assertEquals(64, NativeJigsawMetadata.sourceDistance(axisSpecific, "vertical")); } @Test @@ -121,8 +135,8 @@ public class NativeStructureFactoryTest { .terrainAdapation(TerrainAdjustment.BEARD_BOX) .build()); - assertEquals(0, NativeStructureFactory.horizontalReferenceExpansion(none)); - assertEquals(12, NativeStructureFactory.horizontalReferenceExpansion(beardBox)); + assertEquals(0, NativeJigsawMetadata.horizontalReferenceExpansion(none)); + assertEquals(12, NativeJigsawMetadata.horizontalReferenceExpansion(beardBox)); } @Test diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java index b337b9369..828e44663 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureGenerationKeys; + import net.minecraft.SharedConstants; import net.minecraft.core.Vec3i; import net.minecraft.server.Bootstrap; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java index c1f0885ff..885666848 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureLocatePersistence; + import net.minecraft.SharedConstants; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.Bootstrap; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java index e952f3daf..b2152574e 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java @@ -1,11 +1,19 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceRepair; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureOwnershipFingerprint; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; import art.arcane.iris.structure.nativegen.IrisNativeStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -65,7 +73,7 @@ public class NativeStructureOwnershipFingerprintTest { generated, 0, 80, -64, 320); int alignedLocatorY = NativeStructureOwnershipFingerprint.locatorY(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( generated, @@ -85,11 +93,9 @@ public class NativeStructureOwnershipFingerprintTest { StructureStart reloaded = new StructureStart(structure, origin, 0, regeneratedPieces); String afterReload = NativeStructureOwnershipFingerprint.fingerprint( "minecraft:monument", reloaded); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - wrapped, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY()), - NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", wrapped, NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY())); assertEquals(beforeReload, afterReload); assertTrue(NativeStructureOwnershipFingerprint.matches(ownership, reloaded)); @@ -110,11 +116,9 @@ public class NativeStructureOwnershipFingerprintTest { for (StructurePiece piece : moved.getPieces()) { piece.move(1, 0, 0); } - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - expected, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(expected).minY()), - expected.getBoundingBox()); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", expected, expected.getBoundingBox()), plan(origin, NativeStructureReferenceEnvelope.contentBounds(expected).minY())); assertFalse(NativeStructureOwnershipFingerprint.matches(ownership, moved)); } @@ -129,18 +133,16 @@ public class NativeStructureOwnershipFingerprintTest { NativeStructureVerticalPlacer.alignOceanMonumentToSeaLevel( generated, 0, 80, -64, 320); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( generated, structure, 0, terrain); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - wrapped, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY()), - NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", wrapped, NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY())); PiecesContainer regeneratedPieces = OceanMonumentStructure.regeneratePiecesAfterLoad( origin, seed, new PiecesContainer(wrapped.getPieces())); StructureStart reloaded = new StructureStart(structure, origin, 0, regeneratedPieces); @@ -263,13 +265,10 @@ public class NativeStructureOwnershipFingerprintTest { private static NativeStructureOwnershipRecord ownership( ChunkPos origin, StructureStart start, OceanMonumentStructure structure) { - return NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - start, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY()), - NativeStructureReferenceEnvelope.referenceBounds( - start, structure, new IrisStructureTerrain()) - ); + return NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", start, NativeStructureReferenceEnvelope.referenceBounds( + start, structure, new IrisStructureTerrain())), plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY())); } private static StructureStart poolStart( diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java index 76552761d..cb0a0ad48 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java @@ -1,12 +1,22 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.StructureOwnershipRecordView; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureOwnershipRecovery; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureOwnershipFingerprint; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; import art.arcane.iris.structure.placement.StructurePlacementGrid; import art.arcane.iris.structure.nativegen.IrisNativeStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; import net.minecraft.core.Direction; @@ -63,7 +73,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, reloaded, - plan, expected, false); + plan, expected, false, NativeStructureOwnershipRecord::capture); assertNotNull(recovered); assertEquals(StructurePlacementGrid.placementIdentity(plan.placement()), @@ -71,7 +81,7 @@ public class NativeStructureOwnershipRecoveryTest { assertEquals(expectedLocatorY, recovered.locatorY()); assertNotEquals(NativeStructureOwnershipFingerprint.locatorY(reloaded), recovered.locatorY()); - assertEquals(IrisStructureTerrainMode.FORCE_CARVE, + assertEquals(StructureTerrainMode.FORCE_CARVE, recovered.restoredDecision().terrain().resolvedMode()); assertEquals(24, recovered.restoredDecision().terrain().getHorizontalPadding()); assertTrue(recovered.referenceMinChunkX() @@ -89,7 +99,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, natural, - plan, natural, true); + plan, natural, true, NativeStructureOwnershipRecord::capture); assertNull(recovered); } @@ -114,10 +124,10 @@ public class NativeStructureOwnershipRecoveryTest { StructurePlacementGrid.placementIdentity(changedIdentity.placement())); assertNull(NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, expected, - changedIdentity, expected, false)); + changedIdentity, expected, false, NativeStructureOwnershipRecord::capture)); assertNull(NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, changedGeometry, - plan(origin, "geometry-check", 0), expected, false)); + plan(origin, "geometry-check", 0), expected, false, NativeStructureOwnershipRecord::capture)); } @Test @@ -129,7 +139,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, expected, - plan(origin, "fingerprint-check", 16), expected, false); + plan(origin, "fingerprint-check", 16), expected, false, NativeStructureOwnershipRecord::capture); assertNotNull(recovered); StructureStart moved = monumentStart(structure, origin, seed); @@ -148,10 +158,11 @@ public class NativeStructureOwnershipRecoveryTest { OceanMonumentStructure structure = structure(); StructureStart start = monumentStart(structure, origin, seed); NativeStructureStartPlan plan = plan( - origin, "vacuum-envelope-refresh", IrisStructureTerrainMode.VACUUM, 0); + origin, "vacuum-envelope-refresh", StructureTerrainMode.VACUUM, 0); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(start); - NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, content); + NativeStructureOwnershipRecord stale = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, content), plan); NativeStructureOwnershipRecord refreshed = NativeStructureOwnershipRecovery.refreshReferenceEnvelope( @@ -166,7 +177,7 @@ public class NativeStructureOwnershipRecoveryTest { assertEquals(stale.locatorY(), refreshed.locatorY()); assertEquals(stale.contentFingerprint(), refreshed.contentFingerprint()); assertEquals(stale.decision(), refreshed.decision()); - assertEquals(IrisStructureTerrainMode.VACUUM, + assertEquals(StructureTerrainMode.VACUUM, refreshed.restoredDecision().terrain().resolvedMode()); BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); @@ -187,10 +198,10 @@ public class NativeStructureOwnershipRecoveryTest { OceanMonumentStructure structure = structure(); StructureStart expected = monumentStart(structure, origin, seed); NativeStructureStartPlan plan = plan( - origin, "vacuum-content-check", IrisStructureTerrainMode.VACUUM, 0); - NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture( - structureKey, expected, plan, - NativeStructureReferenceEnvelope.contentBounds(expected)); + origin, "vacuum-content-check", StructureTerrainMode.VACUUM, 0); + NativeStructureOwnershipRecord stale = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, expected, NativeStructureReferenceEnvelope.contentBounds(expected)), plan); StructureStart moved = monumentStart(structure, origin, seed); for (StructurePiece piece : moved.getPieces()) { piece.move(1, 0, 0); @@ -210,8 +221,9 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureStartPlan plan = plan(origin, "current-force-carve", 24); BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, envelope); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, envelope), plan); assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope( structureKey, structure, start, ownership)); @@ -226,19 +238,20 @@ public class NativeStructureOwnershipRecoveryTest { StructureStart start = monumentStart(structure, origin, seed); BoundingBox initial = NativeStructureReferenceEnvelope.contentBounds(start); int maximumReferenceBlockX = ((origin.x() - + NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15; + + StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15; int shiftX = maximumReferenceBlockX - initial.maxX(); for (StructurePiece piece : start.getPieces()) { piece.move(shiftX, 0, 0); } NativeStructureStartPlan plan = plan( - origin, "clipped-vacuum", IrisStructureTerrainMode.VACUUM, 0); + origin, "clipped-vacuum", StructureTerrainMode.VACUUM, 0); BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, envelope); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, envelope), plan); - assertEquals(origin.x() + NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS, + assertEquals(origin.x() + StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS, ownership.referenceMaxChunkX()); assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope( structureKey, structure, start, ownership)); @@ -270,12 +283,12 @@ public class NativeStructureOwnershipRecoveryTest { String placementId, int horizontalPadding) { return plan(origin, placementId, - IrisStructureTerrainMode.FORCE_CARVE, horizontalPadding); + StructureTerrainMode.FORCE_CARVE, horizontalPadding); } private static NativeStructureStartPlan plan(ChunkPos origin, String placementId, - IrisStructureTerrainMode terrainMode, + StructureTerrainMode terrainMode, int horizontalPadding) { IrisNativeStructure source = new IrisNativeStructure() .setStructure("minecraft:monument") diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java index 2bbc447ce..fb5fed0eb 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java @@ -1,8 +1,18 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePalette; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureTerrainSettings; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator; + import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.placement.IrisStructureYBand; import art.arcane.volmlib.util.math.RNG; import net.minecraft.SharedConstants; @@ -76,7 +86,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:stronghold", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(1) .setCeilingPadding(1) .setFloorPadding(1), @@ -100,7 +110,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:stronghold", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.ENCASE), + new IrisStructureTerrain().setMode(StructureTerrainMode.ENCASE), null); assertEquals(-1, bounds.minY()); @@ -181,7 +191,7 @@ public class NativeStructurePostProcessorEncaseTest { world(new HashMap<>()), area, List.of(new NativeStructureTerrainIntegrator.TerrainTarget( "test:bounded-snapshot", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)))); assertNotNull(sourceTerrain); assertEquals(23 * 16 * 16, sourceTerrain.sampledCells()); @@ -210,7 +220,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:stronghold", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setEncasePalette(palette), NativeStructurePostProcessorEncaseTest::tuffBlock); @@ -221,10 +231,10 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void omittedTerrainConfigurationRetainsSourceSemantics() { for (TerrainAdjustment adjustment : TerrainAdjustment.values()) { - IrisStructureTerrain resolved = NativeStructureTerrainIntegrator.resolveNativeTerrain( + StructureTerrainSettings resolved = NativeStructureTerrainIntegrator.resolveNativeTerrain( start(adjustment, 64), null); - assertEquals(IrisStructureTerrainMode.SOURCE, resolved.resolvedMode()); + assertEquals(StructureTerrainMode.SOURCE, resolved.resolvedMode()); } } @@ -240,7 +250,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "test:bury", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX() - 5, groundY, bounds.minZ())); @@ -267,7 +277,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "test:encapsulate", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX() - 11, bounds.minY(), bounds.minZ())); @@ -304,7 +314,7 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void explicitTerrainConfigurationWinsOverAutoEncase() { IrisStructureTerrain configured = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.PRESERVE); + .setMode(StructureTerrainMode.PRESERVE); assertSame(configured, NativeStructureTerrainIntegrator.resolveNativeTerrain( start(TerrainAdjustment.BURY, 64), configured)); @@ -313,11 +323,11 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void legacyTemplateAirIsClearedAfterEveryTerrainFillPath() { IrisStructureTerrain source = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.SOURCE); + .setMode(StructureTerrainMode.SOURCE); IrisStructureTerrain preserve = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.PRESERVE); + .setMode(StructureTerrainMode.PRESERVE); IrisStructureTerrain encase = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE); + .setMode(StructureTerrainMode.ENCASE); assertTrue(NativeStructureTerrainIntegrator.clearsLegacyTemplateAir( start(TerrainAdjustment.BURY, 64), source)); @@ -337,7 +347,7 @@ public class NativeStructurePostProcessorEncaseTest { BoundingBox sourceBounds = generated.getBoundingBox(); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(4); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -373,7 +383,7 @@ public class NativeStructurePostProcessorEncaseTest { StructureStart generated = start(TerrainAdjustment.BURY, 64); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(112); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -446,7 +456,7 @@ public class NativeStructurePostProcessorEncaseTest { assertEquals(minY, start.getBoundingBox().minY()); } - private static BlockState tuffBlock(IrisMaterialPalette palette, RNG rng, int x, int y, int z) { + private static BlockState tuffBlock(StructurePalette palette, RNG rng, int x, int y, int z) { assertNotNull(palette); assertNotNull(rng); return Blocks.TUFF.defaultBlockState(); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java index d6673c473..ff8c54f08 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java @@ -1,7 +1,11 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import net.minecraft.SharedConstants; import net.minecraft.core.Direction; import net.minecraft.core.HolderSet; @@ -95,7 +99,7 @@ public class NativeStructurePostProcessorMonumentTest { start.getStructure(), 0, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(4)); int offset = NativeStructureVerticalPlacer.applyVerticalPlacement( diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java index c560dc1b4..d663da32f 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java @@ -1,5 +1,11 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReflection; + import net.minecraft.SharedConstants; import net.minecraft.core.HolderSet; import net.minecraft.server.Bootstrap; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java index b8e367d8a..44e360ebe 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java @@ -1,9 +1,29 @@ package art.arcane.iris.nativegen; -import art.arcane.iris.generation.mantle.StructureCarvingFootprint; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureFoundationBuilder; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureSurfaceSupportBuilder; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVegetationClearer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTemplateOccupancy; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReflection; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.WorldgenTerrainHeightmaps; + +import art.arcane.volmlib.util.structure.StructureCarvingFootprint; +import art.arcane.volmlib.util.structure.StructureCarveShape; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; @@ -214,10 +234,10 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, x, sourceY, 0, Blocks.GRASS_BLOCK.defaultBlockState()); } assertTrue(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(start, IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(start, StructureTerrainMode.FLATTEN))); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> x < 2 ? groundY - 32 : groundY + 32); for (int x = 0; x <= 3; x++) { @@ -240,7 +260,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, 0, groundY - 19, 0, Blocks.DIRT.defaultBlockState()); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> groundY); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> groundY); for (int y = groundY - 19; y <= groundY; y++) { assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 0, y, 0)); @@ -260,9 +280,9 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Map originalSubmerged = new HashMap<>(submerged); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(buried), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> 100); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> 100); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(submerged), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> groundY - 8); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> groundY - 8); assertEquals(originalBuried, buried); assertEquals(originalSubmerged, submerged); @@ -280,12 +300,12 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart start = new StructureStart(structure, new ChunkPos(0, 0), 0, new PiecesContainer(desertStart().getPieces())); assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(start, IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(start, StructureTerrainMode.FLATTEN))); } assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(desertStart(TerrainAdjustment.BURY), IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(desertStart(TerrainAdjustment.BURY), StructureTerrainMode.FLATTEN))); assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(desertStart(TerrainAdjustment.ENCAPSULATE), IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(desertStart(TerrainAdjustment.ENCAPSULATE), StructureTerrainMode.FLATTEN))); } @Test @@ -294,8 +314,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))), new BoundingBox(0, 128, 0, 0, 136, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setFlattenRange(32); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setFlattenRange(32); BoundingBox area = new BoundingBox(0, 0, 0, 0, 255, 0); Map blocks = new HashMap<>(); put(blocks, 0, 94, 0, Blocks.GOLD_BLOCK.defaultBlockState()); @@ -323,7 +343,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))), new BoundingBox(0, 64, 0, 0, 70, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece)); - IrisStructureTerrain terrain = new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN) + IrisStructureTerrain terrain = new IrisStructureTerrain().setMode(StructureTerrainMode.FLATTEN) .setHorizontalPadding(128).setFlattenRange(128); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds(start, start.getStructure(), terrain); @@ -351,7 +371,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Map blocks = supportTerrain(0, 0); Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written); @@ -372,7 +392,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, 0, 80, 0, Blocks.STONE.defaultBlockState()); Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertTrue(written.isEmpty()); @@ -399,7 +419,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { } NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> x == footprint.minX() ? 80 : 112); assertEquals(81, piece.getBoundingBox().minY()); @@ -421,8 +441,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(4, 65, 0, 4, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24); BoundingBox area = new BoundingBox(0, 0, 0, 8, 128, 0); Map blocks = flatTerrain(area, 96); for (int x : List.of(0, 8)) { @@ -472,8 +492,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(0, 65, 0, 8, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 0); WorldGenLevel world = world(chunk); @@ -532,8 +552,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(4, 65, 0, 6, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24).setFlattenRange(32); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24).setFlattenRange(32); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 0); WorldGenLevel world = world(chunk); @@ -579,8 +599,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { } } StructureStart start = rigidSurfaceStart(pieces); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24).setFlattenRange(64); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24).setFlattenRange(64); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 8); Map blocks = new HashMap<>(); for (int x = 0; x <= 8; x++) { @@ -667,11 +687,11 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain( new NativeStructureTerrainIntegrator.TerrainTarget( "test:beard_box", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)))); assertFalse(NativeStructureSurfaceFitter.requiresSurfaceTerrain( new NativeStructureTerrainIntegrator.TerrainTarget( "test:beard_box", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.PRESERVE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.PRESERVE)))); } @Test @@ -681,15 +701,15 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.TerrainTarget sourceNone = new NativeStructureTerrainIntegrator.TerrainTarget( "test:none", none, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)); NativeStructureTerrainIntegrator.TerrainTarget vacuumNone = new NativeStructureTerrainIntegrator.TerrainTarget( "test:none", none, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); NativeStructureTerrainIntegrator.TerrainTarget vacuumBox = new NativeStructureTerrainIntegrator.TerrainTarget( "test:box", box, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); assertFalse(NativeStructureSurfaceFitter.requiresSurfaceTerrain(sourceNone)); assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(vacuumNone)); @@ -778,7 +798,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List targets = List.of( new NativeStructureTerrainIntegrator.TerrainTarget( "nova_structures:tavern_oak", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE))); NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, targets, (x, z) -> 65); @@ -886,7 +906,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 64, 0)); @@ -911,7 +931,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 65, 0)); @@ -932,7 +952,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 80); for (int y = 61; y <= 74; y++) { @@ -957,7 +977,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 79); for (int y = 61; y <= 74; y++) { @@ -989,7 +1009,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.SurfaceTerrainPlan plan = NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); for (int y = 61; y <= 64; y++) { put(blocks, 0, y, 0, Blocks.CAVE_AIR.defaultBlockState()); @@ -1029,7 +1049,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.SurfaceTerrainPlan plan = NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); put(blocks, 0, 65, 0, Blocks.COBBLESTONE.defaultBlockState()); @@ -1051,7 +1071,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { new BoundingBox(15, 65, 0, 16, 70, 0), 1, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece), TerrainAdjustment.NONE); NativeStructureTerrainIntegrator.TerrainTarget target = - surfaceTarget(start, IrisStructureTerrainMode.VACUUM); + surfaceTarget(start, StructureTerrainMode.VACUUM); BoundingBox wideArea = new BoundingBox(0, 54, 0, 31, 72, 15); BoundingBox westArea = new BoundingBox(0, 54, 0, 15, 72, 15); BoundingBox eastArea = new BoundingBox(16, 54, 0, 31, 72, 15); @@ -1100,7 +1120,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1121,7 +1141,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1145,7 +1165,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 1, 66, 0)); @@ -1170,7 +1190,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1190,7 +1210,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 65, 0)); @@ -1215,7 +1235,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1236,7 +1256,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 4, 61, 4)); @@ -1254,7 +1274,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 50); assertEquals(originalBlocks, blocks); @@ -1273,7 +1293,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60, 1); assertEquals(originalBlocks, blocks); @@ -1297,7 +1317,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); for (int y = 61; y <= 65; y++) { @@ -1317,7 +1337,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { new BoundingBox(15, 66, 0, 16, 72, 0), 1, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece), TerrainAdjustment.NONE); NativeStructureTerrainIntegrator.TerrainTarget target = - surfaceTarget(start, IrisStructureTerrainMode.VACUUM); + surfaceTarget(start, StructureTerrainMode.VACUUM); BoundingBox wideArea = new BoundingBox(0, 54, 0, 31, 74, 15); BoundingBox westArea = new BoundingBox(0, 54, 0, 15, 74, 15); BoundingBox eastArea = new BoundingBox(16, 54, 0, 31, 74, 15); @@ -1573,13 +1593,13 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Set vacuumWritten = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(vacuumBlocks), area, - List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(vacuumStart, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), vacuumWritten); assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(supportTerrain(0, 0)), area, - List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.SOURCE)), + List.of(surfaceTarget(vacuumStart, StructureTerrainMode.SOURCE)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty()); PoolElementStructurePiece distantUpper = rigidTemplatePiece( @@ -1589,7 +1609,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(lower, distantUpper), TerrainAdjustment.NONE); assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(supportTerrain(0, 0)), area, - List.of(surfaceTarget(distantStart, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(distantStart, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty()); } @@ -1676,8 +1696,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(blocks), area, List.of( - surfaceTarget(start, IrisStructureTerrainMode.PRESERVE), - surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + surfaceTarget(start, StructureTerrainMode.PRESERVE), + surfaceTarget(start, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written); @@ -2033,7 +2053,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:desert_pyramid", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX(), bounds.minY(), bounds.minZ())); @@ -2055,7 +2075,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:desert_pyramid", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(1) .setCeilingPadding(1), null); @@ -2089,7 +2109,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:ancient_city", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FORCE_CARVE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.FORCE_CARVE), null); assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, firstBounds.minX(), y, z)); @@ -2338,8 +2358,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:ancient_city", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(6) .setCeilingPadding(8) .setFloorPadding(0) @@ -2378,7 +2398,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart generated = desertStart(); BoundingBox content = generated.getBoundingBox(); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -2403,7 +2423,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart generated = desertStart(TerrainAdjustment.NONE); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.VACUUM) + .setMode(StructureTerrainMode.VACUUM) .setHorizontalPadding(64); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds( @@ -2425,7 +2445,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { ChunkPos origin = generated.getChunkPos(); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(124); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds( @@ -2575,11 +2595,11 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget( StructureStart start) { - return surfaceTarget(start, IrisStructureTerrainMode.SOURCE); + return surfaceTarget(start, StructureTerrainMode.SOURCE); } private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget( - StructureStart start, IrisStructureTerrainMode mode) { + StructureStart start, StructureTerrainMode mode) { return new NativeStructureTerrainIntegrator.TerrainTarget( "nova_structures:tavern_oak", start, new IrisStructureTerrain().setMode(mode)); @@ -2619,8 +2639,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { private static NativeStructureTerrainIntegrator.OrganicCarve organicCarve( StructureStart start, int horizontalPadding, double lobeStrength) { IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(horizontalPadding) .setCeilingPadding(8) .setFloorPadding(0) @@ -2630,7 +2650,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { return NativeStructureTerrainIntegrator.organicCarve( NativeStructureTerrainIntegrator.carveFootprint(start, horizontalPadding, NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager), - terrain, IrisStructureCarveShape.ERODED, TEST_SEED); + terrain, StructureCarveShape.ERODED, TEST_SEED); } /** @@ -2648,8 +2668,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { return true; }, SLAB_PADDING, 1_000_000); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(SLAB_PADDING) .setCeilingPadding(8) .setFloorPadding(0) @@ -2664,7 +2684,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.carveOrganicColumns(world(blocks), area, NativeStructureTerrainIntegrator.organicCarve( - footprint, terrain, IrisStructureCarveShape.ERODED, TEST_SEED)); + footprint, terrain, StructureCarveShape.ERODED, TEST_SEED)); int[] depths = new int[SLAB_DEPTH]; for (int z = 0; z < SLAB_DEPTH; z++) { diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java index 08014b250..daa609bc6 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java @@ -1,5 +1,11 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVegetationClearer; + import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java index 0e8d00137..9628c91b6 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTemplateOccupancy; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReflection; + import com.mojang.datafixers.util.Either; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java index f0e99a84e..e5c758e17 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeTerrainHeightCache; + import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraft.world.level.levelgen.Heightmap; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java index fd3515b60..289c8ae40 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java @@ -1,10 +1,14 @@ package art.arcane.iris.nativegen; +import art.arcane.iris.world.history.TerrainNativeBlockKeys; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeTransitionColumn; + import art.arcane.iris.world.history.BoundaryColumnGeometry; import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import net.minecraft.SharedConstants; @@ -44,8 +48,8 @@ public final class NativeTransitionColumnTest { @Test public void customKeysUseCarrierForNativeColumnsAndHeightmaps() { String key = "itemsadder:rocks/ruby_ore"; - PlatformBlockState custom = mock(PlatformBlockState.class); - PlatformBlockState carrier = mock(PlatformBlockState.class); + NativeBlockState custom = mock(NativeBlockState.class); + NativeBlockState carrier = mock(NativeBlockState.class); when(custom.placementBaseState()).thenReturn(carrier); when(carrier.key()).thenReturn("minecraft:oak_log[axis=z]"); IrisPlatform platform = mock(IrisPlatform.class); @@ -58,17 +62,17 @@ public final class NativeTransitionColumnTest { try (MockedStatic platforms = mockStatic(IrisPlatforms.class)) { platforms.when(IrisPlatforms::isBound).thenReturn(true); platforms.when(IrisPlatforms::get).thenReturn(platform); - NoiseColumn column = NativeTransitionColumn.column(signature, height); + NoiseColumn column = NativeTransitionColumn.column(signature.geometry(), height, TerrainNativeBlockKeys::placementKey); assertEquals(Direction.Axis.Z, column.getBlock(-15).getValue(RotatedPillarBlock.AXIS)); - assertEquals(-14, NativeTransitionColumn.height(signature, Heightmap.Types.OCEAN_FLOOR_WG, height)); + assertEquals(-14, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.OCEAN_FLOOR_WG, height, TerrainNativeBlockKeys::placementKey)); } } @Test public void preservesNativePropertiesCavesAndFluids() { - NoiseColumn column = NativeTransitionColumn.column(signature("minecraft:oak_log[axis=x]"), - LevelHeightAccessor.create(-16, 32)); + NoiseColumn column = NativeTransitionColumn.column(signature("minecraft:oak_log[axis=x]").geometry(), + LevelHeightAccessor.create(-16, 32), TerrainNativeBlockKeys::placementKey); assertEquals(Direction.Axis.X, column.getBlock(-15).getValue(RotatedPillarBlock.AXIS)); assertTrue(column.getBlock(-14).is(Blocks.CAVE_AIR)); assertEquals(Integer.valueOf(3), column.getBlock(-13).getValue(LiquidBlock.LEVEL)); @@ -79,14 +83,13 @@ public final class NativeTransitionColumnTest { public void usesNativeHeightmapPredicatesForWaterAndOpenSpace() { TerrainBoundarySignature signature = signature("minecraft:oak_log[axis=x]"); LevelHeightAccessor height = LevelHeightAccessor.create(-16, 32); - assertEquals(-12, NativeTransitionColumn.height(signature, Heightmap.Types.WORLD_SURFACE_WG, height)); - assertEquals(-14, NativeTransitionColumn.height(signature, Heightmap.Types.OCEAN_FLOOR_WG, height)); + assertEquals(-12, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.WORLD_SURFACE_WG, height, TerrainNativeBlockKeys::placementKey)); + assertEquals(-14, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.OCEAN_FLOOR_WG, height, TerrainNativeBlockKeys::placementKey)); } @Test public void rejectsUnknownSavedStatesInsteadOfReplacingTerrain() { - assertThrows(IllegalArgumentException.class, () -> NativeTransitionColumn.column( - signature("missing:removed_block"), LevelHeightAccessor.create(-16, 32))); + assertThrows(IllegalArgumentException.class, () -> NativeTransitionColumn.column(signature("missing:removed_block").geometry(), LevelHeightAccessor.create(-16, 32), TerrainNativeBlockKeys::placementKey)); } private static TerrainBoundarySignature signature(String solidState) { diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSourceStructureContractTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSourceStructureContractTest.java index 9f83d1d70..e9bd4a967 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSourceStructureContractTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSourceStructureContractTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.iris.platform.bukkit.nms.BukkitBiomePolicy; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.terrain.IrisDimension; import net.minecraft.SharedConstants; @@ -28,10 +30,10 @@ public class CustomBiomeSourceStructureContractTest { IrisDimension upper = new IrisDimension(); upper.setLoadKey("Layers/Upper"); - assertEquals("layers:upper/aurora", CustomBiomeSource.customBiomeKey(upper, "Aurora")); + assertEquals("layers:upper/aurora", BukkitBiomePolicy.customBiomeKey(upper, "Aurora")); assertNotEquals( - CustomBiomeSource.customBiomeKey(host, "Aurora"), - CustomBiomeSource.customBiomeKey(upper, "Aurora") + BukkitBiomePolicy.customBiomeKey(host, "Aurora"), + BukkitBiomePolicy.customBiomeKey(upper, "Aurora") ); } @@ -39,8 +41,8 @@ public class CustomBiomeSourceStructureContractTest { public void temporaryNaturalBiomeAnswersAreNotMemoized() { Engine temporary = engineAnsweringNaturalFallback(true, 12, -8); Engine stable = engineAnsweringNaturalFallback(false, 12, -8); - assertFalse(CustomBiomeSource.isBiomeCacheable(temporary, 12, -8)); - assertTrue(CustomBiomeSource.isBiomeCacheable(stable, 12, -8)); + assertFalse(BukkitBiomePolicy.isBiomeCacheable(temporary, 12, -8)); + assertTrue(BukkitBiomePolicy.isBiomeCacheable(stable, 12, -8)); } private static Engine engineAnsweringNaturalFallback( diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorMonumentLocateContractTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorMonumentLocateContractTest.java index 7e06960bf..d129b321f 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorMonumentLocateContractTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorMonumentLocateContractTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; -import art.arcane.iris.nativegen.NativeStructureLocateResults; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureLocateResults; import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorSpawnAdmissionTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorSpawnAdmissionTest.java index 57427d9f9..5ff7bfe53 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorSpawnAdmissionTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/IrisChunkGeneratorSpawnAdmissionTest.java @@ -1,5 +1,10 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.iris.platform.bukkit.nms.BukkitGeneratorContext; +import org.bukkit.World; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeChunkGenerator; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeBiomeSourceImpl; + import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.GenerationSessionManager; import art.arcane.iris.generation.runtime.GenerationTransitionGate; @@ -89,8 +94,8 @@ public class IrisChunkGeneratorSpawnAdmissionTest { } } - private static void setField(IrisChunkGenerator generator, String name, Object value) throws Exception { - Field field = IrisChunkGenerator.class.getDeclaredField(name); + private static void setField(NativeChunkGenerator generator, String name, Object value) throws Exception { + Field field = NativeChunkGenerator.class.getDeclaredField(name); field.setAccessible(true); field.set(generator, value); } @@ -98,7 +103,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { private static final class Fixture { private final GenerationSessionManager sessions = new GenerationSessionManager(true); private final IrisEngine engine = mock(IrisEngine.class); - private final IrisChunkGenerator generator = mock(IrisChunkGenerator.class, CALLS_REAL_METHODS); + private final NativeChunkGenerator generator = mock(NativeChunkGenerator.class, CALLS_REAL_METHODS); private final Holder biome = Holder.direct(mock(Biome.class)); private final StructureManager structures = mock(StructureManager.class); private final WeightedList expected = mock(WeightedList.class); @@ -110,7 +115,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { GenerationHistoryRuntimeRouter router = mock(GenerationHistoryRuntimeRouter.class); SavedBiomeRuntime saved = mock(SavedBiomeRuntime.class); ChunkGenerator delegate = mock(ChunkGenerator.class); - CustomBiomeSource source = mock(CustomBiomeSource.class); + NativeBiomeSourceImpl source = mock(NativeBiomeSourceImpl.class); when(engine.getGenerationSessions()).thenReturn(sessions); when(engine.getGenerationHistoryRuntimeRouter()).thenReturn(Optional.of(router)); when(router.biomes()).thenReturn(saved); @@ -126,7 +131,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { }); when(delegate.getMobsAt(biome, structures, MobCategory.MONSTER, new BlockPos(0, 64, 0))) .thenReturn(expected); - setField(generator, "engine", engine); + setField(generator, "context", new BukkitGeneratorContext(new BukkitGeneratorContext.Options(1L, engine, mock(World.class)))); setField(generator, "delegate", delegate); setField(generator, "customBiomeSource", source); } diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBindingDatapackStructureScopeTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBindingDatapackStructureScopeTest.java index da026c026..16f1c7da7 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBindingDatapackStructureScopeTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBindingDatapackStructureScopeTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeStructureSetFilter; + import art.arcane.iris.pack.datapack.DatapackIngestService; import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; @@ -59,9 +61,9 @@ public class NMSBindingDatapackStructureScopeTest { List.of(), List.of("managed:illager_barracks")); - DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection vanilla = NativeStructureSetFilter.filter( List.of(managedSet), index, Set.of()); - DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection declaring = NativeStructureSetFilter.filter( List.of(managedSet), index, index.declaredSources(List.of(SOURCE))); assertEquals(0, vanilla.structureSets().size()); @@ -80,9 +82,9 @@ public class NMSBindingDatapackStructureScopeTest { List.of("managed:tavern"), List.of()); - DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection vanilla = NativeStructureSetFilter.filter( List.of(vanillaSet), index, Set.of()); - DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection declaring = NativeStructureSetFilter.filter( List.of(vanillaSet), index, index.declaredSources(List.of(SOURCE))); assertEquals(1, vanilla.structureSets().size()); @@ -100,7 +102,7 @@ public class NMSBindingDatapackStructureScopeTest { "minecraft:custom", structureHolder("managed:only")); DatapackStructureScopeIndex index = index(List.of("managed:only"), List.of()); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(unmanagedSet), index, Set.of()); assertEquals(0, selection.structureSets().size()); @@ -124,18 +126,18 @@ public class NMSBindingDatapackStructureScopeTest { 34, 8, RandomSpreadType.LINEAR, 14357620))); DatapackStructureScopeIndex scopeIndex = index( List.of(), List.of("managed:illager_barracks")); - DatapackStructureStateFilter.StructureSetKeyIndex keyIndex = - DatapackStructureStateFilter.keyIndex(List.of(registered)); + NativeStructureSetFilter.StructureSetKeyIndex keyIndex = + NativeStructureSetFilter.keyIndex(List.of(registered)); - DatapackStructureStateFilter.Selection excluded = - DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection excluded = + NativeStructureSetFilter.filter( List.of(spigotDirect), scopeIndex, Set.of(), new IrisImportedStructureControl(), keyIndex); - DatapackStructureStateFilter.Selection retained = - DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection retained = + NativeStructureSetFilter.filter( List.of(spigotDirect), scopeIndex, scopeIndex.declaredSources(List.of(SOURCE)), @@ -150,8 +152,8 @@ public class NMSBindingDatapackStructureScopeTest { @Test public void structureScopeDoesNotLinkPaperOnlyPlacementClasses() throws IOException { - InputStream classResource = NMSBindingDatapackStructureScopeTest.class - .getResourceAsStream("DatapackStructureStateFilter.class"); + InputStream classResource = NativeStructureSetFilter.class + .getResourceAsStream("NativeStructureSetFilter.class"); assertNotNull(classResource); try (InputStream input = classResource) { String classFile = new String(input.readAllBytes(), StandardCharsets.ISO_8859_1); @@ -176,7 +178,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(complexes, fossils), index(List.of(), List.of()), Set.of(), control); RandomSpreadStructurePlacement scaledComplexes = @@ -206,7 +208,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(custom), index(List.of(), List.of()), Set.of(), control); assertSame(custom, selection.structureSets().getFirst()); @@ -238,14 +240,14 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(dependent, target), index(List.of(), List.of()), Set.of(), control); Holder scaledDependent = selection.structureSets().get(0); Holder scaledTarget = selection.structureSets().get(1); assertNotSame(dependent, scaledDependent); assertNotSame(target, scaledTarget); - assertSame(scaledTarget, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledTarget, NativeStructureSetFilter.exclusionZone( scaledDependent.value().placement()).orElseThrow().otherSet()); assertEquals(26, ((RandomSpreadStructurePlacement) scaledTarget.value().placement()).spacing()); @@ -284,14 +286,14 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(first, second), index(List.of(), List.of()), Set.of(), control); Holder scaledFirst = selection.structureSets().get(0); Holder scaledSecond = selection.structureSets().get(1); - assertSame(scaledSecond, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledSecond, NativeStructureSetFilter.exclusionZone( scaledFirst.value().placement()).orElseThrow().otherSet()); - assertSame(scaledFirst, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledFirst, NativeStructureSetFilter.exclusionZone( scaledSecond.value().placement()).orElseThrow().otherSet()); assertEquals(26, ((RandomSpreadStructurePlacement) scaledSecond.value().placement()).spacing()); @@ -310,7 +312,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(custom), index(List.of(), List.of()), Set.of(), control); StructurePlacement placement = selection.structureSets().getFirst().value().placement(); @@ -340,12 +342,12 @@ public class NMSBindingDatapackStructureScopeTest { List.of("managed:blocked"), List.of("managed:blocked")); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(vanillaSet, managedSet), index, Set.of()); assertEquals(1, selection.structureSets().size()); StructurePlacement scopedPlacement = selection.structureSets().getFirst().value().placement(); - assertEquals(0, DatapackStructureStateFilter.exclusionZone(scopedPlacement).stream().count()); + assertEquals(0, NativeStructureSetFilter.exclusionZone(scopedPlacement).stream().count()); } private static DatapackStructureScopeIndex index( diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdatesTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdatesTest.java index e95ea23fa..780746594 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdatesTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructurePoiUpdatesTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeStructurePoiUpdates; + import ca.spottedleaf.moonrise.common.util.TickThread; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccessTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccessTest.java index 46bf5b7f9..11a8992f7 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccessTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NativeStructureWorldgenAccessTest.java @@ -1,9 +1,11 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeStructureWorldgenAccess; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.nativegen.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator; +import art.arcane.volmlib.util.structure.StructureTerrainMode; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructurePostProcessor; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -84,7 +86,7 @@ public class NativeStructureWorldgenAccessTest { StructureStart start = new StructureStart(structure, generationCenter(), 0, new PiecesContainer(List.of(piece))); List targets = List.of( new NativeStructureTerrainIntegrator.TerrainTarget("minecraft:village_plains", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN))); + new IrisStructureTerrain().setMode(StructureTerrainMode.FLATTEN))); BoundingBox area = new BoundingBox(generationCenter().getMinBlockX(), -64, generationCenter().getMinBlockZ(), generationCenter().getMaxBlockX(), 319, generationCenter().getMaxBlockZ()); RecordingDelegate unbounded = new RecordingDelegate(); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkIOFlushTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkIOFlushTest.java index 82d65e665..80e5880c3 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkIOFlushTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkIOFlushTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeTerrainAccessImpl; import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; @@ -83,7 +84,7 @@ public class NmsChunkIOFlushTest { } private static final class Fixture { - private final NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + private final NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); private final CraftWorld world = mock(CraftWorld.class); private final ServerLevel level = mock(ServerLevel.class); private final RegionDataController chunks = mock(RegionDataController.class); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkTaskPollingTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkTaskPollingTest.java index 37e60eeeb..514f649fd 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkTaskPollingTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsChunkTaskPollingTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeTerrainAccessImpl; import art.arcane.volmlib.util.scheduling.FoliaScheduler; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; @@ -41,7 +42,7 @@ public class NmsChunkTaskPollingTest { ServerLevel level = mock(ServerLevel.class); MinecraftServer server = mock(MinecraftServer.class); ServerChunkCache chunks = mock(ServerChunkCache.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); CompletableFuture completed = new CompletableFuture<>(); when(world.getHandle()).thenReturn(level); when(level.getServer()).thenReturn(server); @@ -66,7 +67,7 @@ public class NmsChunkTaskPollingTest { CraftWorld world = mock(CraftWorld.class); ServerLevel level = mock(ServerLevel.class); MinecraftServer server = mock(MinecraftServer.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); when(world.getHandle()).thenReturn(level); when(level.getServer()).thenReturn(server); when(server.getRunningThread()).thenReturn(new Thread("Other server owner")); @@ -83,7 +84,7 @@ public class NmsChunkTaskPollingTest { public void foliaNeverUsesTheWorldWideTaskPump() { Server bukkitServer = mock(Server.class); CraftWorld world = mock(CraftWorld.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); try (MockedStatic bukkit = mockStatic(Bukkit.class); MockedStatic folia = mockStatic(FoliaScheduler.class)) { bukkit.when(Bukkit::getServer).thenReturn(bukkitServer); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsEntitySpawnDifficultyTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsEntitySpawnDifficultyTest.java index e3f01d057..0b9e4add5 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsEntitySpawnDifficultyTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsEntitySpawnDifficultyTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeTerrainAccessImpl; import net.minecraft.SharedConstants; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; @@ -41,7 +42,7 @@ public class NmsEntitySpawnDifficultyTest { public void peacefulRejectsEveryNativeForbiddenTypeBeforeBukkitCreatesIt() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); int rejected = 0; @@ -64,7 +65,7 @@ public class NmsEntitySpawnDifficultyTest { public void peacefulPreservesPassiveAndNativeMonsterCategoryExceptions() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); Entity created = mock(Entity.class); doReturn(created).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); @@ -77,7 +78,7 @@ public class NmsEntitySpawnDifficultyTest { @Test public void nonPeacefulDifficultiesPreserveHostileSpawns() { CraftWorld world = mock(CraftWorld.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); Entity created = mock(Entity.class); doReturn(created).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); @@ -92,7 +93,7 @@ public class NmsEntitySpawnDifficultyTest { public void unrelatedBukkitSpawnFailuresStillPropagate() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); IllegalStateException failure = new IllegalStateException("Spawn rejected by platform"); doThrow(failure).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCheckpointTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCheckpointTest.java deleted file mode 100644 index 58535530a..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainCheckpointTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.world.history.SavedTerrainChunk; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import net.minecraft.SharedConstants; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; - -public class NmsSavedTerrainCheckpointTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void checkpointWaitsForDirtyAndUnloadedChunkWritesBeforeFlushAndVerification() throws Exception { - ServerLevel level = mock(ServerLevel.class); - Path root = Path.of("checkpoint-world"); - byte[] receipt = new byte[]{1, 2, 3}; - NmsSavedTerrainCapture.Checkpoint saved = new NmsSavedTerrainCapture.Checkpoint(new ChunkPos(4, -5), - new NmsSavedTerrainCapture.Verification("minecraft:full", receipt, 7L)); - NmsSavedTerrainCapture.Checkpoint unloaded = NmsSavedTerrainCapture.checkpoint(-8, 9, null); - Set completed = new HashSet<>(); - AtomicBoolean flushed = new AtomicBoolean(); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, 4, -5, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - completed.add(saved.chunk()); - return Priority.COMPLETING; - }); - io.when(() -> MoonriseRegionFileIO.getPriority(level, -8, 9, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - completed.add(unloaded.chunk()); - return Priority.COMPLETING; - }); - io.when(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - assertEquals(Set.of(saved.chunk(), unloaded.chunk()), completed); - flushed.set(true); - return null; - }); - terrain.when(() -> SavedTerrainChunk.verifyCheckpoint(root, 4, -5, "minecraft:full", receipt, 7L)) - .thenAnswer(invocation -> { - assertTrue(flushed.get()); - return null; - }); - - NmsSavedTerrainCapture.finishCheckpoint(level, root, List.of(saved, unloaded)); - - terrain.verify(() -> SavedTerrainChunk.verifyCheckpoint(root, 4, -5, "minecraft:full", receipt, 7L)); - io.verify(() -> MoonriseRegionFileIO.flush(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), never()); - io.verify(() -> MoonriseRegionFileIO.getPriority(level, 100, 100, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), never()); - terrain.verifyNoMoreInteractions(); - } - } - - @Test - public void storageFlushFailureCannotCompleteTheCheckpoint() throws Exception { - ServerLevel level = mock(ServerLevel.class); - IOException failure = new IOException("storage flush failed"); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenThrow(failure); - - assertSame(failure, assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.finishCheckpoint(level, Path.of("checkpoint-world"), List.of()))); - terrain.verifyNoInteractions(); - } - } - - @Test - public void persistedReceiptMismatchCannotCompleteTheCheckpoint() throws Exception { - ServerLevel level = mock(ServerLevel.class); - Path root = Path.of("checkpoint-world"); - byte[] receipt = new byte[]{4, 5}; - IOException failure = new IOException("saved terrain receipt mismatch"); - NmsSavedTerrainCapture.Checkpoint saved = new NmsSavedTerrainCapture.Checkpoint(new ChunkPos(1, 2), - new NmsSavedTerrainCapture.Verification("minecraft:noise", receipt, 3L)); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, 1, 2, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenReturn(Priority.COMPLETING); - terrain.when(() -> SavedTerrainChunk.verifyCheckpoint(root, 1, 2, "minecraft:noise", receipt, 3L)) - .thenThrow(failure); - - assertSame(failure, assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.finishCheckpoint(level, root, List.of(saved)))); - io.verify(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)); - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainSnapshotTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainSnapshotTest.java deleted file mode 100644 index 58288c28f..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainSnapshotTest.java +++ /dev/null @@ -1,223 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import art.arcane.iris.world.task.J; -import ca.spottedleaf.concurrentutil.completable.Completable; -import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -import ca.spottedleaf.concurrentutil.executor.thread.BalancedPrioritisedThreadPool; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.common.PlatformHooks; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import net.minecraft.SharedConstants; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; -import org.bukkit.World; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class NmsSavedTerrainSnapshotTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void ownerReservesSaveOrderBeforeOffOwnerSerializationAndLaterAutosave() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor(); - MockedStatic snapshots = mockStatic(SerializableChunkData.class); - MockedStatic hooks = mockStatic(PlatformHooks.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - Thread owner = Thread.currentThread(); - ChunkAccess chunk = mock(ChunkAccess.class); - SerializableChunkData snapshot = mock(SerializableChunkData.class); - PlatformHooks platform = mock(PlatformHooks.class); - CompoundTag capturedData = new CompoundTag(); - CompoundTag autosave = new CompoundTag(); - List submissions = new ArrayList<>(); - when(chunk.getPos()).thenReturn(new ChunkPos(4, -9)); - snapshots.when(() -> SerializableChunkData.copyOf(fixture.level, chunk)).thenAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - return snapshot; - }); - hooks.when(PlatformHooks::get).thenReturn(platform); - doAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - return null; - }).when(platform).chunkSyncSave(fixture.level, chunk, snapshot); - when(snapshot.write()).thenAnswer(invocation -> { - assertNotSame(owner, Thread.currentThread()); - assertEquals(List.of("checkpoint", "autosave"), submissions); - return capturedData; - }); - io.when(() -> MoonriseRegionFileIO.scheduleSave(eq(fixture.level), eq(4), eq(-9), - any(Completable.class), any(PrioritisedExecutor.PrioritisedTask.class), - eq(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), eq(Priority.NORMAL))) - .thenAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - submissions.add("checkpoint"); - return null; - }); - io.when(() -> MoonriseRegionFileIO.scheduleSave(fixture.level, 4, -9, autosave, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)).thenAnswer(invocation -> { - submissions.add("autosave"); - return null; - }); - - NmsSavedTerrainCapture.CapturedChunk captured = NmsSavedTerrainCapture.scheduleSnapshot(fixture.level, chunk); - - assertEquals(List.of("checkpoint"), submissions); - assertFalse(captured.data().isDone()); - verify(snapshot, never()).write(); - verify(platform).chunkSyncSave(fixture.level, chunk, snapshot); - MoonriseRegionFileIO.scheduleSave(fixture.level, 4, -9, autosave, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA); - Future result = checkpoint.submit(() -> NmsSavedTerrainCapture.awaitSnapshot( - fixture.level, captured, System.nanoTime() + TimeUnit.SECONDS.toNanos(3))); - - assertSame(capturedData, result.get(5, TimeUnit.SECONDS)); - verify(snapshot).write(); - } - } - - @Test - public void failedOffOwnerSerializationCannotSucceedAsACheckpoint() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor(); - MockedStatic snapshots = mockStatic(SerializableChunkData.class); - MockedStatic hooks = mockStatic(PlatformHooks.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - ChunkAccess chunk = mock(ChunkAccess.class); - SerializableChunkData snapshot = mock(SerializableChunkData.class); - IllegalStateException failure = new IllegalStateException("serialization failed"); - when(chunk.getPos()).thenReturn(new ChunkPos(4, -9)); - snapshots.when(() -> SerializableChunkData.copyOf(fixture.level, chunk)).thenReturn(snapshot); - hooks.when(PlatformHooks::get).thenReturn(mock(PlatformHooks.class)); - when(snapshot.write()).thenThrow(failure); - NmsSavedTerrainCapture.CapturedChunk captured = NmsSavedTerrainCapture.scheduleSnapshot(fixture.level, chunk); - - Future result = checkpoint.submit(() -> assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitSnapshot(fixture.level, captured, - System.nanoTime() + TimeUnit.SECONDS.toNanos(3)))); - - assertSame(failure, result.get(5, TimeUnit.SECONDS).getCause()); - assertTrue(captured.data().isCompletedExceptionally()); - } - } - - @Test - public void checkpointBoundsOwnerRequestsUntilPriorNativeWritesFinish() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService owner = Executors.newSingleThreadExecutor(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor()) { - World world = mock(World.class); - when(world.getWorldFolder()).thenReturn(new File("checkpoint-world")); - List holders = new ArrayList<>(129); - for (int index = 0; index < 129; index++) { - NewChunkHolder holder = mock(NewChunkHolder.class); - setField(holder, "chunkX", index); - holders.add(holder); - } - AtomicInteger requested = new AtomicInteger(); - AtomicBoolean writesBlocked = new AtomicBoolean(true); - CountDownLatch firstBatchWaiting = new CountDownLatch(1); - Future result = checkpoint.submit(() -> { - try (MockedStatic scheduling = mockStatic(J.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - scheduling.when(() -> J.runRegionFuture(eq(world), anyInt(), anyInt(), any(Runnable.class))) - .thenAnswer(invocation -> { - requested.incrementAndGet(); - Runnable action = invocation.getArgument(3); - return CompletableFuture.runAsync(action, owner); - }); - io.when(() -> MoonriseRegionFileIO.getPriority(eq(fixture.level), anyInt(), anyInt(), - eq(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA))).thenAnswer(invocation -> { - firstBatchWaiting.countDown(); - return writesBlocked.get() ? Priority.NORMAL : Priority.COMPLETING; - }); - NmsSavedTerrainCapture.flushSnapshots(world, fixture.level, holders); - io.verify(() -> MoonriseRegionFileIO.getPriority(fixture.level, 128, 0, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), times(2)); - return null; - } - }); - try { - assertTrue(firstBatchWaiting.await(5, TimeUnit.SECONDS)); - assertEquals(64, requested.get()); - assertTrue(owner.submit(() -> true).get(5, TimeUnit.SECONDS)); - assertFalse(result.isDone()); - writesBlocked.set(false); - result.get(5, TimeUnit.SECONDS); - assertEquals(129, requested.get()); - } finally { - writesBlocked.set(false); - } - } - } - - private static void setField(Object instance, String name, Object value) throws Exception { - Field field = instance.getClass().getField(name); - field.setAccessible(true); - field.set(instance, value); - } - - private static final class SaveFixture implements AutoCloseable { - private final BalancedPrioritisedThreadPool pool = new BalancedPrioritisedThreadPool( - TimeUnit.MILLISECONDS.toNanos(1), Executors.defaultThreadFactory()); - private final ServerLevel level = mock(ServerLevel.class); - - private SaveFixture() throws Exception { - BalancedPrioritisedThreadPool.OrderedStreamGroup group = pool.createOrderedStreamGroup(); - ChunkTaskScheduler scheduler = mock(ChunkTaskScheduler.class); - setField(scheduler, "saveExecutor", group.createExecutor()); - setField(scheduler, "compressionExecutor", group.createExecutor()); - setField(scheduler, "chunkHolderManager", mock(ChunkHolderManager.class)); - when(level.moonrise$getChunkTaskScheduler()).thenReturn(scheduler); - } - - @Override - public void close() throws Exception { - pool.halt(true); - assertTrue(pool.joinInterruptable(5_000)); - } - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainWriteBarrierTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainWriteBarrierTest.java deleted file mode 100644 index 899ee2195..000000000 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsSavedTerrainWriteBarrierTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; - -import ca.spottedleaf.concurrentutil.executor.thread.BalancedPrioritisedThreadPool; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import net.minecraft.SharedConstants; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -public class NmsSavedTerrainWriteBarrierTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void checkpointHelpsSaveAndCompressionWhileNativeGenerationOccupiesTheWorker() throws Exception { - BalancedPrioritisedThreadPool pool = new BalancedPrioritisedThreadPool( - TimeUnit.MILLISECONDS.toNanos(1), Executors.defaultThreadFactory()); - BalancedPrioritisedThreadPool.OrderedStreamGroup group = pool.createOrderedStreamGroup(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue generation = group.createExecutor(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue saves = group.createExecutor(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue compression = group.createExecutor(); - ServerLevel level = level(saves, compression); - ChunkPos chunk = new ChunkPos(4, -9); - CountDownLatch generationEntered = new CountDownLatch(1); - CountDownLatch releaseGeneration = new CountDownLatch(1); - AtomicBoolean pendingWrite = new AtomicBoolean(true); - AtomicInteger serialized = new AtomicInteger(); - AtomicInteger compressed = new AtomicInteger(); - AtomicReference saveThread = new AtomicReference<>(); - AtomicReference compressionThread = new AtomicReference<>(); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, chunk.x(), chunk.z(), - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> pendingWrite.get() ? Priority.NORMAL : Priority.COMPLETING); - generation.queueTask(() -> { - generationEntered.countDown(); - try { - assertTrue(releaseGeneration.await(5, TimeUnit.SECONDS)); - } catch (InterruptedException interruption) { - Thread.currentThread().interrupt(); - throw new AssertionError(interruption); - } - }); - pool.adjustThreadCount(1); - assertTrue(generationEntered.await(5, TimeUnit.SECONDS)); - saves.queueTask(() -> { - saveThread.set(Thread.currentThread()); - serialized.incrementAndGet(); - compression.queueTask(() -> { - compressionThread.set(Thread.currentThread()); - compressed.incrementAndGet(); - pendingWrite.set(false); - }); - }); - - NmsSavedTerrainCapture.awaitWrite(level, chunk, System.nanoTime() + TimeUnit.SECONDS.toNanos(3)); - - assertEquals(1L, releaseGeneration.getCount()); - assertEquals(1, serialized.get()); - assertEquals(1, compressed.get()); - assertFalse(pendingWrite.get()); - assertSame(Thread.currentThread(), saveThread.get()); - assertSame(Thread.currentThread(), compressionThread.get()); - } finally { - releaseGeneration.countDown(); - pool.halt(true); - assertTrue(pool.joinInterruptable(5_000)); - } - } - - @Test - public void waitingForNativeWriteHonorsTheSharedDeadline() throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkPos chunk = new ChunkPos(2, 3); - IOException failure = assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitWrite(level, chunk, System.nanoTime() - 1L)); - assertTrue(failure.getMessage().contains("Timed out")); - } - - @Test - public void interruptedNativeWriteWaitPreservesInterruption() throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkPos chunk = new ChunkPos(2, 3); - Thread.currentThread().interrupt(); - try { - IOException failure = assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitWrite(level, chunk, - System.nanoTime() + TimeUnit.SECONDS.toNanos(3))); - assertTrue(failure.getMessage().contains("Interrupted")); - assertTrue(Thread.currentThread().isInterrupted()); - } finally { - Thread.interrupted(); - } - } - - private static ServerLevel level(BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue saves, - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue compression) throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkTaskScheduler scheduler = mock(ChunkTaskScheduler.class); - Field savesField = ChunkTaskScheduler.class.getField("saveExecutor"); - Field compressionField = ChunkTaskScheduler.class.getField("compressionExecutor"); - savesField.setAccessible(true); - compressionField.setAccessible(true); - savesField.set(scheduler, saves); - compressionField.set(scheduler, compression); - when(level.moonrise$getChunkTaskScheduler()).thenReturn(scheduler); - return level; - } -} diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/WorldgenTerrainHeightmapsTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/WorldgenTerrainHeightmapsTest.java index d37f9f51b..4485ce4e4 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/WorldgenTerrainHeightmapsTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/WorldgenTerrainHeightmapsTest.java @@ -1,6 +1,8 @@ package art.arcane.iris.platform.bukkit.nms.v26_2_R1; -import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps; +import art.arcane.volmlib.nativelib.v26_2_R1.terrain.NativeTerrainPipeline; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.WorldgenTerrainHeightmaps; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -68,7 +70,7 @@ public class WorldgenTerrainHeightmapsTest { public void authoringHeightmapsReadPlacedObjectsAndFluidsWithoutTerrainResolvers() { ProtoChunk chunk = terrainChunk(new ChunkPos(0, 0)); - IrisChunkGenerator.primeAuthoringHeightmaps(chunk); + NativeTerrainPipeline.primeAuthoringHeightmaps(chunk); assertEquals(LAND_CANOPY_TOP, chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, LAND_X, LAND_Z)); assertEquals(LAND_CANOPY_TOP, chunk.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, LAND_X, LAND_Z)); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java index 4e4f08681..62eff8f25 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java @@ -18,6 +18,7 @@ package art.arcane.iris.structure; +import art.arcane.volmlib.nativelib.common.structure.StructureReaderFixture; import art.arcane.iris.structure.authoring.StructureCapability; import art.arcane.iris.structure.authoring.StructureLoss; import net.minecraft.SharedConstants; @@ -33,7 +34,6 @@ import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class VillageImporterListPoolElementRuntimeContractTest { @@ -54,7 +54,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructureTemplatePool.Projection.RIGID ); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(towers); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(towers)); Map entry = VillageImporter.piecePoolEntry( "qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower", 1 @@ -85,7 +85,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of(Map.of("name", "minecraft:bottom"))); - assertSame(tower, resolution.physicalElement()); + assertEquals(tower.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("minecraft:pillager_outpost/watchtower", resolution.templateLocation()); assertEquals(1, resolution.omittedElements()); assertEquals("qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower", entry.get("piece")); @@ -103,7 +103,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement waystone = StructurePoolElement .legacy("dungeons_and_taverns:waystones/waystone_scaffold") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(waystone); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(waystone)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "dungeons_and_taverns:waystones", "qa/pool/dungeons_and_taverns/waystones", @@ -116,7 +116,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(waystone, resolution.physicalElement()); + assertEquals(waystone.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("dungeons_and_taverns:waystones/waystone_scaffold", resolution.templateLocation()); assertEquals(VillageImporter.PoolMemberDisposition.EMPTY, normalization.disposition()); assertEquals(true, normalization.poolEntry().get("empty")); @@ -130,7 +130,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement orphan = StructurePoolElement .legacy("test:orphan_house") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(orphan); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(orphan)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:orphan_pool", "qa/pool/test/orphan_pool", @@ -143,7 +143,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 37, List.of()); - assertSame(orphan, resolution.physicalElement()); + assertEquals(orphan.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("test:orphan_house", resolution.templateLocation()); assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition()); assertTrue(normalization.poolEntry().isEmpty()); @@ -160,7 +160,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement start = StructurePoolElement .legacy("test:air_start") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(start); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(start)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:start", "qa/pool/test/start", @@ -173,7 +173,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(start, resolution.physicalElement()); + assertEquals(start.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, normalization.disposition()); assertEquals("qa/piece/test/air_start", normalization.poolEntry().get("piece")); assertTrue(normalization.losses().isEmpty()); @@ -184,7 +184,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement inert = StructurePoolElement .legacy("test:mixed_air_inert") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(inert); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(inert)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:mixed", "qa/pool/test/mixed", @@ -197,7 +197,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(inert, resolution.physicalElement()); + assertEquals(inert.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition()); assertTrue(normalization.poolEntry().isEmpty()); assertEquals("connectorless_all_air_mixed_member_omitted", @@ -214,8 +214,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement nonAirElement = StructurePoolElement .legacy("test:mixed_non_air_primary") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement); - VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement); + VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(allAirElement)); + VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(nonAirElement)); VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember( "test:mixed", "qa/pool/test/mixed", @@ -239,8 +239,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { 41, List.of()); - assertSame(allAirElement, allAirResolution.physicalElement()); - assertSame(nonAirElement, nonAirResolution.physicalElement()); + assertEquals(allAirElement.getClass().getSimpleName(), allAirResolution.physicalElement().typeName()); + assertEquals(nonAirElement.getClass().getSimpleName(), nonAirResolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition()); assertEquals("qa/piece/test/mixed_all_air_primary", allAir.poolEntry().get("piece")); @@ -257,8 +257,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement nonAirElement = StructurePoolElement .legacy("test:non_air_primary") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement); - VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement); + VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(allAirElement)); + VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(nonAirElement)); VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember( "test:primary", "qa/pool/test/primary", @@ -282,8 +282,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { 29, List.of()); - assertSame(allAirElement, allAirResolution.physicalElement()); - assertSame(nonAirElement, nonAirResolution.physicalElement()); + assertEquals(allAirElement.getClass().getSimpleName(), allAirResolution.physicalElement().typeName()); + assertEquals(nonAirElement.getClass().getSimpleName(), nonAirResolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition()); assertEquals("qa/piece/test/all_air_primary", allAir.poolEntry().get("piece")); diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java similarity index 96% rename from adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java rename to adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java index 565ce0959..40b8d8c4a 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java @@ -1,8 +1,14 @@ -package art.arcane.iris.nativegen; +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.structure.nativegen.IrisStructureVolumePolicy; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVolumeSource; + +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; @@ -63,7 +69,7 @@ public class NativeStructureVolumeIndexTest { StructureStart start = swampHut(0, 0, 4, 6); BoundingBox bounds = start.getPieces().getFirst().getBoundingBox(); - KList volumes = NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, start); + KList volumes = NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, start); assertEquals(1, volumes.size()); NativeStructureVolume volume = volumes.getFirst(); @@ -78,8 +84,8 @@ public class NativeStructureVolumeIndexTest { @Test public void invalidStartsContributeNoVolumes() { - assertNull(NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, StructureStart.INVALID_START)); - assertNull(NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, null)); + assertNull(NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, StructureStart.INVALID_START)); + assertNull(NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, null)); } @Test @@ -174,9 +180,10 @@ public class NativeStructureVolumeIndexTest { listeners.remove(invocation.getArgument(0)); return null; }).when(engine).removeGenerationRuntimeRetirementListener(any()); - NativeStructureVolumeIndex.Context context = new NativeStructureVolumeIndex.Context( + NativeStructureVolumeSource.Context context = new NativeStructureVolumeSource.Context( null, null, null, null, () -> null, () -> null, () -> null); - NativeStructureVolumeIndex.install(engine, context); + NativeStructureVolumeIndex.install(engine, + new NativeStructureVolumeSource<>(context, IrisStructureVolumePolicy.INSTANCE)::volumesAt); try { assertEquals(1, listeners.size()); IntConsumer original = listeners.iterator().next(); @@ -501,7 +508,7 @@ public class NativeStructureVolumeIndexTest { if (chunkX != originChunkX || chunkZ != originChunkZ) { return NativeStructureVolume.NONE; } - return NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, swampHut(chunkX, chunkZ, 4, 6)); + return NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, swampHut(chunkX, chunkZ, 4, 6)); } } diff --git a/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java new file mode 100644 index 000000000..2a816a9aa --- /dev/null +++ b/adapters/bukkit/nms/v26_2_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java @@ -0,0 +1,12 @@ +package art.arcane.volmlib.nativelib.common.structure; + +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; + +public final class StructureReaderFixture { + private StructureReaderFixture() { + } + + public static NativeStructureReader.Element element(Object handle) { + return new ReflectiveStructureReader.ElementView(handle, null, ReflectiveStructureReader::readDefaultConnectors); + } +} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/ForcedStructureChunkGenerator.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/ForcedStructureChunkGenerator.java deleted file mode 100644 index 409b3eb40..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/ForcedStructureChunkGenerator.java +++ /dev/null @@ -1,83 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; -import java.util.Set; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.blending.Blender; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -final class ForcedStructureChunkGenerator extends ChunkGenerator { - private final ChunkGenerator delegate; - - ForcedStructureChunkGenerator(ChunkGenerator delegate, Holder sourceBiome) { - super(new FixedBiomeSource(sourceBiome)); - this.delegate = delegate; - } - - @Override - protected MapCodec codec() { - return MapCodec.unit(this); - } - - @Override - public void spawnOriginalMobs(WorldGenRegion worldGenRegion) { - delegate.spawnOriginalMobs(worldGenRegion); - } - - @Override - public int getGenDepth() { - return delegate.getGenDepth(); - } - - @Override - public CompletableFuture buildTerrain(ChunkAccess centerChunk, Blender blender, RandomState randomState, - StructureManager structureManager, BiomeManager biomeManager, - WorldGenRegion carverBiomeRegion, Set> possibleBiomes) { - return delegate.buildTerrain( - centerChunk, blender, randomState, structureManager, biomeManager, carverBiomeRegion, possibleBiomes); - } - - @Override - public int getSeaLevel() { - return delegate.getSeaLevel(); - } - - @Override - public int getMinY() { - return delegate.getMinY(); - } - - @Override - public int getBaseHeight(int x, int z, Heightmap.Types type, - LevelHeightAccessor heightAccessor, RandomState randomState) { - return delegate.getBaseHeight(x, z, type, heightAccessor, randomState); - } - - @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, - RandomState randomState) { - return delegate.getBaseColumn(x, z, heightAccessor, randomState); - } - - @Override - public void addDebugScreenInfo(List result, RandomState randomState, - BlockPos feetPos, SamplerContext samplerContext) { - delegate.addDebugScreenInfo(result, randomState, feetPos, samplerContext); - } - -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeBuildFutures.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeBuildFutures.java deleted file mode 100644 index 704ec9d53..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeBuildFutures.java +++ /dev/null @@ -1,31 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - -/** - * Shared waiter for the racy build caches in this package: the loser of a putIfAbsent race - * must surface the same exception the builder threw, not a CompletionException wrapper. - */ -final class NativeBuildFutures { - private NativeBuildFutures() { - } - - static T awaitBuild(CompletableFuture future, String what) { - try { - return future.join(); - } catch (CompletionException error) { - Throwable cause = error.getCause(); - if (cause instanceof RuntimeException runtime) { - throw runtime; - } - if (cause instanceof Error fatal) { - throw fatal; - } - if (cause == null) { - throw error; - } - throw new IllegalStateException(what + " failed", cause); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeGenerationWriteGuard.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeGenerationWriteGuard.java deleted file mode 100644 index 44e47bcd2..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeGenerationWriteGuard.java +++ /dev/null @@ -1,78 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.status.ChunkPyramid; -import net.minecraft.world.level.chunk.status.ChunkStatus; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; - -public final class NativeGenerationWriteGuard { - private NativeGenerationWriteGuard() { - } - - public static boolean allowsDecoration(Engine engine, WorldGenLevel region, ChunkPos center) { - int radius = ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FEATURES).blockStateWriteRadius(); - for (int offsetX = -radius; offsetX <= radius; offsetX++) { - for (int offsetZ = -radius; offsetZ <= radius; offsetZ++) { - int chunkX = Math.addExact(center.x(), offsetX); - int chunkZ = Math.addExact(center.z(), offsetZ); - ChunkAccess target = region.getChunk(chunkX, chunkZ); - if (target.getPersistedStatus().isOrAfter(ChunkStatus.FULL) - && !engine.getComplex().allowsMantleChunkWrite(chunkX, chunkZ)) { - return false; - } - } - } - return true; - } - - public static boolean isHistoricalStructure(Engine engine, long activation) { - if (activation <= 0 || !(engine instanceof IrisEngine irisEngine)) { - return false; - } - GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); - return router != null && activation < router.history().activeActivation().activationId(); - } - - public static boolean allowsPendingStage(Engine engine, ChunkAccess chunk, ChunkStatus stage) { - return stage.isOrAfter(ChunkStatus.FEATURES) - && chunk.getPersistedStatus().isOrAfter(ChunkStatus.TERRAIN) - && !chunk.getPersistedStatus().isOrAfter(stage) - && !engine.getComplex().allowsMantleChunkWrite(chunk.getPos().x(), chunk.getPos().z()); - } - - public static Predicate protectedPositions( - IrisStaticObjectLayer staticObjects, - DimensionStackContext stackContext, - int minimumY - ) { - Map layouts = new ConcurrentHashMap<>(); - return position -> { - if (!staticObjects.isEmpty() && staticObjects.contains( - position.getX(), position.getY() - minimumY, position.getZ())) { - return true; - } - if (stackContext == null) { - return false; - } - long columnKey = ((long) position.getX() << 32) - ^ (position.getZ() & 0xFFFFFFFFL); - DimensionStackLayout layout = layouts.computeIfAbsent( - columnKey, - ignored -> stackContext.sample(position.getX(), position.getZ()) - ); - return layout.isHostFeatureProtectedY(position.getY() - minimumY); - }; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFactory.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFactory.java deleted file mode 100644 index 562ee510a..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFactory.java +++ /dev/null @@ -1,286 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.jigsaw.IrisJigsawConfiguration; -import art.arcane.iris.structure.jigsaw.IrisJigsawHeightmap; -import art.arcane.iris.structure.jigsaw.IrisJigsawLiquidSettings; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.Locale; -import java.util.Objects; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; - -public final class NativeStructureFactory { - private NativeStructureFactory() { - } - - public static StructureStart generate(GenerationContext context, Holder sourceHolder, - NativeStructureStartPlan plan, int references) { - Structure source = sourceHolder.value(); - Structure configured = configure( - context.registryAccess(), source, plan.source().getJigsaw(), - plan.placement().isUnderground(), plan.baseY()); - Holder sourceBiome = source.biomes().stream().findFirst() - .or(() -> context.biomeSource().possibleBiomes().stream().findFirst()) - .orElseThrow(() -> new IllegalStateException("Configured native structure '" - + plan.source().getStructure() + "' has no usable generation biome")); - ChunkGenerator forcedGenerator = new ForcedStructureChunkGenerator( - context.generator(), sourceBiome); - StructureStart generated = configured.generate( - sourceHolder, - context.levelKey(), - context.registryAccess(), - forcedGenerator, - forcedGenerator.getBiomeSource(), - context.randomState().createClimateSampler(SamplerContext.EMPTY_UNCACHED), - context.randomState(), - context.templateManager(), - context.levelSeed(), - new ChunkPos(plan.chunkX(), plan.chunkZ()), - references, - context.heightAccessor(), - context.biomePredicate() - ); - if (generated == null || !generated.isValid()) { - return StructureStart.INVALID_START; - } - StructureStart positioned; - if (plan.placement().isUnderground()) { - positioned = NativeStructureVerticalPlacer.relocateToMinY( - generated, source, plan.baseY(), context.heightAccessor()); - } else { - NativeStructureVerticalPlacer.applyVerticalPlacement( - generated, - plan.source().getStructure(), - 0, - context.seaLevel(), - context.heightAccessor().getMinY(), - Math.addExact(context.heightAccessor().getMinY(), context.heightAccessor().getHeight()), - false, - false, - null, - context.surfaceHeight()); - positioned = generated; - } - if (!positioned.isValid()) { - return StructureStart.INVALID_START; - } - return NativeStructureReferenceEnvelope.wrapForPublication( - positioned, - source, - references, - plan.placement().resolvedTerrain(), - plan.source().getStructure() - ); - } - - public static JigsawSourceMetadata sourceMetadata(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source) { - CompoundTag sourceTag = encodeJigsaw(registryAccess, source); - return new JigsawSourceMetadata( - sourceDistance(sourceTag, "horizontal"), - horizontalReferenceExpansion(source), - NativeStructureTemplatePoolBounds.sourceHorizontalSpan( - registryAccess, templateManager, source)); - } - - public static int templatePoolHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - String templatePoolKey) { - return NativeStructureTemplatePoolBounds.horizontalSpan( - registryAccess, templateManager, templatePoolKey); - } - - public static int jigsawStartPoolHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source, - String templatePoolKey) { - return NativeStructureTemplatePoolBounds.horizontalSpan( - registryAccess, templateManager, source, templatePoolKey); - } - - static Structure configure(RegistryAccess registryAccess, Structure source, - IrisJigsawConfiguration configuration, - boolean underground, int baseY) { - Objects.requireNonNull(source, "Native structure source must not be null"); - if (!(source instanceof JigsawStructure sourceJigsaw)) { - if (configuration != null) { - throw new IllegalStateException("Jigsaw options require a registered jigsaw structure, found " - + source.getClass().getName()); - } - return source; - } - RegistryOps registryOps = RegistryOps.create(NbtOps.INSTANCE, registryAccess); - CompoundTag structureTag = encodeJigsaw(registryOps, sourceJigsaw); - CompoundTag configuredTag = structureTag.copy(); - if (underground) { - CompoundTag height = new CompoundTag(); - height.putInt("absolute", baseY); - configuredTag.put("start_height", height); - configuredTag.remove("project_start_to_heightmap"); - } - applyConfiguration(configuredTag, configuration); - Structure decoded = Structure.DIRECT_CODEC.parse(registryOps, configuredTag).getOrThrow(); - if (!(decoded instanceof JigsawStructure configured)) { - throw new IllegalStateException("Configured native jigsaw codec produced " - + decoded.getClass().getName()); - } - return configured; - } - - private static CompoundTag encodeJigsaw(RegistryAccess registryAccess, - JigsawStructure source) { - return encodeJigsaw(RegistryOps.create(NbtOps.INSTANCE, registryAccess), source); - } - - private static CompoundTag encodeJigsaw(RegistryOps registryOps, - JigsawStructure source) { - Tag encoded = Structure.DIRECT_CODEC.encodeStart(registryOps, source).getOrThrow(); - if (!(encoded instanceof CompoundTag structureTag)) { - throw new IllegalStateException("Native jigsaw codec did not produce a compound"); - } - return structureTag; - } - - private static void applyConfiguration(CompoundTag tag, IrisJigsawConfiguration configuration) { - if (configuration == null) { - return; - } - if (configuration.getStartPool() != null && !configuration.getStartPool().isBlank()) { - tag.putString("start_pool", configuration.getStartPool().trim()); - } - if (configuration.getStartJigsawName() != null - && !configuration.getStartJigsawName().isBlank()) { - if ("NONE".equalsIgnoreCase(configuration.getStartJigsawName().trim())) { - tag.remove("start_jigsaw_name"); - } else { - tag.putString("start_jigsaw_name", configuration.getStartJigsawName().trim()); - } - } - if (configuration.getMaxDepth() != null) { - tag.putInt("size", configuration.getMaxDepth()); - } - applyDistance(tag, configuration); - if (configuration.getUseExpansionHack() != null) { - tag.putBoolean("use_expansion_hack", configuration.getUseExpansionHack()); - } - applyHeightmap(tag, configuration.getProjectStartToHeightmap()); - applyDimensionPadding(tag, configuration); - applyLiquidSettings(tag, configuration.getLiquidSettings()); - } - - private static void applyDistance(CompoundTag tag, IrisJigsawConfiguration configuration) { - Integer horizontal = configuration.getMaxDistanceHorizontal(); - Integer vertical = configuration.getMaxDistanceVertical(); - if (horizontal == null && vertical == null) { - return; - } - int resolvedHorizontal = horizontal == null ? sourceDistance(tag, "horizontal") : horizontal; - int resolvedVertical = vertical == null ? sourceDistance(tag, "vertical") : vertical; - if (resolvedHorizontal == resolvedVertical) { - tag.putInt("max_distance_from_center", resolvedHorizontal); - return; - } - CompoundTag distance = new CompoundTag(); - distance.putInt("horizontal", resolvedHorizontal); - distance.putInt("vertical", resolvedVertical); - tag.put("max_distance_from_center", distance); - } - - static int sourceDistance(CompoundTag tag, String axis) { - Tag raw = tag.get("max_distance_from_center"); - if (raw instanceof CompoundTag compound) { - return compound.getIntOr(axis, 128); - } - return tag.getIntOr("max_distance_from_center", 128); - } - - static int horizontalReferenceExpansion(Structure structure) { - BoundingBox content = new BoundingBox(0, 0, 0, 0, 0, 0); - BoundingBox adjusted = structure.adjustBoundingBox(content); - int expansion = Math.max(content.minX() - adjusted.minX(), adjusted.maxX() - content.maxX()); - expansion = Math.max(expansion, content.minZ() - adjusted.minZ()); - return Math.max(0, Math.max(expansion, adjusted.maxZ() - content.maxZ())); - } - - private static void applyHeightmap(CompoundTag tag, IrisJigsawHeightmap heightmap) { - if (heightmap == null || heightmap == IrisJigsawHeightmap.SOURCE) { - return; - } - if (heightmap == IrisJigsawHeightmap.NONE) { - tag.remove("project_start_to_heightmap"); - return; - } - tag.putString("project_start_to_heightmap", heightmap.name()); - } - - private static void applyDimensionPadding(CompoundTag tag, IrisJigsawConfiguration configuration) { - Integer bottom = configuration.getDimensionPaddingBottom(); - Integer top = configuration.getDimensionPaddingTop(); - if (bottom == null && top == null) { - return; - } - int resolvedBottom = bottom == null ? sourcePadding(tag, "bottom") : bottom; - int resolvedTop = top == null ? sourcePadding(tag, "top") : top; - if (resolvedBottom == resolvedTop) { - tag.putInt("dimension_padding", resolvedBottom); - return; - } - CompoundTag padding = new CompoundTag(); - padding.putInt("bottom", resolvedBottom); - padding.putInt("top", resolvedTop); - tag.put("dimension_padding", padding); - } - - private static int sourcePadding(CompoundTag tag, String side) { - Tag raw = tag.get("dimension_padding"); - if (raw instanceof CompoundTag compound) { - return compound.getIntOr(side, 0); - } - return tag.getIntOr("dimension_padding", 0); - } - - private static void applyLiquidSettings(CompoundTag tag, IrisJigsawLiquidSettings settings) { - if (settings == null || settings == IrisJigsawLiquidSettings.SOURCE) { - return; - } - tag.putString("liquid_settings", settings.name().toLowerCase(Locale.ROOT)); - } - - public record GenerationContext( - RegistryAccess registryAccess, - ChunkGenerator generator, - BiomeSource biomeSource, - RandomState randomState, - StructureTemplateManager templateManager, - long levelSeed, - ResourceKey levelKey, - LevelHeightAccessor heightAccessor, - Predicate> biomePredicate, - int seaLevel, - IntBinaryOperator surfaceHeight - ) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFoundationBuilder.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFoundationBuilder.java deleted file mode 100644 index 3ca6bbd73..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureFoundationBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.placement.IrisStructureStiltSettings; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.SupportType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; -import java.util.Objects; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureFoundationBuilder { - private static final int FOUNDATION_VERTICAL_TOLERANCE = 1; - - private NativeStructureFoundationBuilder() { - } - - private static List foundationEnvelope(BoundingBox area, StructureStart start) { - BoundingBox structure = NativeStructureReferenceEnvelope.contentBounds(start); - int minX = Math.max(area.minX(), structure.minX()); - int minZ = Math.max(area.minZ(), structure.minZ()); - int maxX = Math.min(area.maxX(), structure.maxX()); - int maxZ = Math.min(area.maxZ(), structure.maxZ()); - if (minX > maxX || minZ > maxZ) { - return List.of(); - } - List pieces = start.getPieces(); - List columns = new ArrayList<>((maxX - minX + 1) * (maxZ - minZ + 1)); - BitSet envelope = new BitSet(area.getYSpan()); - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - envelope.clear(); - markFoundationEnvelope(envelope, pieces, area, x, z); - int cellCount = envelope.cardinality(); - if (cellCount == 0) { - continue; - } - int[] ys = new int[cellCount]; - int cell = 0; - for (int bit = envelope.nextSetBit(0); bit >= 0; bit = envelope.nextSetBit(bit + 1)) { - ys[cell++] = area.minY() + bit; - } - columns.add(new FoundationColumn(x, z, ys)); - } - } - return List.copyOf(columns); - } - - private static void markFoundationEnvelope(BitSet envelope, List pieces, BoundingBox area, - int x, int z) { - for (StructurePiece piece : pieces) { - BoundingBox bounds = piece.getBoundingBox(); - if (x < bounds.minX() || x > bounds.maxX() || z < bounds.minZ() || z > bounds.maxZ()) { - continue; - } - int groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - if (groundY < bounds.minY()) { - continue; - } - } - int minY = Math.max(area.minY(), bounds.minY()); - int maxY = Math.min(area.maxY(), Math.min(bounds.maxY(), groundY + FOUNDATION_VERTICAL_TOLERANCE)); - if (minY <= maxY) { - envelope.set(minY - area.minY(), maxY - area.minY() + 1); - } - } - } - - static void placeStilts(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureStiltSettings settings, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver, - IntBinaryOperator surfaceHeight, - boolean surfaceStructure) { - Objects.requireNonNull(surfaceHeight, "Structure stilts require an Iris terrain height resolver"); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - int structureHash = structureId == null ? 0 : structureId.hashCode(); - RNG rng = new RNG(world.getSeed() ^ structureHash); - for (FoundationColumn column : foundationEnvelope(area, start)) { - if (!isStiltColumn(column.x(), column.z(), settings.getSpacing())) { - continue; - } - int foundationY = findFoundationY(world, column, position); - if (foundationY == Integer.MIN_VALUE) { - continue; - } - int terrainY = surfaceStructure - ? Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(column.x(), column.z()))) - : area.minY() - 1; - int anchorY = findStiltAnchorY( - world, column.x(), column.z(), foundationY, - Math.max(1, settings.getMaxDepth()), terrainY, area.minY(), position); - if (anchorY == Integer.MIN_VALUE) { - continue; - } - for (int y = foundationY - 1; y > anchorY; y--) { - position.set(column.x(), y, column.z()); - BlockState stilt = settings.getPalette() == null - ? Blocks.COBBLESTONE.defaultBlockState() - : Objects.requireNonNull( - paletteBlockResolver.resolve( - settings.getPalette(), rng, column.x(), y, column.z()), - "Stilt palette returned no block for " + structureId + " at " - + column.x() + "," + y + "," + column.z()); - world.setBlock(position, stilt, 2); - } - } - } - - static boolean isStiltColumn(int x, int z, int spacing) { - int resolvedSpacing = Math.max(1, spacing); - return resolvedSpacing == 1 - || Math.floorMod(x, resolvedSpacing) == 0 - && Math.floorMod(z, resolvedSpacing) == 0; - } - - static int findStiltAnchorY( - WorldGenLevel world, int x, int z, int foundationY, int maxDepth, - int terrainY, int areaMinY, BlockPos.MutableBlockPos position) { - int minimumAnchorY = Math.max( - areaMinY, Math.max(terrainY, foundationY - maxDepth - 1)); - for (int y = foundationY - 1; y >= minimumAnchorY; y--) { - BlockState state = world.getBlockState(position.set(x, y, z)); - boolean vegetation = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - if (!vegetation && state.isFaceSturdy( - world, position, Direction.UP, SupportType.FULL)) { - return y; - } - } - return Integer.MIN_VALUE; - } - - public static StiltSupportAudit auditStiltSupport(WorldGenLevel world, BoundingBox area, - StructureStart start, BlockState expectedStilt, - IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(expectedStilt, "Expected stilt state must not be null"); - Objects.requireNonNull(surfaceHeight, "Structure stilt audit requires an Iris terrain height resolver"); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - int baseColumns = 0; - int stiltBlocks = 0; - int stiltColumns = 0; - int unsupportedColumns = 0; - for (FoundationColumn column : foundationEnvelope(area, start)) { - int foundationY = findFoundationY(world, column, position); - if (foundationY == Integer.MIN_VALUE) { - continue; - } - baseColumns++; - int terrainY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(column.x(), column.z()))); - boolean grounded = foundationY <= terrainY + 1; - boolean stiltColumn = false; - for (int y = foundationY - 1; y >= area.minY(); y--) { - if (y <= terrainY) { - grounded = true; - break; - } - BlockState state = world.getBlockState(position.set(column.x(), y, column.z())); - if (state.is(expectedStilt.getBlock())) { - stiltBlocks++; - stiltColumn = true; - if (y == area.minY()) { - grounded = true; - } - continue; - } - boolean vegetation = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - grounded = state.isSolid() && !vegetation; - break; - } - if (stiltColumn) { - stiltColumns++; - } - if (!grounded) { - unsupportedColumns++; - } - } - return new StiltSupportAudit(baseColumns, stiltBlocks, stiltColumns, unsupportedColumns); - } - - private static int findFoundationY(WorldGenLevel world, FoundationColumn column, - BlockPos.MutableBlockPos position) { - for (int cell = 0; cell < column.ys().length; cell++) { - int y = column.ys()[cell]; - BlockState state = world.getBlockState(position.set(column.x(), y, column.z())); - if (state.isSolid()) { - return y; - } - } - return Integer.MIN_VALUE; - } - - private record FoundationColumn(int x, int z, int[] ys) { - } - - public record StiltSupportAudit(int baseColumns, int stiltBlocks, int stiltColumns, - int unsupportedColumns) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationException.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationException.java deleted file mode 100644 index db470e80e..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationException.java +++ /dev/null @@ -1,35 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -public final class NativeStructureGenerationException extends IllegalStateException { - private NativeStructureGenerationException(FailureContext context, Throwable cause) { - super(message(context), cause); - } - - private NativeStructureGenerationException(FailureContext context) { - super(message(context)); - } - - public static NativeStructureGenerationException failure(String operation, String structureContext, - int chunkX, int chunkZ, Throwable cause) { - return new NativeStructureGenerationException( - new FailureContext(operation, structureContext, chunkX, chunkZ), cause); - } - - public static NativeStructureGenerationException failure(String operation, String structureContext, - int chunkX, int chunkZ) { - return new NativeStructureGenerationException( - new FailureContext(operation, structureContext, chunkX, chunkZ)); - } - - private static String message(FailureContext context) { - String resolvedContext = context.structureContext() == null || context.structureContext().isBlank() - ? "" - : context.structureContext(); - return "Iris native structure " + context.operation() + " failed for " + resolvedContext - + " in chunk " + context.chunkX() + "," + context.chunkZ() - + "; chunk generation was aborted to prevent partial structure state"; - } - - private record FailureContext(String operation, String structureContext, int chunkX, int chunkZ) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationKeys.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationKeys.java deleted file mode 100644 index 3afcbfe63..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureGenerationKeys.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -public final class NativeStructureGenerationKeys { - private static final Method FREQUENCY_METHOD = resolveFrequencyMethod(); - - private NativeStructureGenerationKeys() { - } - - public static Set reachable(ServerLevel level, Set possibleBiomeKeys) { - ServerLevel activeLevel = Objects.requireNonNull(level, "Native structure reachability requires a level"); - Set possibleBiomes = Objects.requireNonNull( - possibleBiomeKeys, "Native structure reachability requires possible biome keys"); - if (!activeLevel.getServer().getWorldGenSettings().options().generateStructures()) { - return Collections.emptySet(); - } - - Set keys = new LinkedHashSet<>(); - List> structureSets = - activeLevel.getChunkSource().getGeneratorState().possibleStructureSets(); - for (Holder structureSetHolder : structureSets) { - StructureSet structureSet = structureSetHolder.value(); - if (!isEnabledPlacement(structureSet.placement())) { - continue; - } - for (StructureSet.StructureSelectionEntry entry : structureSet.structures()) { - if (entry.weight() <= 0 || !hasPossibleBiome(entry.structure().value(), possibleBiomes)) { - continue; - } - Optional> key = entry.structure().unwrapKey(); - key.ifPresent(resourceKey -> keys.add(resourceKey.identifier().toString())); - } - } - return Collections.unmodifiableSet(keys); - } - - static boolean hasPossibleBiome(Structure structure, Set possibleBiomeKeys) { - for (Holder holder : structure.biomes()) { - Optional> key = holder.unwrapKey(); - if (key.isPresent() && possibleBiomeKeys.contains(key.get().identifier().toString())) { - return true; - } - } - return false; - } - - static boolean isEnabledPlacement(StructurePlacement placement) { - return placementFrequency(placement) > 0.0F; - } - - private static float placementFrequency(StructurePlacement placement) { - StructurePlacement activePlacement = Objects.requireNonNull( - placement, "Native structure placement frequency requires a placement"); - if (!(activePlacement instanceof AbstractSpreadingStructurePlacement)) { - return 1F; - } - try { - return ((Float) FREQUENCY_METHOD.invoke(activePlacement)).floatValue(); - } catch (ReflectiveOperationException error) { - throw new IllegalStateException( - "Unable to read native structure placement frequency from " - + activePlacement.getClass().getName(), error); - } - } - - private static Method resolveFrequencyMethod() { - Method frequencyMethod = null; - for (Method method : AbstractSpreadingStructurePlacement.class.getDeclaredMethods()) { - if (Modifier.isStatic(method.getModifiers()) - || method.getParameterCount() != 0 - || method.getReturnType() != float.class) { - continue; - } - if (frequencyMethod != null) { - throw new IllegalStateException( - "Native structure placement exposes multiple zero-argument float accessors"); - } - frequencyMethod = method; - } - if (frequencyMethod == null || !frequencyMethod.trySetAccessible()) { - throw new IllegalStateException( - "Native structure placement frequency accessor is unavailable"); - } - return frequencyMethod; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocatePersistence.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocatePersistence.java deleted file mode 100644 index fd22b553a..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocatePersistence.java +++ /dev/null @@ -1,351 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.placement.IrisStructureLocator; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.SharedConstants; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.StreamTagVisitor; -import net.minecraft.nbt.Tag; -import net.minecraft.nbt.visitors.CollectFields; -import net.minecraft.nbt.visitors.FieldSelector; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.datafix.DataFixTypes; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SimpleRegionStorage; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -public final class NativeStructureLocatePersistence { - public static final int MAX_SELECTED_CANDIDATE_RETRIES = 64; - private static final int MAX_STORAGE_PROBES = 512; - private static final Method PAPER_LEVEL_TYPE_KEY = paperLevelTypeKey(); - - private NativeStructureLocatePersistence() { - } - - public static Probe probe(ServerLevel level, Structure structure, boolean requireUnreferenced) { - return probe(level, structure, requireUnreferenced, new ProbeBudget(MAX_STORAGE_PROBES)); - } - - public static ProbeBudget probeBudget() { - return new ProbeBudget(MAX_STORAGE_PROBES); - } - - public static Probe probe(ServerLevel level, Structure structure, boolean requireUnreferenced, - ProbeBudget budget) { - return new Probe(level, structure, requireUnreferenced, budget); - } - - public static Search search(Engine engine, String structureKey, - int blockX, int blockZ, int radius, Probe probe) { - return new Search(engine, structureKey, blockX, blockZ, radius, probe); - } - - public static final class Probe { - private final ServerLevel level; - private final Structure structure; - private final String structureKey; - private final boolean requireUnreferenced; - private final ProbeBudget budget; - private final Map storedDecisions = new HashMap<>(); - - Probe(ServerLevel level, Structure structure, boolean requireUnreferenced, - ProbeBudget budget) { - this.level = level; - this.structure = structure; - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Identifier identifier = registry.getKey(structure); - if (identifier == null) { - throw new IllegalStateException("Cannot probe an unregistered native structure"); - } - this.structureKey = identifier.toString(); - this.requireUnreferenced = requireUnreferenced; - this.budget = budget; - } - - public boolean accepts(int chunkX, int chunkZ) { - ChunkAccess loaded = level.getChunkSource().getChunk( - chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false); - if (loaded != null) { - StructureStart start = loaded.getStartForStructure(structure); - return start != null && start.isValid() - && (!requireUnreferenced || start.canBeReferenced()); - } - - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - Boolean storedDecision = storedDecisions.get(chunkPos.pack()); - if (storedDecision != null) { - return storedDecision; - } - boolean accepted = budget.acceptsStored( - level, chunkPos, structureKey, requireUnreferenced); - storedDecisions.put(chunkPos.pack(), accepted); - return accepted; - } - - public StructureStart verifySelected(int chunkX, int chunkZ) { - ChunkAccess chunk = level.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS); - StructureStart start = chunk.getStartForStructure(structure); - if (start == null || !start.isValid()) { - return null; - } - if (requireUnreferenced) { - if (!start.canBeReferenced()) { - return null; - } - } - return start; - } - - private void reference(StructureStart start) { - if (requireUnreferenced) { - level.structureManager().addReference(start); - } - } - } - - public static final class ProbeBudget { - private final int maximum; - private final Map storedChunks = new HashMap<>(); - private final ChunkStorageScanner scanner; - private final StoredChunkDatafixer datafixer; - private int used; - private boolean scanFailureReported; - - private ProbeBudget(int maximum) { - this(maximum, null, ProbeBudget::datafix); - } - - ProbeBudget(int maximum, ChunkStorageScanner scanner) { - this(maximum, scanner, ProbeBudget::datafix); - } - - ProbeBudget(int maximum, ChunkStorageScanner scanner, - StoredChunkDatafixer datafixer) { - this.maximum = maximum; - this.scanner = scanner; - this.datafixer = Objects.requireNonNull( - datafixer, "Stored chunk datafixer must not be null"); - } - - private void claim() { - if (used >= maximum) { - throw new IrisStructureLocator.CandidateSearchLimitException(); - } - used++; - } - - private StoredChunkState storedState(ServerLevel level, ChunkPos chunkPos) { - StoredChunkState cached = storedChunks.get(chunkPos.pack()); - if (cached != null) { - return cached; - } - claim(); - CollectFields fields = new CollectFields( - new FieldSelector(IntTag.TYPE, "DataVersion"), - new FieldSelector("Level", "Structures", CompoundTag.TYPE, "Starts"), - new FieldSelector("structures", CompoundTag.TYPE, "starts")); - ChunkStorageScanner activeScanner = scanner == null - ? level.getChunkSource().chunkMap.chunkScanner()::scanChunk : scanner; - try { - activeScanner.scan(chunkPos, fields).join(); - } catch (RuntimeException error) { - if (!scanFailureReported) { - scanFailureReported = true; - IrisLogging.reportError( - "Native structure locate could not scan stored chunk state; candidates will be verified by loading their structure starts.", - error); - } - StoredChunkState unresolved = StoredChunkState.missing(); - storedChunks.put(chunkPos.pack(), unresolved); - return unresolved; - } - Tag result = fields.getResult(); - StoredChunkState resolved; - try { - resolved = result instanceof CompoundTag storedChunk - ? StoredChunkState.parse(datafixer.datafix(level, storedChunk)) - : StoredChunkState.missing(); - } catch (RuntimeException error) { - if (!scanFailureReported) { - scanFailureReported = true; - IrisLogging.reportError( - "Native structure locate could not datafix stored chunk state; candidates will be verified by loading their structure starts.", - error); - } - resolved = StoredChunkState.missing(); - } - storedChunks.put(chunkPos.pack(), resolved); - return resolved; - } - - private static CompoundTag datafix(ServerLevel level, CompoundTag storedChunk) { - int dataVersion = NbtUtils.getDataVersion(storedChunk); - int currentDataVersion = SharedConstants.getCurrentVersion().dataVersion().version(); - if (dataVersion >= currentDataVersion) { - return storedChunk; - } - if (level == null) { - throw new IllegalStateException("Stored chunk datafix requires an active level"); - } - CompoundTag context = chunkDataFixContext(level); - SimpleRegionStorage.injectDatafixingContext(storedChunk, context); - return DataFixTypes.CHUNK.updateToCurrentVersion( - level.getServer().getFixerUpper(), storedChunk, dataVersion); - } - - private static CompoundTag chunkDataFixContext(ServerLevel level) { - CompoundTag context = new CompoundTag(); - String levelIdentifier = level.dimension().identifier().toString(); - if (PAPER_LEVEL_TYPE_KEY == null) { - context.putString("dimension", levelIdentifier); - } else { - context.putString("dimension", paperLevelTypeKey(level).identifier().toString()); - context.putString("level_identifier", levelIdentifier); - } - level.getChunkSource().getGenerator().getTypeNameForDataFixer() - .ifPresent(identifier -> context.putString("generator", identifier.toString())); - return context; - } - - boolean acceptsStored(ServerLevel level, ChunkPos chunkPos, - String structureKey, boolean requireUnreferenced) { - return storedState(level, chunkPos).accepts(structureKey, requireUnreferenced); - } - - int used() { - return used; - } - } - - public static final class Search { - private final Engine engine; - private final String structureKey; - private final int blockX; - private final int blockZ; - private final int radius; - private final Probe probe; - private final Set rejectedChunks = new HashSet<>(); - - private Search(Engine engine, String structureKey, int blockX, int blockZ, - int radius, Probe probe) { - this.engine = engine; - this.structureKey = structureKey; - this.blockX = blockX; - this.blockZ = blockZ; - this.radius = radius; - this.probe = probe; - } - - public IrisStructureLocator.LocateResult predict() { - if (rejectedChunks.size() >= MAX_SELECTED_CANDIDATE_RETRIES) { - return new IrisStructureLocator.LocateResult( - IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED, 0, 0, 0); - } - return IrisStructureLocator.locateInPlacementRings( - engine, structureKey, blockX, blockZ, radius, - (chunkX, chunkZ) -> !rejectedChunks.contains(ChunkPos.pack(chunkX, chunkZ)) - && probe.accepts(chunkX, chunkZ)); - } - - public VerifiedStart verify(IrisStructureLocator.LocateResult result) { - int chunkX = result.originX() >> 4; - int chunkZ = result.originZ() >> 4; - StructureStart start = probe.verifySelected(chunkX, chunkZ); - if (start == null) { - return null; - } - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecovery.resolve( - engine, probe.level, structureKey, probe.structure, start); - if (ownership == null) { - return null; - } - return new VerifiedStart(start, ownership); - } - - public void reject(IrisStructureLocator.LocateResult result) { - rejectedChunks.add(ChunkPos.pack(result.originX() >> 4, result.originZ() >> 4)); - } - - public void reference(VerifiedStart verified) { - probe.reference(verified.start()); - } - } - - private static Method paperLevelTypeKey() { - try { - return ServerLevel.class.getMethod("getTypeKey"); - } catch (NoSuchMethodException ignored) { - return null; - } - } - - private static ResourceKey paperLevelTypeKey(ServerLevel level) { - try { - Object value = PAPER_LEVEL_TYPE_KEY.invoke(level); - if (value instanceof ResourceKey key) { - return key; - } - throw new IllegalStateException("Paper level type key has an unexpected value"); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot read Paper level type key", error); - } - } - - public record VerifiedStart(StructureStart start, - NativeStructureOwnershipRecord ownership) { - } - - @FunctionalInterface - interface ChunkStorageScanner { - CompletableFuture scan(ChunkPos chunkPos, StreamTagVisitor visitor); - } - - @FunctionalInterface - interface StoredChunkDatafixer { - CompoundTag datafix(ServerLevel level, CompoundTag storedChunk); - } - - private record StoredChunkState(boolean stored, boolean startsPresent, CompoundTag starts) { - private static StoredChunkState missing() { - return new StoredChunkState(false, false, new CompoundTag()); - } - - private static StoredChunkState parse(CompoundTag chunk) { - CompoundTag structures = chunk.getCompoundOrEmpty("structures"); - return structures.getCompound("starts") - .map(starts -> new StoredChunkState(true, true, starts)) - .orElseGet(() -> new StoredChunkState(true, false, new CompoundTag())); - } - - private boolean accepts(String structureKey, boolean requireUnreferenced) { - if (!stored || !startsPresent) { - return true; - } - return starts.getCompound(structureKey).map(start -> { - String id = start.getStringOr("id", ""); - return !StructureStart.INVALID_START_ID.equals(id) - && (!requireUnreferenced || start.getIntOr("references", 0) == 0); - }).orElse(false); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocateResults.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocateResults.java deleted file mode 100644 index 7ee4adb22..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureLocateResults.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.nativegen.v26_3_R1; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; - -public final class NativeStructureLocateResults { - private NativeStructureLocateResults() { - } - - public static Pair nearest(BlockPos origin, Pair replacement, - Pair nativeResult) { - if (replacement == null) { - return nativeResult; - } - if (nativeResult == null) { - return replacement; - } - return horizontalDistanceSquared(origin, nativeResult.getFirst()) - <= horizontalDistanceSquared(origin, replacement.getFirst()) ? nativeResult : replacement; - } - - public static Pair selectAndReference( - BlockPos origin, - Pair replacement, Runnable replacementReference, - Pair nativeResult, Runnable nativeReference) { - Pair selected = nearest(origin, replacement, nativeResult); - if (selected == replacement && replacement != null) { - replacementReference.run(); - } else if (selected == nativeResult && nativeResult != null) { - nativeReference.run(); - } - return selected; - } - - private static long horizontalDistanceSquared(BlockPos origin, BlockPos target) { - long dx = (long) target.getX() - origin.getX(); - long dz = (long) target.getZ() - origin.getZ(); - return dx * dx + dz * dz; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipFingerprint.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipFingerprint.java deleted file mode 100644 index cbba9630b..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipFingerprint.java +++ /dev/null @@ -1,239 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.placement.StructurePlacementGrid; -import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HexFormat; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -public final class NativeStructureOwnershipFingerprint { - private static final Comparator PIECE_ORDER = Comparator - .comparing(PieceIdentity::type) - .thenComparingInt(PieceIdentity::minX) - .thenComparingInt(PieceIdentity::minY) - .thenComparingInt(PieceIdentity::minZ) - .thenComparingInt(PieceIdentity::maxX) - .thenComparingInt(PieceIdentity::maxY) - .thenComparingInt(PieceIdentity::maxZ) - .thenComparingInt(PieceIdentity::orientation) - .thenComparingInt(PieceIdentity::generationDepth) - .thenComparing(PieceIdentity::detail); - - private NativeStructureOwnershipFingerprint() { - } - - public static NativeStructureOwnershipRecord capture(String structureKey, - StructureStart start, - NativeStructureStartPlan plan, - BoundingBox referenceEnvelope) { - StructureStart resolvedStart = requireValid(start); - NativeStructureStartPlan resolvedPlan = Objects.requireNonNull( - plan, "Native structure start plan must not be null"); - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(resolvedStart); - BoundingBox references = Objects.requireNonNull( - referenceEnvelope, "Native structure reference envelope must not be null"); - ChunkPos origin = resolvedStart.getChunkPos(); - return NativeStructureOwnershipRecord.create( - structureKey, - origin.x(), - origin.z(), - StructurePlacementGrid.placementIdentity(resolvedPlan.placement()), - resolvedPlan.baseY(), - content.minX(), - content.minY(), - content.minZ(), - content.maxX(), - content.maxY(), - content.maxZ(), - locatorY(resolvedStart), - references.minX() >> 4, - references.maxX() >> 4, - references.minZ() >> 4, - references.maxZ() >> 4, - fingerprint(structureKey, resolvedStart), - NativeStructurePlacementPlanner.decisionFor(resolvedPlan) - ); - } - - public static boolean matches(NativeStructureOwnershipRecord record, StructureStart start) { - if (record == null || start == null || !start.isValid()) { - return false; - } - ChunkPos origin = start.getChunkPos(); - if (origin.x() != record.originChunkX() - || origin.z() != record.originChunkZ() - || !fingerprint(record.structureKey(), start).equals(record.contentFingerprint())) { - return false; - } - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(start); - if (content.getXSpan() != (record.contentMaxX() - record.contentMinX()) + 1 - || content.getYSpan() != (record.contentMaxY() - record.contentMinY()) + 1 - || content.getZSpan() != (record.contentMaxZ() - record.contentMinZ()) + 1) { - return false; - } - return start.getStructure() instanceof OceanMonumentStructure - || content.minX() == record.contentMinX() - && content.minY() == record.contentMinY() - && content.minZ() == record.contentMinZ() - && content.maxX() == record.contentMaxX() - && content.maxY() == record.contentMaxY() - && content.maxZ() == record.contentMaxZ(); - } - - public static String fingerprint(String structureKey, StructureStart start) { - StructureStart resolvedStart = requireValid(start); - MessageDigest digest = sha256(); - updateString(digest, Objects.requireNonNull(structureKey, - "Native structure key must not be null").trim().toLowerCase(Locale.ROOT)); - updateInt(digest, resolvedStart.getChunkPos().x()); - updateInt(digest, resolvedStart.getChunkPos().z()); - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(resolvedStart); - List pieces = contentPieces(resolvedStart, content.minY()); - updateInt(digest, pieces.size()); - for (PieceIdentity piece : pieces) { - updateString(digest, piece.type()); - updateInt(digest, piece.minX()); - updateInt(digest, piece.minY()); - updateInt(digest, piece.minZ()); - updateInt(digest, piece.maxX()); - updateInt(digest, piece.maxY()); - updateInt(digest, piece.maxZ()); - updateInt(digest, piece.orientation()); - updateInt(digest, piece.generationDepth()); - updateString(digest, piece.detail()); - } - return HexFormat.of().formatHex(digest.digest()); - } - - public static int locatorY(StructureStart start) { - StructureStart resolvedStart = requireValid(start); - if (!resolvedStart.getPieces().isEmpty()) { - return resolvedStart.getPieces().getFirst().getLocatorPosition().getY(); - } - throw new IllegalStateException("Native structure contains no locatable content pieces"); - } - - private static List contentPieces(StructureStart start, int contentMinY) { - List pieces = new ArrayList<>(); - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - Identifier type = BuiltInRegistries.STRUCTURE_PIECE.getKey(piece.getType()); - Direction orientation = piece.getOrientation(); - String detail = pieceDetail(piece, contentMinY); - pieces.add(new PieceIdentity( - type == null ? "" : type.toString(), - bounds.minX(), bounds.minY() - contentMinY, bounds.minZ(), - bounds.maxX(), bounds.maxY() - contentMinY, bounds.maxZ(), - orientation == null ? -1 : orientation.get2DDataValue(), - piece.getGenDepth(), - detail - )); - } - if (pieces.isEmpty()) { - throw new IllegalStateException("Native structure contains no content pieces"); - } - pieces.sort(PIECE_ORDER); - return List.copyOf(pieces); - } - - private static String pieceDetail(StructurePiece piece, int contentMinY) { - if (!(piece instanceof PoolElementStructurePiece poolPiece)) { - return ""; - } - return poolElementIdentity(poolPiece.getElement()) + "|" - + poolPiece.getPosition().getX() + "," - + (poolPiece.getPosition().getY() - contentMinY) + "," - + poolPiece.getPosition().getZ() + "|" - + poolPiece.getGroundLevelDelta() + "|" - + poolPiece.getRotation().getSerializedName(); - } - - private static String poolElementIdentity(StructurePoolElement element) { - Identifier type = BuiltInRegistries.STRUCTURE_POOL_ELEMENT.getKey(element.getType()); - StringBuilder identity = new StringBuilder(element.getClass().getName()) - .append('|') - .append(type == null ? "" : type) - .append('|') - .append(element.getProjection().getSerializedName()) - .append('|') - .append(element.getGroundLevelDelta()); - if (element == EmptyPoolElement.INSTANCE) { - return identity.append("|empty").toString(); - } - if (element instanceof SinglePoolElement single) { - try { - return identity.append("|template=") - .append(single.getTemplateLocation()).toString(); - } catch (RuntimeException inlineTemplate) { - return identity.append("|runtime-template").toString(); - } - } - if (element instanceof ListPoolElement list) { - identity.append("|list["); - for (StructurePoolElement child : list.getElements()) { - String childIdentity = poolElementIdentity(child); - identity.append(childIdentity.length()).append(':').append(childIdentity); - } - return identity.append(']').toString(); - } - return identity.append("|bounded-custom").toString(); - } - - private static StructureStart requireValid(StructureStart start) { - StructureStart resolved = Objects.requireNonNull( - start, "Native structure start must not be null"); - if (!resolved.isValid()) { - throw new IllegalArgumentException("Native structure start must be valid"); - } - return resolved; - } - - private static MessageDigest sha256() { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException error) { - throw new IllegalStateException("SHA-256 is unavailable", error); - } - } - - private static void updateString(MessageDigest digest, String value) { - byte[] encoded = value.getBytes(StandardCharsets.UTF_8); - updateInt(digest, encoded.length); - digest.update(encoded); - } - - private static void updateInt(MessageDigest digest, int value) { - digest.update((byte) (value >>> 24)); - digest.update((byte) (value >>> 16)); - digest.update((byte) (value >>> 8)); - digest.update((byte) value); - } - - private record PieceIdentity(String type, - int minX, int minY, int minZ, - int maxX, int maxY, int maxZ, - int orientation, int generationDepth, String detail) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipRecovery.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipRecovery.java deleted file mode 100644 index c237a7e1f..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureOwnershipRecovery.java +++ /dev/null @@ -1,195 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.placement.StructurePlacementGrid; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.structure.nativegen.NativeStructureSuppression; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.util.Locale; -import java.util.Objects; - -public final class NativeStructureOwnershipRecovery { - private NativeStructureOwnershipRecovery() { - } - - public static NativeStructureOwnershipRecord resolve( - Engine engine, ServerLevel level, String structureKey, - Structure structure, StructureStart start) { - Objects.requireNonNull(engine, "Native structure ownership recovery requires an engine"); - ServerLevel activeLevel = Objects.requireNonNull( - level, "Native structure ownership recovery requires a level"); - Structure activeStructure = Objects.requireNonNull( - structure, "Native structure ownership recovery requires a structure"); - if (start == null || !start.isValid() || start.getStructure() != activeStructure) { - return null; - } - ChunkPos origin = start.getChunkPos(); - NativeStructureOwnershipRecord persisted = NativeStructureOwnershipStore.findPersisted( - engine, structureKey, origin.x(), origin.z()); - if (persisted != null) { - NativeStructureOwnershipRecord refreshed = refreshReferenceEnvelope( - structureKey, activeStructure, start, persisted); - if (refreshed != null) { - if (refreshed != persisted) { - NativeStructureOwnershipStore.record(engine, refreshed); - } - return refreshed; - } - NativeStructureOwnershipStore.discard( - engine, structureKey, origin.x(), origin.z()); - } - NativeStructureStartPlan plan = NativeStructurePlacementPlanner.matchingPlan( - engine, structureKey, origin.x(), origin.z()); - if (!matchesPlan(structureKey, origin, plan)) { - return null; - } - Registry registry = activeLevel.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Holder holder = registry.wrapAsHolder(activeStructure); - ChunkGeneratorStructureState state = activeLevel.getChunkSource().getGeneratorState(); - if (naturalStartIsAmbiguous(engine, structureKey, activeStructure, holder, state, plan)) { - return null; - } - StructureStart expected = generateExpected( - engine, activeLevel, holder, plan, start.getReferences()); - NativeStructureOwnershipRecord recovered = proveCandidate( - structureKey, activeStructure, start, plan, expected, false); - if (recovered == null) { - return null; - } - NativeStructureOwnershipStore.record(engine, recovered); - return recovered; - } - - static NativeStructureOwnershipRecord refreshReferenceEnvelope( - String structureKey, Structure structure, StructureStart start, - NativeStructureOwnershipRecord ownership) { - if (structure == null || start == null || !start.isValid() - || start.getStructure() != structure || ownership == null - || !ownership.structureKey().equals(normalize(structureKey)) - || !NativeStructureOwnershipFingerprint.matches(ownership, start)) { - return null; - } - IrisStructureTerrain terrain = NativeStructureTerrainIntegrator.resolveNativeTerrain( - start, ownership.restoredDecision().terrain()); - if (terrain.resolvedMode() != IrisStructureTerrainMode.VACUUM) { - return ownership; - } - BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds( - start, structure, terrain, structureKey); - int referenceMinChunkX = expected.minX() >> 4; - int referenceMaxChunkX = expected.maxX() >> 4; - int referenceMinChunkZ = expected.minZ() >> 4; - int referenceMaxChunkZ = expected.maxZ() >> 4; - if (ownership.referenceMinChunkX() == referenceMinChunkX - && ownership.referenceMaxChunkX() == referenceMaxChunkX - && ownership.referenceMinChunkZ() == referenceMinChunkZ - && ownership.referenceMaxChunkZ() == referenceMaxChunkZ) { - return ownership; - } - return ownership.withReferenceEnvelope( - referenceMinChunkX, referenceMaxChunkX, - referenceMinChunkZ, referenceMaxChunkZ); - } - - static NativeStructureOwnershipRecord proveCandidate( - String structureKey, Structure structure, StructureStart persisted, - NativeStructureStartPlan plan, StructureStart expected, - boolean naturalStartAmbiguous) { - if (naturalStartAmbiguous || structure == null - || persisted == null || !persisted.isValid() - || expected == null || !expected.isValid() - || persisted.getStructure() != structure - || expected.getStructure() != structure - || !matchesPlan(structureKey, persisted.getChunkPos(), plan) - || !persisted.getChunkPos().equals(expected.getChunkPos())) { - return null; - } - BoundingBox referenceEnvelope = NativeStructureReferenceEnvelope.referenceBounds( - expected, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord candidate = NativeStructureOwnershipFingerprint.capture( - structureKey, expected, plan, referenceEnvelope); - if (candidate.placementIdentity() - != StructurePlacementGrid.placementIdentity(plan.placement())) { - return null; - } - return NativeStructureOwnershipFingerprint.matches(candidate, persisted) - ? candidate : null; - } - - private static boolean matchesPlan(String structureKey, ChunkPos origin, - NativeStructureStartPlan plan) { - if (structureKey == null || structureKey.isBlank() || plan == null) { - return false; - } - return plan.chunkX() == origin.x() - && plan.chunkZ() == origin.z() - && normalize(structureKey).equals(normalize(plan.source().getStructure())); - } - - private static StructureStart generateExpected( - Engine engine, ServerLevel level, Holder holder, - NativeStructureStartPlan plan, int references) { - ChunkGeneratorStructureState state = level.getChunkSource().getGeneratorState(); - ChunkGenerator generator = level.getChunkSource().getGenerator(); - BiomeSource biomeSource = generator.getBiomeSource(); - NativeStructureFactory.GenerationContext generationContext = - new NativeStructureFactory.GenerationContext( - level.registryAccess(), - generator, - biomeSource, - state.randomState(), - level.getStructureTemplateManager(), - state.getLevelSeed(), - level.dimension(), - level, - biome -> true, - generator.getSeaLevel(), - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight() - ); - return NativeStructureFactory.generate(generationContext, holder, plan, references); - } - - private static boolean naturalStartIsAmbiguous( - Engine engine, String structureKey, Structure structure, - Holder holder, ChunkGeneratorStructureState state, - NativeStructureStartPlan plan) { - if (plan.placement().getNativeSuppression() == NativeStructureSuppression.REPLACE_SOURCE) { - return false; - } - NativeStructureGenerationStatus sourceStatus = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())).status(); - if (sourceStatus == NativeStructureGenerationStatus.REPLACED_BY_IRIS) { - return false; - } - for (StructurePlacement placement : state.getPlacementsForStructure(holder)) { - if (placement.isStructureChunk(state, plan.chunkX(), plan.chunkZ())) { - return true; - } - } - return false; - } - - private static String normalize(String structureKey) { - return structureKey == null ? "" : structureKey.trim().toLowerCase(Locale.ROOT); - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructurePostProcessor.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructurePostProcessor.java deleted file mode 100644 index 329592aef..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructurePostProcessor.java +++ /dev/null @@ -1,66 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.structure.placement.IrisStructureStiltSettings; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.List; -import java.util.function.IntBinaryOperator; - -public final class NativeStructurePostProcessor { - private NativeStructurePostProcessor() { - } - - public static void place(WorldGenLevel world, StructureManager structureManager, ChunkGenerator generator, - WorldgenRandom random, BoundingBox area, ChunkPos chunkPos, String structureId, - StructureStart start, IrisNativeStructureDecision decision, - PaletteBlockResolver paletteBlockResolver, - IntBinaryOperator surfaceHeight) { - IrisStructureStiltSettings stilt = decision.stilt(); - NativeStructureVerticalPlacer.ensureMonumentSeaLevelAlignment(start, structureId, decision.yShift(), - generator.getSeaLevel(), area.minY(), area.maxY() + 1); - start.placeInChunk(world, structureManager, generator, random, area, chunkPos); - if (stilt != null) { - NativeStructureFoundationBuilder.placeStilts(world, area, structureId, start, stilt, - paletteBlockResolver, surfaceHeight, - !NativeStructureVegetationClearer.isUndergroundStep(start.getStructure().step())); - } - } - - public static void prepareTerrain(WorldGenLevel world, BoundingBox area, - List targets, - PaletteBlockResolver paletteBlockResolver) { - if (targets == null || targets.isEmpty()) { - return; - } - NativeStructureTerrainIntegrator.SourceTerrainSnapshot sourceTerrain = - NativeStructureTerrainIntegrator.captureSourceTerrain(world, area, targets); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - NativeStructureTerrainIntegrator.integrateTerrain(world, area, target.structureId(), target.start(), - target.terrain(), paletteBlockResolver, sourceTerrain); - } - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (NativeStructureTerrainIntegrator.clearsLegacyTemplateAir( - target.start(), target.terrain())) { - NativeStructureTerrainIntegrator.clearLegacyTemplateAir( - world, area, target.start(), () -> world.getLevel().getStructureTemplateManager()); - } - } - NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( - world, area, targets, () -> world.getLevel().getStructureTemplateManager()); - } - - @FunctionalInterface - public interface PaletteBlockResolver { - BlockState resolve(IrisMaterialPalette palette, RNG rng, int x, int y, int z); - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceEnvelope.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceEnvelope.java deleted file mode 100644 index 4f59cb9b8..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceEnvelope.java +++ /dev/null @@ -1,239 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; - -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureReferenceEnvelope { - private static final int MAX_REFERENCE_DISTANCE_CHUNKS = 8; - private static final Set WARNED_CLIPPED_TERRAIN = ConcurrentHashMap.newKeySet(); - private static final Set WARNED_SKIPPED_CONTENT = ConcurrentHashMap.newKeySet(); - - private NativeStructureReferenceEnvelope() { - } - - public static StructureStart wrap(StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain) { - return wrap(generated, source, references, terrain, null); - } - - public static StructureStart wrap(StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain, String structureKey) { - referenceBounds(generated, source, terrain, structureKey); - return new StructureStart( - source, - generated.getChunkPos(), - references, - new PiecesContainer(List.copyOf(generated.getPieces())) - ); - } - - public static StructureStart wrapForPublication( - StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain, String structureKey) { - try { - return wrap(generated, source, references, terrain, structureKey); - } catch (UnrepresentableContentException exception) { - warnSkippedContent(generated, source, structureKey); - return StructureStart.INVALID_START; - } - } - - public static BoundingBox referenceBounds(StructureStart start, Structure source, - IrisStructureTerrain terrain) { - return referenceBounds(start, source, terrain, null); - } - - public static BoundingBox referenceBounds(StructureStart start, Structure source, - IrisStructureTerrain terrain, - String structureKey) { - IrisStructureTerrainMode mode = terrain == null - ? IrisStructureTerrainMode.PRESERVE : terrain.resolvedMode(); - boolean usesEnvelope = mode == IrisStructureTerrainMode.BORE - || mode == IrisStructureTerrainMode.FORCE_CARVE - || mode == IrisStructureTerrainMode.VACUUM - || mode == IrisStructureTerrainMode.FLATTEN - || mode == IrisStructureTerrainMode.ENCASE; - int horizontalPadding = mode == IrisStructureTerrainMode.VACUUM - ? NativeStructureSurfaceFitter.surfaceTerrainRadius() - : usesEnvelope ? Math.max(0, terrain.getHorizontalPadding()) : 0; - BoundingBox content = contentBounds(start); - if (!fitsReferenceRange(start.getChunkPos(), content)) { - throw new UnrepresentableContentException("Native structure content at " - + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " exceeds Minecraft's " + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk structure reference range"); - } - BoundingBox adjustedEnvelope; - boolean arithmeticClipped = false; - try { - BoundingBox envelope = horizontalPadding == 0 - ? content - : new BoundingBox( - Math.subtractExact(content.minX(), horizontalPadding), - content.minY(), - Math.subtractExact(content.minZ(), horizontalPadding), - Math.addExact(content.maxX(), horizontalPadding), - content.maxY(), - Math.addExact(content.maxZ(), horizontalPadding) - ); - adjustedEnvelope = mode == IrisStructureTerrainMode.SOURCE - ? source.adjustBoundingBox(content) : envelope; - adjustedEnvelope = encapsulating(adjustedEnvelope, content); - } catch (ArithmeticException exception) { - adjustedEnvelope = maximumReferenceBounds(start.getChunkPos(), content); - arithmeticClipped = true; - } - BoundingBox referencedEnvelope = clampReferenceRange(start.getChunkPos(), adjustedEnvelope); - if (arithmeticClipped || !sameHorizontalBounds(adjustedEnvelope, referencedEnvelope)) { - warnClippedTerrain(start, source, structureKey); - return referencedEnvelope; - } - return adjustedEnvelope; - } - - public static boolean contentFitsReferenceRange(StructureStart start) { - try { - return fitsReferenceRange(start.getChunkPos(), contentBounds(start)); - } catch (UnrepresentableContentException exception) { - return false; - } - } - - public static BoundingBox contentBounds(StructureStart start) { - return contentBounds(start.getPieces()); - } - - private static BoundingBox contentBounds(List pieces) { - BoundingBox bounds = null; - for (StructurePiece piece : pieces) { - bounds = bounds == null - ? copy(piece.getBoundingBox()) - : bounds.encapsulate(piece.getBoundingBox()); - } - if (bounds == null) { - throw new IllegalStateException("Native jigsaw generated no content pieces"); - } - return bounds; - } - - private static BoundingBox clampReferenceRange(ChunkPos startChunk, BoundingBox envelope) { - int minX = minimumReferenceBlock(startChunk.x()); - int maxX = maximumReferenceBlock(startChunk.x()); - int minZ = minimumReferenceBlock(startChunk.z()); - int maxZ = maximumReferenceBlock(startChunk.z()); - return new BoundingBox( - Math.max(envelope.minX(), minX), - envelope.minY(), - Math.max(envelope.minZ(), minZ), - Math.min(envelope.maxX(), maxX), - envelope.maxY(), - Math.min(envelope.maxZ(), maxZ) - ); - } - - private static BoundingBox maximumReferenceBounds(ChunkPos startChunk, BoundingBox content) { - return new BoundingBox( - minimumReferenceBlock(startChunk.x()), content.minY(), - minimumReferenceBlock(startChunk.z()), - maximumReferenceBlock(startChunk.x()), content.maxY(), - maximumReferenceBlock(startChunk.z()) - ); - } - - private static boolean fitsReferenceRange(ChunkPos startChunk, BoundingBox content) { - return content.minX() >= minimumReferenceBlock(startChunk.x()) - && content.maxX() <= maximumReferenceBlock(startChunk.x()) - && content.minZ() >= minimumReferenceBlock(startChunk.z()) - && content.maxZ() <= maximumReferenceBlock(startChunk.z()); - } - - private static int minimumReferenceBlock(int originChunk) { - return checkedReferenceBlock( - ((long) originChunk - MAX_REFERENCE_DISTANCE_CHUNKS) << 4, originChunk); - } - - private static int maximumReferenceBlock(int originChunk) { - return checkedReferenceBlock( - (((long) originChunk + MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15L, originChunk); - } - - private static int checkedReferenceBlock(long block, int originChunk) { - if (block < Integer.MIN_VALUE || block > Integer.MAX_VALUE) { - throw new UnrepresentableContentException("Native structure origin chunk " + originChunk - + " exceeds Minecraft's block coordinate range"); - } - return (int) block; - } - - private static boolean sameHorizontalBounds(BoundingBox left, BoundingBox right) { - return left.minX() == right.minX() - && left.minZ() == right.minZ() - && left.maxX() == right.maxX() - && left.maxZ() == right.maxZ(); - } - - private static BoundingBox copy(BoundingBox bounds) { - return new BoundingBox( - bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ()); - } - - private static BoundingBox encapsulating(BoundingBox left, BoundingBox right) { - return new BoundingBox( - Math.min(left.minX(), right.minX()), - Math.min(left.minY(), right.minY()), - Math.min(left.minZ(), right.minZ()), - Math.max(left.maxX(), right.maxX()), - Math.max(left.maxY(), right.maxY()), - Math.max(left.maxZ(), right.maxZ()) - ); - } - - private static void warnClippedTerrain( - StructureStart start, Structure source, String structureKey) { - String key = warningKey(source, structureKey); - if (WARNED_CLIPPED_TERRAIN.add(key)) { - IrisLogging.warn("Clipping optional terrain envelope for native structure '" - + key + "' at " + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " to Minecraft's " + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk reference range; all generated content remains exactly referenced"); - } - } - - private static void warnSkippedContent( - StructureStart start, Structure source, String structureKey) { - String key = warningKey(source, structureKey); - if (WARNED_SKIPPED_CONTENT.add(key)) { - IrisLogging.warn("Skipping native structure '" + key + "' at " - + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " because its generated content exceeds Minecraft's " - + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk reference range; no structure start, ownership, references, or blocks were published"); - } - } - - private static String warningKey(Structure source, String structureKey) { - if (structureKey != null && !structureKey.isBlank()) { - return structureKey.trim().toLowerCase(Locale.ROOT); - } - return source.getClass().getName(); - } - - public static final class UnrepresentableContentException extends IllegalStateException { - private UnrepresentableContentException(String message) { - super(message); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceRepair.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceRepair.java deleted file mode 100644 index ba8c6cf1b..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReferenceRepair.java +++ /dev/null @@ -1,246 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.spi.IrisLogging; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.ArrayList; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureReferenceRepair { - private static final int REFERENCE_DISTANCE_CHUNKS = - NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS; - private static final Set WARNED_POLICY_INVALIDATIONS = ConcurrentHashMap.newKeySet(); - - private NativeStructureReferenceRepair() { - } - - public static void createReferences(Engine engine, WorldGenLevel level, - StructureManager structureManager, ChunkAccess targetChunk) { - ChunkPos target = targetChunk.getPos(); - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - ServerLevel serverLevel = level.getLevel(); - List starts = new ArrayList<>(); - for (int originChunkX = target.x() - REFERENCE_DISTANCE_CHUNKS; - originChunkX <= target.x() + REFERENCE_DISTANCE_CHUNKS; originChunkX++) { - for (int originChunkZ = target.z() - REFERENCE_DISTANCE_CHUNKS; - originChunkZ <= target.z() + REFERENCE_DISTANCE_CHUNKS; originChunkZ++) { - try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = - openOriginRuntimeScope(engine, originChunkX, originChunkZ)) { - ChunkAccess originChunk = level.getChunk( - originChunkX, originChunkZ, ChunkStatus.STRUCTURE_STARTS); - for (Map.Entry entry : originChunk.getAllStarts().entrySet()) { - ScannedStart start = scanStart( - engine, serverLevel, structureManager, targetChunk, registry, - originChunk, entry.getKey(), entry.getValue()); - if (start != null && isTargetRelevant(engine, targetChunk, start)) { - starts.add(start); - } - } - } - } - } - for (ScannedStart start : starts) { - boolean valid; - synchronized (start.originChunk()) { - StructureStart current = start.originChunk().getStartForStructure(start.structure()); - valid = current == start.start() && current.isValid(); - } - if (!valid) { - continue; - } - structureManager.addReferenceForStructure( - start.structure(), - start.origin().pack(), targetChunk); - } - } - - private static GenerationHistoryRuntimeRouter.CoordinateScope openOriginRuntimeScope( - Engine engine, - int chunkX, - int chunkZ - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(chunkX << 4, chunkZ << 4); - } catch (IOException failure) { - throw new IllegalStateException("Unable to resolve native structure references for origin " - + chunkX + "," + chunkZ + " through generation history.", failure); - } - } - - private static boolean isTargetRelevant( - Engine engine, ChunkAccess targetChunk, ScannedStart scanned) { - StructureStart start = scanned.start(); - Structure structure = scanned.structure(); - ChunkPos target = targetChunk.getPos(); - if (scanned.ownership() != null) { - return requiresReference(target, scanned.structureKey(), start, scanned.ownership()); - } - if (scanned.registered()) { - return requiresNaturalReference( - engine, target, scanned.structureKey(), structure, start); - } - return NativeStructureReferenceEnvelope.contentBounds(start).intersects( - target.getMinBlockX(), target.getMinBlockZ(), - target.getMaxBlockX(), target.getMaxBlockZ()); - } - - private static ScannedStart scanStart( - Engine engine, ServerLevel level, StructureManager structureManager, - ChunkAccess targetChunk, Registry registry, - ChunkAccess originChunk, Structure structure, StructureStart start) { - Identifier identifier = registry.getKey(structure); - String structureKey = identifier == null - ? structure.getClass().getName() : identifier.toString(); - if (start == null || !start.isValid()) { - return null; - } - if (!NativeStructureReferenceEnvelope.contentFitsReferenceRange(start)) { - if (identifier != null) { - NativeStructureOwnershipStore.discard( - engine, structureKey, start.getChunkPos().x(), start.getChunkPos().z()); - } - invalidateStart(structureManager, originChunk, structure, start, targetChunk); - return null; - } - ChunkPos startOrigin = start.getChunkPos(); - NativeStructureOwnershipRecord ownership = identifier == null ? null - : NativeStructureOwnershipRecovery.resolve( - engine, level, structureKey, structure, start); - if (ownership != null) { - return new ScannedStart( - originChunk, structure, start, structureKey, ownership, true); - } - if (identifier != null && !naturalPolicyAllows(engine, structureKey, structure)) { - invalidateStart(structureManager, originChunk, structure, start, targetChunk); - if (WARNED_POLICY_INVALIDATIONS.add(structureKey)) { - IrisLogging.warn("Invalidating persisted natural structure start '" - + structureKey + "' because the current Iris dimension policy disables it"); - } - return null; - } - return new ScannedStart( - originChunk, structure, start, structureKey, null, identifier != null); - } - - private static boolean naturalPolicyAllows( - Engine engine, String structureKey, Structure structure) { - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - return naturalDecisionAllows(decision); - } - - static boolean naturalDecisionAllows(IrisNativeStructureDecision decision) { - return Objects.requireNonNull( - decision, "Native structure decision must not be null").generate(); - } - - private static void invalidateStart( - StructureManager structureManager, ChunkAccess originChunk, - Structure structure, StructureStart start, ChunkAccess targetChunk) { - synchronized (originChunk) { - StructureStart current = originChunk.getStartForStructure(structure); - if (current == start && current.isValid()) { - structureManager.setStartForStructure( - structure, - StructureStart.INVALID_START, originChunk); - } - } - if (targetChunk != null) { - removeTargetReference(targetChunk, structure, start.getChunkPos().pack()); - } - } - - private static void removeTargetReference( - ChunkAccess targetChunk, Structure structure, long origin) { - LongSet current = targetChunk.getAllReferences().get(structure); - if (current == null || !current.contains(origin)) { - return; - } - Map updated = new HashMap<>(targetChunk.getAllReferences()); - LongSet retained = new LongOpenHashSet(current); - retained.remove(origin); - if (retained.isEmpty()) { - updated.remove(structure); - } else { - updated.put(structure, retained); - } - targetChunk.setAllReferences(updated); - } - - static boolean requiresNaturalReference(Engine engine, ChunkPos target, String structureKey, - Structure structure, StructureStart start) { - if (target == null || start == null || !start.isValid()) { - return false; - } - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - if (!decision.generate()) { - return false; - } - BoundingBox referenceBounds = NativeStructureReferenceEnvelope.referenceBounds( - start, structure, - NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()), - structureKey); - return referenceBounds.intersects( - target.getMinBlockX(), target.getMinBlockZ(), - target.getMaxBlockX(), target.getMaxBlockZ()); - } - - static boolean requiresReference(ChunkPos target, String structureKey, - StructureStart start, - NativeStructureOwnershipRecord ownership) { - if (target == null || start == null || !start.isValid() - || ownership == null || !ownership.structureKey().equals(structureKey) - || !ownership.covers(target.x(), target.z())) { - return false; - } - return NativeStructureOwnershipFingerprint.matches(ownership, start); - } - - private record ScannedStart( - ChunkAccess originChunk, - Structure structure, - StructureStart start, - String structureKey, - NativeStructureOwnershipRecord ownership, - boolean registered - ) { - private ChunkPos origin() { - return ownership == null - ? start.getChunkPos() - : new ChunkPos(ownership.originChunkX(), ownership.originChunkZ()); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReflection.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReflection.java deleted file mode 100644 index 51ab6596d..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureReflection.java +++ /dev/null @@ -1,386 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import com.mojang.datafixers.util.Either; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ServerLevelAccessor; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.levelgen.structure.ScatteredFeaturePiece; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces; -import net.minecraft.world.level.levelgen.structure.structures.RuinedPortalPiece; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; - -final class NativeStructureReflection { - private NativeStructureReflection() { - } - - static Field resolveScatteredHeightPositionField() { - Field resolved = null; - for (Field field : ScatteredFeaturePiece.class.getDeclaredFields()) { - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || field.getType() != int.class - || !Modifier.isProtected(modifiers) || Modifier.isFinal(modifiers)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("ScatteredFeaturePiece has multiple mutable protected int fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("ScatteredFeaturePiece height-position field is missing"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("ScatteredFeaturePiece height-position field is inaccessible"); - } - return resolved; - } - - static RuinedPortalPiece.VerticalPlacement ruinedPortalVerticalPlacement(RuinedPortalPiece piece) { - try { - return (RuinedPortalPiece.VerticalPlacement) RuinedPortalPlacementAccess.FIELD.get(piece); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native ruined portal vertical placement", error); - } - } - - private static Field resolveRuinedPortalPlacementField() { - Field resolved = null; - for (Field field : RuinedPortalPiece.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) - || field.getType() != RuinedPortalPiece.VerticalPlacement.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("RuinedPortalPiece has multiple vertical-placement fields"); - } - resolved = field; - } - if (resolved == null || !resolved.trySetAccessible()) { - throw new IllegalStateException("RuinedPortalPiece vertical-placement field is inaccessible"); - } - return resolved; - } - - private static Field resolveMonumentChildPiecesField() { - Field resolved = null; - for (Field field : OceanMonumentPieces.MonumentBuilding.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != List.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("Ocean Monument has multiple instance List fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("Ocean Monument child-pieces List field is missing"); - } - if (!Modifier.isPrivate(resolved.getModifiers()) || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("Ocean Monument child-pieces field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("Ocean Monument child-pieces field is inaccessible"); - } - return resolved; - } - - static Field resolveSinglePoolTemplateField() { - Field resolved = null; - for (Field field : SinglePoolElement.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != Either.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("SinglePoolElement has multiple instance Either fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("SinglePoolElement template Either field is missing"); - } - if (!Modifier.isProtected(resolved.getModifiers()) || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("SinglePoolElement template field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("SinglePoolElement template field is inaccessible"); - } - return resolved; - } - - static StructureTemplate resolveTemplate(SinglePoolElement element, - Supplier templates) { - Object value; - try { - value = SinglePoolTemplateAccess.FIELD.get(element); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure pool template", error); - } - if (!(value instanceof Either reference)) { - throw new IllegalStateException("Native structure pool template field is not an Either"); - } - return resolveTemplateReference(reference, templates); - } - - @SuppressWarnings("unchecked") - static List resolveTemplateBlocks( - StructureTemplate template, StructurePlaceSettings settings, - BlockPos origin) { - Object value; - try { - value = StructureTemplatePalettesAccess.FIELD.get(template); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure template palettes", error); - } - if (!(value instanceof List paletteValues)) { - throw new IllegalStateException("Native structure template palettes field is not a List"); - } - List palettes = - (List) paletteValues; - if (palettes.isEmpty()) { - return List.of(); - } - return settings.getRandomPalette(palettes, origin).blocks(); - } - - static StructurePlaceSettings resolvePlacementSettings( - SinglePoolElement element, PoolElementStructurePiece piece, - BoundingBox area) { - LiquidSettings liquidSettings; - try { - liquidSettings = (LiquidSettings) PoolPieceLiquidSettingsAccess.FIELD.get(piece); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure piece liquid settings", error); - } - try { - return (StructurePlaceSettings) SinglePoolSettingsAccess.METHOD.invoke( - element, piece.getRotation(), area, liquidSettings, false); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot create native structure placement settings", error); - } - } - - @SuppressWarnings("unchecked") - static List processTemplateBlocks( - ServerLevelAccessor world, BlockPos origin, BlockPos referencePos, - StructurePlaceSettings settings, - List blocks, - StructureTemplate template) { - Method forgeMethod = StructureTemplateProcessingAccess.FORGE_METHOD; - if (forgeMethod == null) { - return StructureTemplate.processBlockInfos( - world, origin, referencePos, settings, blocks); - } - try { - return (List) forgeMethod.invoke( - null, world, origin, referencePos, settings, blocks, template); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot process native structure template blocks", error); - } - } - - static StructureTemplate resolveTemplateReference(Either reference, - Supplier templates) { - return reference.map( - location -> resolveNamedTemplate(location, templates), - NativeStructureReflection::requireRuntimeTemplate); - } - - private static StructureTemplate resolveNamedTemplate(Object value, - Supplier templates) { - if (!(value instanceof Identifier identifier)) { - throw new IllegalStateException("Native structure pool template identifier is " - + (value == null ? "null" : value.getClass().getName())); - } - return Objects.requireNonNull(templates == null ? null : templates.get(), - "Native structure template manager is unavailable").getOrCreate(identifier); - } - - private static StructureTemplate requireRuntimeTemplate(Object value) { - if (!(value instanceof StructureTemplate template)) { - throw new IllegalStateException("Native runtime structure pool template is " - + (value == null ? "null" : value.getClass().getName())); - } - return template; - } - - private static Field resolveStructureTemplatePalettesField() { - Field resolved = null; - for (Field field : StructureTemplate.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != List.class - || !isPaletteList(field.getGenericType())) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("StructureTemplate has multiple palette List fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("StructureTemplate palette List field is missing"); - } - if (!Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("StructureTemplate palette field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("StructureTemplate palette field is inaccessible"); - } - return resolved; - } - - private static boolean isPaletteList(Type type) { - if (!(type instanceof ParameterizedType parameterized)) { - return false; - } - Type[] arguments = parameterized.getActualTypeArguments(); - return arguments.length == 1 && arguments[0] == StructureTemplate.Palette.class; - } - - private static Method resolveSinglePoolSettingsMethod() { - Method resolved = null; - Class[] parameterTypes = { - Rotation.class, BoundingBox.class, LiquidSettings.class, boolean.class - }; - for (Method method : SinglePoolElement.class.getDeclaredMethods()) { - if (Modifier.isStatic(method.getModifiers()) - || method.getReturnType() != StructurePlaceSettings.class - || !Arrays.equals(method.getParameterTypes(), parameterTypes)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("SinglePoolElement has multiple placement-settings methods"); - } - resolved = method; - } - if (resolved == null) { - throw new IllegalStateException("SinglePoolElement placement-settings method is missing"); - } - if (!Modifier.isProtected(resolved.getModifiers())) { - throw new IllegalStateException("SinglePoolElement placement-settings method has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("SinglePoolElement placement-settings method is inaccessible"); - } - return resolved; - } - - private static Field resolvePoolPieceLiquidSettingsField() { - Field resolved = null; - for (Field field : PoolElementStructurePiece.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) - || field.getType() != LiquidSettings.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("PoolElementStructurePiece has multiple liquid-settings fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is missing"); - } - if (!Modifier.isPrivate(resolved.getModifiers()) - || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is inaccessible"); - } - return resolved; - } - - private static Method resolveForgeTemplateProcessingMethod() { - Class[] parameterTypes = { - ServerLevelAccessor.class, BlockPos.class, BlockPos.class, - StructurePlaceSettings.class, List.class, StructureTemplate.class - }; - Method resolved = null; - for (Method method : StructureTemplate.class.getDeclaredMethods()) { - if (!Modifier.isPublic(method.getModifiers()) - || !Modifier.isStatic(method.getModifiers()) - || method.getReturnType() != List.class - || !Arrays.equals(method.getParameterTypes(), parameterTypes)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException( - "StructureTemplate has multiple Forge block-processing methods"); - } - resolved = method; - } - return resolved; - } - - static final class MonumentChildPiecesAccess { - static final Field FIELD = resolveMonumentChildPiecesField(); - - private MonumentChildPiecesAccess() { - } - } - - private static final class RuinedPortalPlacementAccess { - private static final Field FIELD = resolveRuinedPortalPlacementField(); - - private RuinedPortalPlacementAccess() { - } - } - - static final class ScatteredHeightPositionAccess { - static final Field FIELD = resolveScatteredHeightPositionField(); - - private ScatteredHeightPositionAccess() { - } - } - - private static final class SinglePoolTemplateAccess { - private static final Field FIELD = resolveSinglePoolTemplateField(); - - private SinglePoolTemplateAccess() { - } - } - - private static final class StructureTemplatePalettesAccess { - private static final Field FIELD = resolveStructureTemplatePalettesField(); - - private StructureTemplatePalettesAccess() { - } - } - - private static final class SinglePoolSettingsAccess { - private static final Method METHOD = resolveSinglePoolSettingsMethod(); - - private SinglePoolSettingsAccess() { - } - } - - private static final class PoolPieceLiquidSettingsAccess { - private static final Field FIELD = resolvePoolPieceLiquidSettingsField(); - - private PoolPieceLiquidSettingsAccess() { - } - } - - private static final class StructureTemplateProcessingAccess { - private static final Method FORGE_METHOD = resolveForgeTemplateProcessingMethod(); - - private StructureTemplateProcessingAccess() { - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureStartInjector.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureStartInjector.java deleted file mode 100644 index e78026086..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureStartInjector.java +++ /dev/null @@ -1,147 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.structure.nativegen.NativeStructureSuppression; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureStartInjector { - private static final Set WARNED_DUPLICATE_STRUCTURES = ConcurrentHashMap.newKeySet(); - - private NativeStructureStartInjector() { - } - - public static Map inject(InjectionContext context) { - Objects.requireNonNull(context, "Native structure injection context must not be null"); - ChunkAccess chunk = context.chunk(); - Registry registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE); - SectionPos section = SectionPos.bottomOf(chunk); - Map configuredStarts = new LinkedHashMap<>(); - for (NativeStructureStartPlan plan : NativeStructurePlacementPlanner.plansAt( - context.engine(), chunk.getPos().x(), chunk.getPos().z())) { - Identifier identifier = Identifier.tryParse(plan.source().getStructure()); - if (identifier == null) { - throw new IllegalStateException("Configured native structure key is invalid: " - + plan.source().getStructure()); - } - Structure structure = registry.getValue(identifier); - if (structure == null) { - throw new IllegalStateException("Configured native structure is not registered: " + identifier); - } - Holder holder = registry.wrapAsHolder(structure); - if (configuredStarts.containsKey(structure)) { - if (WARNED_DUPLICATE_STRUCTURES.add(identifier.toString())) { - IrisLogging.warn("Ignoring duplicate native structure placements for '" - + identifier + "'; the first deterministic candidate owns each chunk start"); - } - continue; - } - StructureStart existing = context.structureManager().getStartForStructure( - structure, chunk); - boolean replacement = plan.placement().getNativeSuppression() - == NativeStructureSuppression.REPLACE_SOURCE; - if (!replacement && existing != null && existing.isValid()) { - NativeStructureGenerationStatus sourceStatus = NativeStructureGenerationPolicy.resolve( - context.engine(), identifier.toString(), - NativeStructureVegetationClearer.isUndergroundStep(structure.step())).status(); - replacement = sourceStatus == NativeStructureGenerationStatus.REPLACED_BY_IRIS; - } - if (!replacement && existing != null && existing.isValid()) { - continue; - } - int references = existing != null && existing.isValid() ? existing.getReferences() : 0; - NativeStructureFactory.GenerationContext generationContext = - new NativeStructureFactory.GenerationContext( - context.registryAccess(), - context.generator(), - context.biomeSource(), - context.structureState().randomState(), - context.templateManager(), - context.structureState().getLevelSeed(), - context.levelKey(), - chunk, - biome -> true, - context.generator().getSeaLevel(), - (x, z) -> Engine.hostHeight(context.engine(), x, z, true) - + context.engine().getMinHeight() - ); - StructureStart generated = NativeStructureFactory.generate( - generationContext, holder, plan, references); - if (!isUsableGeneratedStart(generated)) { - if (replacement) { - context.structureManager().setStartForStructure( - structure, StructureStart.INVALID_START, chunk); - } - NativeStructureOwnershipStore.discard( - context.engine(), identifier.toString(), - chunk.getPos().x(), chunk.getPos().z()); - continue; - } - BoundingBox referenceBounds = NativeStructureReferenceEnvelope.referenceBounds( - generated, structure, plan.placement().resolvedTerrain(), identifier.toString()); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - identifier.toString(), generated, plan, referenceBounds); - NativeStructureOwnershipStore.record(context.engine(), ownership); - try { - context.structureManager().setStartForStructure( - structure, generated, chunk); - } catch (RuntimeException | Error publicationError) { - try { - NativeStructureOwnershipStore.discard( - context.engine(), identifier.toString(), - chunk.getPos().x(), chunk.getPos().z()); - } catch (RuntimeException | Error cleanupError) { - publicationError.addSuppressed(cleanupError); - } - throw publicationError; - } - configuredStarts.put(structure, plan); - } - return Map.copyOf(configuredStarts); - } - - static boolean isUsableGeneratedStart(StructureStart start) { - return start != null && start.isValid(); - } - - public record InjectionContext( - Engine engine, - RegistryAccess registryAccess, - ChunkGeneratorStructureState structureState, - StructureManager structureManager, - ChunkAccess chunk, - StructureTemplateManager templateManager, - ResourceKey levelKey, - ChunkGenerator generator, - BiomeSource biomeSource - ) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceFitter.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceFitter.java deleted file mode 100644 index 6beb66dad..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceFitter.java +++ /dev/null @@ -1,1221 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.object.IrisObjectVacuum; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.TerrainAdjustment; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.structures.IglooPieces; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; -import net.minecraft.world.level.levelgen.structure.structures.OceanRuinStructure; -import net.minecraft.world.level.levelgen.structure.structures.RuinedPortalPiece; -import net.minecraft.world.level.levelgen.structure.structures.ShipwreckStructure; -import net.minecraft.world.level.levelgen.structure.structures.StrongholdStructure; -import net.minecraft.world.level.levelgen.structure.structures.SwampHutPiece; -import net.minecraft.world.level.levelgen.structure.structures.WoodlandMansionPieces; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureSurfaceFitter { - private static final double SURFACE_TERRAIN_FALLOFF = 2.0; - private static final long SURFACE_TERRAIN_INFLUENCE_SCALE = 1_000_000L; - private static final int SURFACE_TERRAIN_RADIUS = 12; - private static final int MAX_SOURCE_SURFACE_DELTA = 6; - private static final int MAX_SURFACE_TEMPLATE_CELLS = 4_194_304; - private static final Set WARNED_SOURCE_BUDGET = - ConcurrentHashMap.newKeySet(); - private static final Set WARNED_VACUUM_BUDGET = - ConcurrentHashMap.newKeySet(); - private static final Set WARNED_FLATTEN_BUDGET = - ConcurrentHashMap.newKeySet(); - - private NativeStructureSurfaceFitter() { - } - - public static SurfaceTerrainPlan prepareSurfaceStructures( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight) { - return prepareSurfaceStructures( - world, area, targets, surfaceHeight, MAX_SURFACE_TEMPLATE_CELLS); - } - - static SurfaceTerrainPlan prepareSurfaceStructures( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight, int maximumTemplateCells) { - if (targets == null || targets.isEmpty()) { - return SurfaceTerrainPlan.empty(); - } - Objects.requireNonNull(surfaceHeight, "Surface structure terrain fitting requires an Iris height resolver"); - List anchors = collectSourceSurfaceAnchors( - world, area, targets, maximumTemplateCells); - if (!anchors.isEmpty()) { - fitSurfaceTerrain(world, area, anchors, surfaceHeight); - } - FlattenFootprint flatten = collectFlattenFootprint( - world, area, targets, surfaceHeight, maximumTemplateCells); - Map flattenedHeights = flatten.anchors().isEmpty() && flatten.projectedSupport().isEmpty() ? Map.of() - : fitFlattenTerrain(world, area, flatten, surfaceHeight); - VacuumFootprint vacuum = collectVacuumFootprint( - world, area, targets, maximumTemplateCells); - if (!vacuum.anchors().isEmpty()) { - fitVacuumTerrain(world, area, vacuum, surfaceHeight); - } - return SurfaceTerrainPlan.create( - vacuum, area, flattenedHeights); - } - - public static void repairVacuumFoundations( - WorldGenLevel world, BoundingBox area, - SurfaceTerrainPlan plan) { - if (plan == null || plan.foundationBases.isEmpty()) { - return; - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (long packedBase : plan.foundationBases) { - BlockPos base = BlockPos.of(packedBase); - if (!area.isInside(base)) { - continue; - } - BlockState baseState = world.getBlockState(position.set(base)); - if (!NativeStructureTemplateOccupancy.isSolidBase(baseState)) { - continue; - } - int supportY = findVacuumFoundationSupport( - world, area, position, base.getX(), base.getY() - 1, base.getZ()); - if (supportY == Integer.MIN_VALUE || supportY == base.getY() - 1) { - continue; - } - if (intersectsVacuumOccupancy( - plan.occupiedCells, base.getX(), base.getZ(), - supportY + 1, base.getY() - 1)) { - continue; - } - BlockState fill = resolveSurfaceMaterials( - world, position, base.getX(), base.getZ(), supportY, - area.minY()).subsurface(); - for (int y = supportY + 1; y < base.getY(); y++) { - BlockState existing = world.getBlockState( - position.set(base.getX(), y, base.getZ())); - if (existing.isAir() || !existing.getFluidState().isEmpty()) { - world.setBlock(position, fill, 2); - } - } - } - } - - static boolean shouldPrepareSurfaceTerrain(TerrainAdjustment adjustment, - GenerationStep.Decoration step) { - return step == GenerationStep.Decoration.SURFACE_STRUCTURES - && (adjustment == TerrainAdjustment.BEARD_THIN - || adjustment == TerrainAdjustment.BEARD_BOX); - } - - static int surfaceTerrainRadius() { - return SURFACE_TERRAIN_RADIUS; - } - - static int resolveSurfaceTarget(List anchors, int worldX, int worldZ, - int originalY) { - return resolveSurface(anchors, worldX, worldZ, originalY).targetY(); - } - - private static SurfaceResolution resolveSurface(List anchors, - int worldX, int worldZ, int originalY) { - int localTargetY = originalY; - SurfaceAnchor selectedLocal = null; - long totalInfluence = 0L; - long weightedTargetY = 0L; - long maximumInfluence = 0L; - for (SurfaceAnchor anchor : anchors) { - int outX = IrisObjectVacuum.outset(worldX, anchor.minX(), anchor.maxX()); - int outZ = IrisObjectVacuum.outset(worldZ, anchor.minZ(), anchor.maxZ()); - boolean containsColumn = outX == 0 && outZ == 0; - long distanceSquared = (long) outX * outX + (long) outZ * outZ; - long radiusSquared = (long) SURFACE_TERRAIN_RADIUS * SURFACE_TERRAIN_RADIUS; - if (distanceSquared > radiusSquared) { - continue; - } - double distance = Math.sqrt(distanceSquared); - double factor = Math.pow( - 1D - distance / SURFACE_TERRAIN_RADIUS, SURFACE_TERRAIN_FALLOFF); - if (factor <= 0D) { - continue; - } - int boundedTargetY = boundedSurfaceTarget(originalY, anchor.meetY()); - if (containsColumn) { - if (precedes(anchor, selectedLocal)) { - localTargetY = boundedTargetY; - selectedLocal = anchor; - } - continue; - } - long influence = Math.round(factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - if (influence <= 0L) { - continue; - } - long weightedInfluence = influence * Math.max(1, anchor.strength()); - totalInfluence += weightedInfluence; - weightedTargetY += weightedInfluence * boundedTargetY; - maximumInfluence = Math.max(maximumInfluence, influence); - } - if (selectedLocal != null) { - return new SurfaceResolution( - localTargetY, - selectedLocal.strength() > 1 - && localTargetY == selectedLocal.meetY()); - } - if (totalInfluence == 0L) { - return new SurfaceResolution(originalY, false); - } - double blendedTargetY = weightedTargetY / (double) totalInfluence; - double factor = maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE; - return new SurfaceResolution( - blendSurfaceTarget(originalY, blendedTargetY, factor), false); - } - - private static int boundedSurfaceTarget(int originalY, int meetY) { - int delta = Math.max( - -MAX_SOURCE_SURFACE_DELTA, - Math.min(MAX_SOURCE_SURFACE_DELTA, meetY - originalY)); - return originalY + delta; - } - - private static int blendSurfaceTarget(int originalY, double targetY, double factor) { - double delta = (targetY - originalY) * factor; - int magnitude = (int) Math.round(Math.abs(delta)); - return originalY + (delta < 0D ? -magnitude : magnitude); - } - - private static boolean precedes(SurfaceAnchor candidate, SurfaceAnchor selected) { - if (selected == null) { - return true; - } - if (candidate.strength() != selected.strength()) { - return candidate.strength() > selected.strength(); - } - if (candidate.meetY() != selected.meetY()) { - return candidate.meetY() < selected.meetY(); - } - if (candidate.minX() != selected.minX()) { - return candidate.minX() < selected.minX(); - } - if (candidate.minZ() != selected.minZ()) { - return candidate.minZ() < selected.minZ(); - } - if (candidate.maxX() != selected.maxX()) { - return candidate.maxX() < selected.maxX(); - } - if (candidate.maxZ() != selected.maxZ()) { - return candidate.maxZ() < selected.maxZ(); - } - return false; - } - - private static List collectSourceSurfaceAnchors( - WorldGenLevel world, BoundingBox area, - List targets, - int maximumTemplateCells) { - BoundingBox influenceArea = new BoundingBox( - area.minX() - SURFACE_TERRAIN_RADIUS, area.minY(), - area.minZ() - SURFACE_TERRAIN_RADIUS, - area.maxX() + SURFACE_TERRAIN_RADIUS, area.maxY(), - area.maxZ() + SURFACE_TERRAIN_RADIUS); - List anchors = new ArrayList<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (!requiresSourceSurfaceTerrain(target)) { - continue; - } - StructureStart start = target.start(); - if (!seenStarts.add(start)) { - continue; - } - BoundingBox firstBounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos firstCenter = firstBounds.getCenter(); - BlockPos referencePosition = new BlockPos( - firstCenter.getX(), firstBounds.minY(), firstCenter.getZ()); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - boolean budgetExceeded = false; - for (StructurePiece piece : start.getPieces()) { - if (piece instanceof PoolElementStructurePiece poolPiece) { - StructureTemplatePool.Projection projection = - poolPiece.getElement().getProjection(); - if (!budgetExceeded - && (projection == StructureTemplatePool.Projection.RIGID - || projection == StructureTemplatePool.Projection.TERRAIN_MATCHING) - && intersectsHorizontally(poolPiece.getBoundingBox(), influenceArea)) { - try { - List pieceAnchors = new ArrayList<>(); - addProcessedSurfaceAnchors( - world, influenceArea, poolPiece, referencePosition, - projection, budget, pieceAnchors); - anchors.addAll(pieceAnchors); - } catch (TemplateBudgetExceeded ignored) { - budgetExceeded = true; - warnSourceBudget(target); - } - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - anchors.add(new SurfaceAnchor( - junction.getSourceX(), junction.getSourceX(), - junction.getSourceZ(), junction.getSourceZ(), - junction.getSourceGroundY() - 1, 1)); - } - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - anchors.add(surfaceAnchor(bounds, bounds.minY(), 2)); - } - } - return List.copyOf(anchors); - } - - private static void addProcessedSurfaceAnchors( - WorldGenLevel world, BoundingBox influenceArea, - PoolElementStructurePiece piece, BlockPos referencePosition, - StructureTemplatePool.Projection projection, - TemplateCellBudget budget, - List anchors) { - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, piece, referencePosition, influenceArea, - () -> world.getLevel().getStructureTemplateManager(), - influenceArea::isInside, budget::consume); - if (!occupancy.resolved()) { - return; - } - int maximumFoundationY = piece.getBoundingBox().minY() - + piece.getGroundLevelDelta(); - Map lowest = - NativeStructureTemplateOccupancy.lowestProcessedSolidCells(occupancy.cells()); - for (NativeStructureTemplateOccupancy.LowestCell cell : lowest.values()) { - budget.consume(1); - if (projection == StructureTemplatePool.Projection.RIGID - && cell.y() > maximumFoundationY) { - continue; - } - int groundY = projection == StructureTemplatePool.Projection.RIGID - ? maximumFoundationY : cell.y(); - BoundingBox column = new BoundingBox( - cell.x(), groundY, cell.z(), cell.x(), groundY, cell.z()); - anchors.add(surfaceAnchor(column, groundY, 2)); - } - } - - private static void warnSourceBudget( - NativeStructureTerrainIntegrator.TerrainTarget target) { - String structureId = target.structureId() == null - ? target.start().getStructure().getClass().getName() - : target.structureId(); - if (WARNED_SOURCE_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure SOURCE fitting for '" - + structureId + "' exceeded its bounded template budget; " - + "skipping remaining processed footprints for this start"); - } - } - - private static VacuumFootprint collectVacuumFootprint( - WorldGenLevel world, BoundingBox area, - List targets, - int maximumTemplateCells) { - BoundingBox influenceArea = new BoundingBox( - area.minX() - SURFACE_TERRAIN_RADIUS, area.minY(), - area.minZ() - SURFACE_TERRAIN_RADIUS, - area.maxX() + SURFACE_TERRAIN_RADIUS, area.maxY(), - area.maxZ() + SURFACE_TERRAIN_RADIUS); - Map anchors = new HashMap<>(); - Map columnCaps = new HashMap<>(); - Set foundationBases = new HashSet<>(); - Set occupiedCells = new HashSet<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (target == null || target.terrain() == null - || target.terrain().resolvedMode() != IrisStructureTerrainMode.VACUUM - || target.start() == null || !target.start().isValid() - || !seenStarts.add(target.start())) { - continue; - } - StructureStart start = target.start(); - BoundingBox firstBounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos firstCenter = firstBounds.getCenter(); - BlockPos referencePosition = new BlockPos( - firstCenter.getX(), firstBounds.minY(), firstCenter.getZ()); - Map targetAnchors = new HashMap<>(); - Map targetCaps = new HashMap<>(); - Set targetFoundationBases = new HashSet<>(); - Set targetOccupiedCells = new HashSet<>(); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - try { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!intersectsHorizontally(bounds, influenceArea) - || !(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() - != StructureTemplatePool.Projection.RIGID) { - continue; - } - addVacuumJunctionAnchors( - anchors, influenceArea, poolPiece); - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, poolPiece, referencePosition, influenceArea, - () -> world.getLevel().getStructureTemplateManager(), - influenceArea::isInside, budget::consume); - if (!occupancy.resolved()) { - continue; - } - targetOccupiedCells.addAll(occupancy.cells().keySet()); - Map lowest = - NativeStructureTemplateOccupancy.lowestCells( - occupancy.cells()); - int groundY = bounds.minY() - + poolPiece.getGroundLevelDelta() - 1; - for (NativeStructureTemplateOccupancy.LowestCell cell : lowest.values()) { - budget.consume(1); - long column = NativeStructureTemplateOccupancy.columnKey( - cell.x(), cell.z()); - targetCaps.merge( - column, Math.min(cell.y(), groundY), Math::min); - if (cell.occupancy().blocker() - || cell.y() > groundY + 1 - || !NativeStructureTemplateOccupancy.isSolidBase( - cell.occupancy().state())) { - continue; - } - addVacuumAnchor(targetAnchors, cell.x(), cell.z(), - new VacuumAnchor(cell.y() - 1, 2)); - targetFoundationBases.add(BlockPos.asLong( - cell.x(), cell.y(), cell.z())); - } - } - } catch (TemplateBudgetExceeded ignored) { - warnVacuumBudget(target); - continue; - } - targetAnchors.forEach((column, anchor) -> anchors.merge( - column, anchor, NativeStructureSurfaceFitter::preferredVacuumAnchor)); - targetCaps.forEach((column, cap) -> columnCaps.merge( - column, cap, Math::min)); - foundationBases.addAll(targetFoundationBases); - occupiedCells.addAll(targetOccupiedCells); - } - return new VacuumFootprint( - Map.copyOf(anchors), Map.copyOf(columnCaps), - Set.copyOf(foundationBases), Set.copyOf(occupiedCells)); - } - - private static void addVacuumJunctionAnchors( - Map anchors, BoundingBox influenceArea, - PoolElementStructurePiece poolPiece) { - for (JigsawJunction junction : poolPiece.getJunctions()) { - if (junction.getSourceX() < influenceArea.minX() - || junction.getSourceX() > influenceArea.maxX() - || junction.getSourceZ() < influenceArea.minZ() - || junction.getSourceZ() > influenceArea.maxZ()) { - continue; - } - addVacuumAnchor(anchors, - junction.getSourceX(), junction.getSourceZ(), - new VacuumAnchor(junction.getSourceGroundY() - 1, 1)); - } - } - - private static void warnVacuumBudget( - NativeStructureTerrainIntegrator.TerrainTarget target) { - String structureId = target.structureId() == null - ? target.start().getStructure().getClass().getName() - : target.structureId(); - if (WARNED_VACUUM_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure VACUUM fitting for '" - + structureId + "' exceeded its bounded template budget; " - + "using jigsaw junction smoothing for oversized starts"); - } - } - - private static boolean intersectsHorizontally( - BoundingBox first, BoundingBox second) { - return first.maxX() >= second.minX() && first.minX() <= second.maxX() - && first.maxZ() >= second.minZ() && first.minZ() <= second.maxZ(); - } - - private static void addVacuumAnchor( - Map anchors, int x, int z, - VacuumAnchor candidate) { - anchors.merge(NativeStructureTemplateOccupancy.columnKey(x, z), candidate, - NativeStructureSurfaceFitter::preferredVacuumAnchor); - } - - private static VacuumAnchor preferredVacuumAnchor( - VacuumAnchor first, VacuumAnchor second) { - if (first.strength() != second.strength()) { - return first.strength() > second.strength() ? first : second; - } - return first.surfaceY() <= second.surfaceY() ? first : second; - } - - static SurfaceAnchor surfaceAnchor(BoundingBox bounds, int groundY, int strength) { - return new SurfaceAnchor(bounds.minX(), bounds.maxX(), bounds.minZ(), bounds.maxZ(), - groundY - 1, strength); - } - - static boolean requiresSurfaceTerrain(StructureStart start) { - return requiresSurfaceTerrain(new NativeStructureTerrainIntegrator.TerrainTarget( - null, start, new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))); - } - - static boolean requiresSurfaceTerrain(NativeStructureTerrainIntegrator.TerrainTarget target) { - if (target == null || target.terrain() == null) { - return false; - } - StructureStart start = target.start(); - if (start == null || !start.isValid()) { - return false; - } - IrisStructureTerrainMode mode = target.terrain().resolvedMode(); - return mode == IrisStructureTerrainMode.VACUUM - || requiresFlattenTerrain(target) - || requiresSourceSurfaceTerrain(target); - } - - private static boolean requiresSourceSurfaceTerrain( - NativeStructureTerrainIntegrator.TerrainTarget target) { - return target != null && target.terrain() != null - && target.start() != null && target.start().isValid() - && target.terrain().resolvedMode() == IrisStructureTerrainMode.SOURCE - && shouldPrepareSurfaceTerrain( - target.start().getStructure().terrainAdaptation(), - target.start().getStructure().step()); - } - - private static void fitSurfaceTerrain(WorldGenLevel world, BoundingBox area, - List anchors, - IntBinaryOperator surfaceHeight) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - int[] originalHeights = new int[width * depth]; - int[] targetHeights = new int[width * depth]; - boolean[] rigidBaseSupport = new boolean[width * depth]; - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - int originalY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(x, z))); - SurfaceResolution resolution = resolveSurface(anchors, x, z, originalY); - originalHeights[column] = originalY; - targetHeights[column] = Math.max(area.minY(), Math.min( - area.maxY(), resolution.targetY())); - rigidBaseSupport[column] = resolution.rigidBaseSupport(); - } - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - applySurfaceColumn(world, position, x, z, - originalHeights[column], targetHeights[column], area.minY(), area.maxY(), - rigidBaseSupport[column]); - } - } - } - - static boolean requiresFlattenTerrain(NativeStructureTerrainIntegrator.TerrainTarget target) { - if (target == null || target.terrain() == null - || target.terrain().resolvedMode() != IrisStructureTerrainMode.FLATTEN - || target.terrain().resolvedFlattenRange() == 0 - || target.start() == null || !target.start().isValid()) { - return false; - } - StructureStart start = target.start(); - return start.getStructure().step() == GenerationStep.Decoration.SURFACE_STRUCTURES - && start.getStructure().terrainAdaptation() != TerrainAdjustment.BURY - && start.getStructure().terrainAdaptation() != TerrainAdjustment.ENCAPSULATE - && !(start.getStructure() instanceof StrongholdStructure) - && !(start.getStructure() instanceof OceanMonumentStructure) - && !(start.getStructure() instanceof OceanRuinStructure) - && (!(start.getStructure() instanceof ShipwreckStructure) - || "minecraft:shipwreck_beached".equals(target.structureId())); - } - - private static FlattenFootprint collectFlattenFootprint( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight, int maximumTemplateCells) { - List anchors = new ArrayList<>(); - Map projectedSupport = new HashMap<>(); - Set seen = Collections.newSetFromMap(new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (!requiresFlattenTerrain(target) || !seen.add(target.start())) { - continue; - } - int radius = Math.max(0, Math.min(128, target.terrain().getHorizontalPadding())); - int range = target.terrain().resolvedFlattenRange(); - BoundingBox influenceArea = new BoundingBox( - area.minX() - radius, area.minY(), area.minZ() - radius, - area.maxX() + radius, area.maxY(), area.maxZ() + radius); - StructureStart start = target.start(); - if (!hasExposedFlattenPiece(start, surfaceHeight, range)) { - continue; - } - BoundingBox first = start.getPieces().getFirst().getBoundingBox(); - BlockPos reference = new BlockPos(first.getCenter().getX(), first.minY(), first.getCenter().getZ()); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - List sources = new ArrayList<>(); - Set projectedColumns = new HashSet<>(); - try { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!intersectsHorizontally(bounds, influenceArea) || !flattenPieceAllowed(start, piece)) { - continue; - } - if (piece instanceof PoolElementStructurePiece poolPiece) { - if (poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID) { - addProcessedSurfaceAnchors(world, influenceArea, poolPiece, reference, - StructureTemplatePool.Projection.RIGID, budget, sources); - } else { - addProjectedFlattenSupport(world, area, poolPiece, reference, budget, projectedColumns); - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - sources.add(new SurfaceAnchor(junction.getSourceX(), junction.getSourceX(), - junction.getSourceZ(), junction.getSourceZ(), junction.getSourceGroundY() - 1, 1)); - } - } else { - sources.add(new SurfaceAnchor(bounds.minX(), bounds.maxX(), bounds.minZ(), bounds.maxZ(), - flattenGroundY(start, piece, surfaceHeight), 2)); - } - } - } catch (TemplateBudgetExceeded ignored) { - String structureId = target.structureId() == null - ? start.getStructure().getClass().getName() : target.structureId(); - if (WARNED_FLATTEN_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure FLATTEN fitting for '" + structureId - + "' exceeded its bounded template budget; skipping this start"); - } - continue; - } - for (SurfaceAnchor source : sources) { - anchors.add(new FlattenAnchor(source, radius, range)); - } - for (long column : projectedColumns) { - projectedSupport.merge(column, range, Math::max); - } - } - return new FlattenFootprint(List.copyOf(anchors), Map.copyOf(projectedSupport)); - } - - private static void addProjectedFlattenSupport( - WorldGenLevel world, BoundingBox area, PoolElementStructurePiece piece, - BlockPos reference, TemplateCellBudget budget, Set columns) { - NativeStructureTemplateOccupancy.OccupancyResult occupancy = NativeStructureTemplateOccupancy.resolve( - world, piece, reference, area, () -> world.getLevel().getStructureTemplateManager(), - area::isInside, budget::consume); - for (Map.Entry entry : occupancy.cells().entrySet()) { - NativeStructureTemplateOccupancy.OccupancyCell cell = entry.getValue(); - if (cell.blocker() || !NativeStructureTemplateOccupancy.isSolidBase(cell.state())) { - continue; - } - BlockPos position = BlockPos.of(entry.getKey()); - columns.add(NativeStructureTemplateOccupancy.columnKey(position.getX(), position.getZ())); - } - } - - private static boolean flattenPieceAllowed(StructureStart start, StructurePiece piece) { - if (piece instanceof RuinedPortalPiece portal) { - RuinedPortalPiece.VerticalPlacement placement = NativeStructureReflection.ruinedPortalVerticalPlacement(portal); - return placement == RuinedPortalPiece.VerticalPlacement.ON_LAND_SURFACE - || placement == RuinedPortalPiece.VerticalPlacement.IN_NETHER; - } - if (piece instanceof IglooPieces.IglooPiece) { - int highest = Integer.MIN_VALUE; - for (StructurePiece candidate : start.getPieces()) { - if (candidate instanceof IglooPieces.IglooPiece) { - highest = Math.max(highest, candidate.getBoundingBox().minY()); - } - } - return piece.getBoundingBox().minY() == highest; - } - return true; - } - - static boolean hasExposedFlattenPiece( - StructureStart start, IntBinaryOperator surfaceHeight, int range) { - for (StructurePiece piece : start.getPieces()) { - if (!flattenPieceAllowed(start, piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int groundY = flattenGroundY(start, piece, surfaceHeight); - int roofY = piece instanceof IglooPieces.IglooPiece || piece instanceof SwampHutPiece - ? groundY + bounds.getYSpan() : bounds.maxY(); - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int surfaceY = surfaceHeight.applyAsInt(x, z); - if (surfaceY <= roofY && (long) groundY - surfaceY <= range) { - return true; - } - } - } - } - return false; - } - - private static int flattenGroundY( - StructureStart start, StructurePiece piece, IntBinaryOperator surfaceHeight) { - BoundingBox bounds = piece.getBoundingBox(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - return bounds.minY() + poolPiece.getGroundLevelDelta() - 1; - } - if (piece instanceof IglooPieces.IglooPiece igloo) { - BlockPos entrance = igloo.templatePosition().offset( - StructureTemplate.calculateRelativePosition(igloo.placeSettings(), new BlockPos(3, 0, 0))); - return surfaceHeight.applyAsInt(entrance.getX(), entrance.getZ()); - } - if (piece instanceof SwampHutPiece || start.getStructure() instanceof ShipwreckStructure) { - long sum = 0; - int minimum = Integer.MAX_VALUE; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int surfaceY = surfaceHeight.applyAsInt(x, z); - sum += surfaceY; - minimum = Math.min(minimum, surfaceY); - } - } - return piece instanceof SwampHutPiece - ? (int) Math.floorDiv(sum, (long) bounds.getXSpan() * bounds.getZSpan()) : minimum; - } - if (piece instanceof WoodlandMansionPieces.WoodlandMansionPiece) { - return start.getBoundingBox().minY() - 1; - } - return bounds.minY() - 1; - } - - static FlattenResolution resolveFlattenSurface( - List anchors, int x, int z, int originalY) { - FlattenAnchor selected = null; - long weightedY = 0; - long totalWeight = 0; - long maximumInfluence = 0; - int range = 0; - for (FlattenAnchor anchor : anchors) { - SurfaceAnchor source = anchor.source(); - int outX = IrisObjectVacuum.outset(x, source.minX(), source.maxX()); - int outZ = IrisObjectVacuum.outset(z, source.minZ(), source.maxZ()); - boolean containsColumn = outX == 0 && outZ == 0; - if (containsColumn && source.strength() > 1) { - if (selected == null || precedes(source, selected.source())) { - selected = anchor; - } - continue; - } - double distance = Math.sqrt((double) outX * outX + (double) outZ * outZ); - if (!containsColumn && (anchor.radius() == 0 || distance >= anchor.radius())) { - continue; - } - double progress = containsColumn ? 0D : distance / anchor.radius(); - double factor = 1D - progress * progress * (3D - 2D * progress); - long influence = Math.round(factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - long weight = influence * source.strength(); - int targetY = originalY + Math.max(-anchor.range(), Math.min(anchor.range(), source.meetY() - originalY)); - weightedY += weight * targetY; - totalWeight += weight; - maximumInfluence = Math.max(maximumInfluence, influence); - range = Math.max(range, anchor.range()); - } - if (selected != null) { - int targetY = originalY + Math.max(-selected.range(), - Math.min(selected.range(), selected.source().meetY() - originalY)); - return new FlattenResolution(targetY, selected.range()); - } - if (totalWeight == 0) { - return new FlattenResolution(originalY, 0); - } - int targetY = blendSurfaceTarget(originalY, weightedY / (double) totalWeight, - maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE); - return new FlattenResolution(targetY, range); - } - - private static Map fitFlattenTerrain( - WorldGenLevel world, BoundingBox area, FlattenFootprint footprint, - IntBinaryOperator surfaceHeight) { - Map heights = new HashMap<>(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int x = area.minX(); x <= area.maxX(); x++) { - for (int z = area.minZ(); z <= area.maxZ(); z++) { - int originalY = Math.max(area.minY(), Math.min(area.maxY(), surfaceHeight.applyAsInt(x, z))); - long column = NativeStructureTemplateOccupancy.columnKey(x, z); - FlattenResolution resolution = resolveFlattenSurface(footprint.anchors(), x, z, originalY); - int projectedRange = footprint.projectedSupport().getOrDefault(column, 0); - int range = Math.max(resolution.range(), projectedRange); - if (range == 0 || originalY < area.maxY() - && !world.getBlockState(position.set(x, originalY + 1, z)).getFluidState().isEmpty()) { - continue; - } - int targetY = Math.max(area.minY(), Math.min(area.maxY(), resolution.targetY())); - int minimumY = Math.max(area.minY(), targetY - range); - applySurfaceColumn(world, position, x, z, originalY, targetY, - minimumY, Math.min(area.maxY(), originalY + range)); - fillFlattenFoundation(world, position, x, z, targetY, minimumY); - heights.put(column, targetY + 1); - } - } - return Map.copyOf(heights); - } - - private static void fillFlattenFoundation( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int targetY, int minimumY) { - SurfaceMaterials materials = resolveSurfaceMaterials(world, position, x, z, targetY, minimumY); - if (!isTerrainBlock(world.getBlockState(position.set(x, targetY, z)))) { - world.setBlock(position, materials.surface(), 2); - } - for (int y = targetY - 1; y >= minimumY; y--) { - BlockState existing = world.getBlockState(position.set(x, y, z)); - if (!existing.getFluidState().isEmpty() - || NativeStructureVegetationClearer.isTreeBlock(existing)) { - break; - } - if (!isTerrainBlock(existing)) { - world.setBlock(position, materials.subsurface(), 2); - } - } - } - - private static void fitVacuumTerrain( - WorldGenLevel world, BoundingBox area, VacuumFootprint footprint, - IntBinaryOperator surfaceHeight) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - int[] originalHeights = new int[width * depth]; - int[] targetHeights = new int[width * depth]; - boolean[] rigidBaseSupport = new boolean[width * depth]; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - int nominalY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(x, z))); - long columnKey = NativeStructureTemplateOccupancy.columnKey(x, z); - VacuumAnchor local = footprint.anchors().get(columnKey); - Integer cap = footprint.columnCaps().get(columnKey); - boolean suppressed = local != null && local.strength() > 1 - && cap != null && cap < local.surfaceY(); - int scanStartY = local != null && local.strength() > 1 && !suppressed - ? Math.max(area.minY(), Math.min(area.maxY(), local.surfaceY())) - : nominalY; - int originalY = resolveVacuumSurfaceHeight( - world, position, x, z, scanStartY, area.minY()); - SurfaceResolution resolution = resolveVacuumSurface( - footprint, x, z, originalY); - originalHeights[column] = originalY; - targetHeights[column] = Math.max(originalY, Math.min( - area.maxY(), resolution.targetY())); - rigidBaseSupport[column] = resolution.rigidBaseSupport() - && targetHeights[column] > originalY; - } - } - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - applySurfaceColumn(world, position, x, z, - originalHeights[column], targetHeights[column], - area.minY(), area.maxY(), rigidBaseSupport[column]); - } - } - } - - private static int resolveVacuumSurfaceHeight( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int nominalY, int worldMinY) { - for (int y = nominalY; y >= worldMinY; y--) { - if (isTerrainBlock(world.getBlockState(position.set(x, y, z)))) { - return y; - } - } - return nominalY; - } - - private static int findVacuumFoundationSupport( - WorldGenLevel world, BoundingBox area, - BlockPos.MutableBlockPos position, int x, int startY, int z) { - if (startY < area.minY()) { - return Integer.MIN_VALUE; - } - int maximumY = Math.min(area.maxY(), startY); - for (int y = maximumY; y >= area.minY(); y--) { - BlockState state = world.getBlockState(position.set(x, y, z)); - if (isTerrainBlock(state)) { - return y; - } - if (!state.isAir() && state.getFluidState().isEmpty()) { - return Integer.MIN_VALUE; - } - } - return Integer.MIN_VALUE; - } - - private static boolean intersectsVacuumOccupancy( - Set occupiedCells, int x, int z, int minimumY, int maximumY) { - for (int y = minimumY; y <= maximumY; y++) { - if (occupiedCells.contains(BlockPos.asLong(x, y, z))) { - return true; - } - } - return false; - } - - private static SurfaceResolution resolveVacuumSurface( - VacuumFootprint footprint, int worldX, int worldZ, int originalY) { - long column = NativeStructureTemplateOccupancy.columnKey(worldX, worldZ); - Integer cap = footprint.columnCaps().get(column); - VacuumAnchor local = footprint.anchors().get(column); - if (local != null && local.strength() > 1) { - int localTargetY = cap == null - ? local.surfaceY() : Math.min(local.surfaceY(), cap); - if (localTargetY <= originalY) { - return new SurfaceResolution(originalY, false); - } - return new SurfaceResolution( - localTargetY, localTargetY == local.surfaceY()); - } - long totalInfluence = 0L; - long weightedSurfaceY = 0L; - long maximumInfluence = 0L; - int radiusSquared = SURFACE_TERRAIN_RADIUS * SURFACE_TERRAIN_RADIUS; - for (int offsetX = -SURFACE_TERRAIN_RADIUS; - offsetX <= SURFACE_TERRAIN_RADIUS; offsetX++) { - for (int offsetZ = -SURFACE_TERRAIN_RADIUS; - offsetZ <= SURFACE_TERRAIN_RADIUS; offsetZ++) { - int horizontalDistanceSquared = offsetX * offsetX + offsetZ * offsetZ; - if (horizontalDistanceSquared > radiusSquared) { - continue; - } - VacuumAnchor anchor = footprint.anchors().get( - NativeStructureTemplateOccupancy.columnKey( - worldX + offsetX, worldZ + offsetZ)); - if (anchor == null || anchor.surfaceY() <= originalY) { - continue; - } - int verticalDistance = Math.abs(anchor.surfaceY() + 1 - originalY); - long distanceSquared = horizontalDistanceSquared - + (long) verticalDistance * verticalDistance; - double factor = 0D; - if (distanceSquared <= radiusSquared) { - factor = surfaceFactor(Math.sqrt(distanceSquared)); - } - if (anchor.strength() > 1 - && verticalDistance > SURFACE_TERRAIN_RADIUS) { - double rescueProgress = Math.min(1D, - (verticalDistance - SURFACE_TERRAIN_RADIUS) - / (double) SURFACE_TERRAIN_RADIUS); - double rescueWeight = rescueProgress * rescueProgress - * (3D - 2D * rescueProgress); - factor = Math.max(factor, - surfaceFactor(Math.sqrt(horizontalDistanceSquared)) - * rescueWeight); - } - long influence = Math.round( - factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - if (influence <= 0L) { - continue; - } - long weightedInfluence = influence * Math.max(1, anchor.strength()); - totalInfluence += weightedInfluence; - weightedSurfaceY += weightedInfluence * anchor.surfaceY(); - maximumInfluence = Math.max(maximumInfluence, influence); - } - } - if (totalInfluence == 0L) { - return new SurfaceResolution(originalY, false); - } - double blendedSurfaceY = weightedSurfaceY / (double) totalInfluence; - double factor = maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE; - int targetY = (int) Math.round( - originalY + ((blendedSurfaceY - originalY) * factor)); - if (cap != null) { - targetY = Math.min(targetY, cap); - } - return new SurfaceResolution(Math.max(originalY, targetY), false); - } - - private static double surfaceFactor(double distance) { - return Math.pow(1D - distance / SURFACE_TERRAIN_RADIUS, - SURFACE_TERRAIN_FALLOFF); - } - - static void applySurfaceColumn(WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int targetY, - int worldMinY, int worldMaxY) { - applySurfaceColumn(world, position, x, z, originalY, targetY, - worldMinY, worldMaxY, false); - } - - static void applySurfaceColumn(WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int targetY, - int worldMinY, int worldMaxY, - boolean requireRigidBaseSupport) { - if (targetY == originalY) { - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY); - } - return; - } - SurfaceMaterials materials = resolveSurfaceMaterials(world, position, x, z, originalY, worldMinY); - if (targetY < originalY) { - BlockState clearedState = clearSurfaceDecorationAndResolveFill( - world, position, x, z, originalY, worldMaxY); - for (int y = originalY; y > targetY; y--) { - world.setBlock(position.set(x, y, z), clearedState, 2); - } - world.setBlock(position.set(x, targetY, z), materials.surface(), 2); - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - return; - } - for (int y = originalY + 1; y < targetY; y++) { - position.set(x, y, z); - if (!NativeStructureVegetationClearer.isTreeBlock(world.getBlockState(position))) { - world.setBlock(position, materials.subsurface(), 2); - } - } - position.set(x, targetY, z); - if (!NativeStructureVegetationClearer.isTreeBlock(world.getBlockState(position))) { - world.setBlock(position, materials.surface(), 2); - } - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - } - - private static void ensureRigidBaseTerrain(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int targetY, int worldMinY) { - int supportY = targetY - 1; - boolean targetIsTerrain = isTerrainBlock(world.getBlockState(position.set(x, targetY, z))); - boolean supportIsTerrain = supportY < worldMinY - || isTerrainBlock(world.getBlockState(position.set(x, supportY, z))); - if (targetIsTerrain && supportIsTerrain) { - return; - } - SurfaceMaterials materials = resolveSurfaceMaterials( - world, position, x, z, targetY, worldMinY); - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - - private static void ensureRigidBaseTerrain(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int targetY, int worldMinY, - SurfaceMaterials materials) { - int supportY = targetY - 1; - position.set(x, targetY, z); - if (!isTerrainBlock(world.getBlockState(position))) { - world.setBlock(position, materials.surface(), 2); - } - if (supportY < worldMinY) { - return; - } - position.set(x, supportY, z); - if (!isTerrainBlock(world.getBlockState(position))) { - world.setBlock(position, materials.subsurface(), 2); - } - } - - private static BlockState clearSurfaceDecorationAndResolveFill( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int worldMaxY) { - BlockState air = Blocks.AIR.defaultBlockState(); - for (int y = originalY + 1; y <= worldMaxY; y++) { - BlockState state = world.getBlockState(position.set(x, y, z)); - if (state.isAir()) { - return air; - } - if (!state.getFluidState().isEmpty()) { - BlockState fluid = state.getFluidState().createLegacyBlock(); - if (state != fluid) { - world.setBlock(position, fluid, 2); - } - return fluid; - } - if (state.isSolid() && !state.is(Blocks.CACTUS) - || NativeStructureVegetationClearer.isTreeBlock(state)) { - return air; - } - world.setBlock(position, air, 2); - } - return air; - } - - private static SurfaceMaterials resolveSurfaceMaterials(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int originalY, - int worldMinY) { - BlockState surface = world.getBlockState(position.set(x, originalY, z)); - BlockState subsurface = null; - for (int y = originalY - 1; y >= worldMinY; y--) { - BlockState candidate = world.getBlockState(position.set(x, y, z)); - if (isTerrainBlock(candidate)) { - subsurface = candidate; - break; - } - } - if (subsurface == null) { - subsurface = isTerrainBlock(surface) ? surface : Blocks.STONE.defaultBlockState(); - } - if (!isTerrainBlock(surface)) { - surface = subsurface; - } - return new SurfaceMaterials(surface, subsurface); - } - - private static boolean isTerrainBlock(BlockState state) { - return state.isSolid() && !NativeStructureVegetationClearer.isTreeBlock(state); - } - - record SurfaceAnchor(int minX, int maxX, int minZ, int maxZ, int meetY, int strength) { - } - - record FlattenAnchor(SurfaceAnchor source, int radius, int range) { - } - - record FlattenResolution(int targetY, int range) { - } - - private record FlattenFootprint(List anchors, Map projectedSupport) { - } - - private record SurfaceMaterials(BlockState surface, BlockState subsurface) { - } - - private record SurfaceResolution(int targetY, boolean rigidBaseSupport) { - } - - private record VacuumFootprint( - Map anchors, Map columnCaps, - Set foundationBases, Set occupiedCells) { - } - - private record VacuumAnchor(int surfaceY, int strength) { - } - - public record SurfaceTerrainPlan( - List foundationBases, Set occupiedCells, Map flattenedHeights) { - private static final SurfaceTerrainPlan EMPTY = - new SurfaceTerrainPlan(List.of(), Set.of(), Map.of()); - - public SurfaceTerrainPlan { - foundationBases = List.copyOf(foundationBases); - occupiedCells = Set.copyOf(occupiedCells); - flattenedHeights = Map.copyOf(flattenedHeights); - } - - private static SurfaceTerrainPlan empty() { - return EMPTY; - } - - private static SurfaceTerrainPlan create( - VacuumFootprint vacuum, BoundingBox area, Map flattenedHeights) { - Set packedBases = vacuum.foundationBases(); - if (packedBases.isEmpty() && flattenedHeights.isEmpty()) { - return EMPTY; - } - List bases = new ArrayList<>(packedBases.size()); - Set columns = new HashSet<>(); - for (long packedBase : packedBases) { - BlockPos base = BlockPos.of(packedBase); - if (!area.isInside(base)) { - continue; - } - bases.add(packedBase); - columns.add(NativeStructureTemplateOccupancy.columnKey( - base.getX(), base.getZ())); - } - if (bases.isEmpty() && flattenedHeights.isEmpty()) { - return EMPTY; - } - bases.sort((first, second) -> { - int yOrder = Integer.compare( - BlockPos.of(first).getY(), BlockPos.of(second).getY()); - return yOrder == 0 ? Long.compare(first, second) : yOrder; - }); - Set occupancy = new HashSet<>(); - for (long packedCell : vacuum.occupiedCells()) { - BlockPos cell = BlockPos.of(packedCell); - if (columns.contains(NativeStructureTemplateOccupancy.columnKey( - cell.getX(), cell.getZ()))) { - occupancy.add(packedCell); - } - } - return new SurfaceTerrainPlan( - bases, occupancy, flattenedHeights); - } - - public void primeHeightmaps(ChunkAccess chunk) { - if (flattenedHeights.isEmpty()) { - return; - } - Heightmap surface = chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG); - Heightmap floor = chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG); - WorldgenTerrainHeightmaps.primeTerrain(chunk, - (x, z) -> flattenedHeights.getOrDefault(NativeStructureTemplateOccupancy.columnKey(x, z), - surface.getFirstAvailable(x & 15, z & 15)), - (x, z) -> flattenedHeights.getOrDefault(NativeStructureTemplateOccupancy.columnKey(x, z), - floor.getFirstAvailable(x & 15, z & 15))); - } - } - - private static final class TemplateCellBudget { - private final int maximum; - private int consumed; - - private TemplateCellBudget(int maximum) { - if (maximum < 0) { - throw new IllegalArgumentException( - "Native structure surface template budget cannot be negative"); - } - this.maximum = maximum; - } - - private void consume(int amount) { - if (amount < 0 || consumed > maximum - amount) { - throw TemplateBudgetExceeded.INSTANCE; - } - consumed += amount; - } - } - - private static final class TemplateBudgetExceeded extends RuntimeException { - private static final TemplateBudgetExceeded INSTANCE = - new TemplateBudgetExceeded(); - - private TemplateBudgetExceeded() { - super(null, null, false, false); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceSupportBuilder.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceSupportBuilder.java deleted file mode 100644 index 627adb3dd..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureSurfaceSupportBuilder.java +++ /dev/null @@ -1,305 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; - -final class NativeStructureSurfaceSupportBuilder { - private static final int MAX_BATCH_CELLS = 524_288; - private static final int MAX_BRIDGE_SPAN = 2; - - private NativeStructureSurfaceSupportBuilder() { - } - - static Set bridgeRigidPieceSupport( - WorldGenLevel world, BoundingBox area, - List targets, - Supplier templates) { - if (targets == null || targets.isEmpty()) { - return Set.of(); - } - Map planned; - try { - BatchCellBudget budget = new BatchCellBudget(); - List requests = new ArrayList<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (target == null - || !NativeStructureSurfaceFitter.requiresSurfaceTerrain(target) - || target.terrain().resolvedMode() == IrisStructureTerrainMode.FLATTEN - && !NativeStructureSurfaceFitter.hasExposedFlattenPiece( - target.start(), (x, z) -> world.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1, - target.terrain().resolvedFlattenRange()) - || !seenStarts.add(target.start())) { - continue; - } - collectTargetRequests( - world, area, target, templates, budget, requests); - } - planned = planWrites(world, area, requests, budget); - } catch (BatchBudgetExceeded ignored) { - return Set.of(); - } - if (planned.isEmpty()) { - return Set.of(); - } - List> ordered = new ArrayList<>(planned.entrySet()); - ordered.sort(Map.Entry.comparingByKey()); - Set written = new HashSet<>(); - for (Map.Entry entry : ordered) { - BlockPos position = BlockPos.of(entry.getKey()); - BlockState existing = world.getBlockState(position); - if (!existing.isSolid() - && !NativeStructureVegetationClearer.isTreeBlock(existing) - && existing.getFluidState().isEmpty() - && world.setBlock(position, entry.getValue(), 2)) { - written.add(entry.getKey()); - } - } - return Set.copyOf(written); - } - - private static void collectTargetRequests( - WorldGenLevel world, BoundingBox area, - NativeStructureTerrainIntegrator.TerrainTarget target, - Supplier templates, - BatchCellBudget budget, List requests) { - if (!NativeStructureSurfaceFitter.requiresSurfaceTerrain(target)) { - return; - } - List anchors = rigidAnchors(target.start()); - if (anchors.size() < 2) { - return; - } - BlockPos referencePos = referencePosition(target.start()); - for (RigidAnchor upper : anchors) { - Map lowerMeets = candidateLowerMeets( - area, upper, anchors, budget); - if (lowerMeets.isEmpty()) { - continue; - } - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, upper.piece(), referencePos, area, templates, - position -> retain(position, area, lowerMeets), budget::consume); - if (!occupancy.resolved()) { - continue; - } - Map lowest = - NativeStructureTemplateOccupancy.lowestCells(occupancy.cells()); - for (Map.Entry entry - : lowest.entrySet()) { - NativeStructureTemplateOccupancy.LowestCell cell = entry.getValue(); - Integer lowerMeet = lowerMeets.get(entry.getKey()); - if (lowerMeet == null || cell.occupancy().blocker() - || !NativeStructureTemplateOccupancy.isSolidBase( - cell.occupancy().state()) - || cell.y() - lowerMeet < 2 - || cell.y() - lowerMeet > MAX_BRIDGE_SPAN) { - continue; - } - budget.consume(1); - requests.add(new SupportRequest( - cell.x(), cell.z(), lowerMeet, cell.y())); - } - } - } - - private static List rigidAnchors(StructureStart start) { - List anchors = new ArrayList<>(); - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() - != StructureTemplatePool.Projection.RIGID) { - continue; - } - BoundingBox bounds = poolPiece.getBoundingBox(); - int meetY = bounds.minY() + poolPiece.getGroundLevelDelta() - 1; - anchors.add(new RigidAnchor(poolPiece, bounds, meetY)); - } - return List.copyOf(anchors); - } - - private static Map candidateLowerMeets( - BoundingBox area, RigidAnchor upper, List anchors, - BatchCellBudget budget) { - Map lowerMeets = new HashMap<>(); - for (RigidAnchor lower : anchors) { - budget.consume(1); - int span = upper.meetY() - lower.meetY(); - if (lower.piece() == upper.piece() || span < 1 - || span > MAX_BRIDGE_SPAN - || lower.meetY() < area.minY() || lower.meetY() > area.maxY()) { - continue; - } - int minimumX = Math.max(area.minX(), - Math.max(upper.bounds().minX(), lower.bounds().minX())); - int maximumX = Math.min(area.maxX(), - Math.min(upper.bounds().maxX(), lower.bounds().maxX())); - int minimumZ = Math.max(area.minZ(), - Math.max(upper.bounds().minZ(), lower.bounds().minZ())); - int maximumZ = Math.min(area.maxZ(), - Math.min(upper.bounds().maxZ(), lower.bounds().maxZ())); - if (minimumX > maximumX || minimumZ > maximumZ) { - continue; - } - for (int x = minimumX; x <= maximumX; x++) { - for (int z = minimumZ; z <= maximumZ; z++) { - budget.consume(1); - lowerMeets.merge(columnKey(x, z), lower.meetY(), Math::max); - } - } - } - return lowerMeets; - } - - private static boolean retain( - BlockPos position, BoundingBox area, Map lowerMeets) { - if (!area.isInside(position)) { - return false; - } - Integer lowerMeet = lowerMeets.get(columnKey(position.getX(), position.getZ())); - return lowerMeet != null && position.getY() <= lowerMeet + MAX_BRIDGE_SPAN; - } - - private static Map planWrites( - WorldGenLevel world, BoundingBox area, - List requests, BatchCellBudget budget) { - List eligible = new ArrayList<>(); - Set materialKeys = new HashSet<>(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (SupportRequest request : requests) { - BlockState lowerTerrain = world.getBlockState(position.set( - request.x(), request.lowerMeetY(), request.z())); - if (!isTerrainSupport(lowerTerrain) - || !gapIsClear(world, area, position, request, budget)) { - continue; - } - eligible.add(request); - materialKeys.add(new MaterialKey( - request.x(), request.z(), request.lowerMeetY())); - } - Map materials = new HashMap<>(); - for (MaterialKey key : materialKeys) { - materials.put(key, resolveSupportState( - world, position, key.x(), key.z(), key.lowerMeetY(), budget)); - } - Map planned = new HashMap<>(); - Set conflicts = new HashSet<>(); - eligible.sort(Comparator.comparingInt(SupportRequest::x) - .thenComparingInt(SupportRequest::z) - .thenComparingInt(SupportRequest::lowerMeetY) - .thenComparingInt(SupportRequest::baseY)); - for (SupportRequest request : eligible) { - MaterialKey materialKey = new MaterialKey( - request.x(), request.z(), request.lowerMeetY()); - BlockState material = materials.get(materialKey); - for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) { - budget.consume(1); - long positionKey = BlockPos.asLong(request.x(), y, request.z()); - if (conflicts.contains(positionKey)) { - continue; - } - BlockState existing = planned.putIfAbsent(positionKey, material); - if (existing != null && !existing.equals(material)) { - planned.remove(positionKey); - conflicts.add(positionKey); - } - } - } - return planned; - } - - private static boolean gapIsClear( - WorldGenLevel world, BoundingBox area, BlockPos.MutableBlockPos position, - SupportRequest request, BatchCellBudget budget) { - for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) { - budget.consume(1); - if (!area.isInside(position.set(request.x(), y, request.z()))) { - return false; - } - BlockState state = world.getBlockState(position); - if (state.isSolid() || NativeStructureVegetationClearer.isTreeBlock(state) - || !state.getFluidState().isEmpty()) { - return false; - } - } - return true; - } - - private static BlockState resolveSupportState( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int lowerMeetY, BatchCellBudget budget) { - budget.consume(1); - BlockState below = world.getBlockState(position.set(x, lowerMeetY - 1, z)); - if (isTerrainSupport(below)) { - return below; - } - return world.getBlockState(position.set(x, lowerMeetY, z)); - } - - private static boolean isTerrainSupport(BlockState state) { - return state.isSolid() - && !NativeStructureVegetationClearer.isTreeBlock(state) - && state.getFluidState().isEmpty(); - } - - private static BlockPos referencePosition(StructureStart start) { - BoundingBox bounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos center = bounds.getCenter(); - return new BlockPos(center.getX(), bounds.minY(), center.getZ()); - } - - private static long columnKey(int x, int z) { - return (long) x << 32 ^ z & 0xffffffffL; - } - - private record RigidAnchor( - PoolElementStructurePiece piece, BoundingBox bounds, int meetY) { - } - - private record SupportRequest(int x, int z, int lowerMeetY, int baseY) { - } - - private record MaterialKey(int x, int z, int lowerMeetY) { - } - - private static final class BatchCellBudget { - private int consumed; - - private void consume(int amount) { - if (amount < 0 || consumed > MAX_BATCH_CELLS - amount) { - throw BatchBudgetExceeded.INSTANCE; - } - consumed += amount; - } - } - - private static final class BatchBudgetExceeded extends RuntimeException { - private static final BatchBudgetExceeded INSTANCE = new BatchBudgetExceeded(); - - private BatchBudgetExceeded() { - super(null, null, false, false); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplateOccupancy.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplateOccupancy.java deleted file mode 100644 index e399a91b4..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplateOccupancy.java +++ /dev/null @@ -1,165 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -final class NativeStructureTemplateOccupancy { - private static final OccupancyCell BLOCKER = new OccupancyCell(null, true); - - private NativeStructureTemplateOccupancy() { - } - - static OccupancyResult resolve( - WorldGenLevel world, PoolElementStructurePiece piece, - BlockPos referencePosition, BoundingBox processingArea, - Supplier templates, - PositionFilter filter, CellBudget budget) { - List leaves = new ArrayList<>(); - if (!flattenSingles(piece.getElement(), leaves)) { - return new OccupancyResult(false, Map.of()); - } - List placements = new ArrayList<>(leaves.size()); - Map occupancy = new HashMap<>(); - for (SinglePoolElement leaf : leaves) { - StructurePlaceSettings settings = NativeStructureReflection.resolvePlacementSettings( - leaf, piece, processingArea); - StructureTemplate template = NativeStructureReflection.resolveTemplate(leaf, templates); - List rawBlocks = - NativeStructureReflection.resolveTemplateBlocks( - template, settings, piece.getPosition()); - budget.consume(rawBlocks.size()); - placements.add(new LeafPlacement(settings, rawBlocks, template)); - for (StructureTemplate.StructureBlockInfo raw : rawBlocks) { - BlockPos position = piece.getPosition().offset( - StructureTemplate.calculateRelativePosition(settings, raw.pos())); - if (filter.retain(position)) { - occupancy.put(position.asLong(), BLOCKER); - } - } - } - for (LeafPlacement placement : placements) { - List processed = - NativeStructureReflection.processTemplateBlocks( - world, piece.getPosition(), referencePosition, - placement.settings(), placement.rawBlocks(), placement.template()); - budget.consume(processed.size()); - for (StructureTemplate.StructureBlockInfo block : processed) { - BlockPos position = block.pos(); - if (!filter.retain(position)) { - continue; - } - BlockState state = block.state() - .mirror(placement.settings().getMirror()) - .rotate(placement.settings().getRotation()); - occupancy.put(position.asLong(), new OccupancyCell(state, false)); - } - } - return new OccupancyResult(true, occupancy); - } - - static Map lowestCells(Map occupancy) { - Map lowest = new HashMap<>(); - for (Map.Entry entry : occupancy.entrySet()) { - OccupancyCell cell = entry.getValue(); - BlockPos position = BlockPos.of(entry.getKey()); - long column = columnKey(position.getX(), position.getZ()); - LowestCell current = lowest.get(column); - if (current == null || position.getY() < current.y()) { - lowest.put(column, new LowestCell( - position.getX(), position.getY(), position.getZ(), cell)); - } - } - return lowest; - } - - static Map lowestProcessedSolidCells(Map occupancy) { - Map lowest = new HashMap<>(); - for (Map.Entry entry : occupancy.entrySet()) { - OccupancyCell cell = entry.getValue(); - if (cell.blocker() || !isSolidBase(cell.state())) { - continue; - } - BlockPos position = BlockPos.of(entry.getKey()); - long column = columnKey(position.getX(), position.getZ()); - LowestCell current = lowest.get(column); - if (current == null || position.getY() < current.y()) { - lowest.put(column, new LowestCell( - position.getX(), position.getY(), position.getZ(), cell)); - } - } - return lowest; - } - - static boolean isSolidBase(BlockState state) { - return state != null && state.isSolid() - && !state.is(Blocks.STRUCTURE_VOID) - && !state.is(Blocks.JIGSAW) - && state.getFluidState().isEmpty(); - } - - static long columnKey(int x, int z) { - return (long) x << 32 ^ z & 0xffffffffL; - } - - private static boolean flattenSingles( - StructurePoolElement element, List leaves) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - if (!flattenSingles(child, leaves)) { - return false; - } - } - return true; - } - if (element == EmptyPoolElement.INSTANCE) { - return true; - } - if (element instanceof SinglePoolElement singleElement) { - leaves.add(singleElement); - return true; - } - return false; - } - - @FunctionalInterface - interface PositionFilter { - boolean retain(BlockPos position); - } - - @FunctionalInterface - interface CellBudget { - void consume(int amount); - } - - record OccupancyResult(boolean resolved, Map cells) { - } - - record OccupancyCell(BlockState state, boolean blocker) { - } - - record LowestCell(int x, int y, int z, OccupancyCell occupancy) { - } - - private record LeafPlacement( - StructurePlaceSettings settings, - List rawBlocks, - StructureTemplate template) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplatePoolBounds.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplatePoolBounds.java deleted file mode 100644 index 815199590..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTemplatePoolBounds.java +++ /dev/null @@ -1,228 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.FeaturePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.pools.alias.DirectPoolAlias; -import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasBinding; -import net.minecraft.world.level.levelgen.structure.pools.alias.RandomGroupPoolAlias; -import net.minecraft.world.level.levelgen.structure.pools.alias.RandomPoolAlias; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -public final class NativeStructureTemplatePoolBounds { - private NativeStructureTemplatePoolBounds() { - } - - public static int sourceHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source) { - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - return sourceHorizontalSpan( - templateManager, - source.getStartPool(), - source.getPoolAliases(), - key -> pools.getValue(key.identifier())); - } - - public static int horizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - String templatePoolKey) { - Identifier identifier = Identifier.tryParse(templatePoolKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered template pool key: " + templatePoolKey); - } - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - StructureTemplatePool pool = pools.getValue(identifier); - if (pool == null) { - throw new IllegalArgumentException("Registered template pool does not exist: " + templatePoolKey); - } - return horizontalSpan(pool, templateManager); - } - - public static int horizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source, - String templatePoolKey) { - Identifier identifier = Identifier.tryParse(templatePoolKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered template pool key: " + templatePoolKey); - } - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - ResourceKey startPoolKey = ResourceKey.create( - Registries.TEMPLATE_POOL, identifier); - StructureTemplatePool startPool = pools.getValue(identifier); - if (startPool == null) { - throw new IllegalArgumentException("Registered template pool does not exist: " + templatePoolKey); - } - return sourceHorizontalSpan( - templateManager, - startPool, - startPoolKey, - source.getPoolAliases(), - key -> pools.getValue(key.identifier())); - } - - static int sourceHorizontalSpan(StructureTemplateManager templateManager, - Holder startPool, - List aliases, - Function, StructureTemplatePool> poolLookup) { - return sourceHorizontalSpan(templateManager, startPool.value(), - startPool.unwrapKey().orElse(null), aliases, poolLookup); - } - - static int sourceHorizontalSpan(StructureTemplateManager templateManager, - StructureTemplatePool startPool, - ResourceKey startPoolKey, - List aliases, - Function, StructureTemplatePool> poolLookup) { - int maximumSpan = directHorizontalSpan(startPool, templateManager); - if (startPoolKey == null) { - return maximumSpan; - } - Set> targets = new HashSet<>(); - for (PoolAliasBinding binding : aliases) { - collectTargets(binding, startPoolKey, targets); - } - for (ResourceKey target : targets) { - StructureTemplatePool targetPool = poolLookup.apply(target); - if (targetPool == null) { - throw new IllegalStateException("Jigsaw start-pool alias target does not exist: " - + target.identifier()); - } - maximumSpan = Math.max(maximumSpan, directHorizontalSpan(targetPool, templateManager)); - } - return maximumSpan; - } - - private static void collectTargets(PoolAliasBinding binding, - ResourceKey startPoolKey, - Set> targets) { - if (binding instanceof DirectPoolAlias direct) { - if (direct.alias().equals(startPoolKey)) { - targets.add(direct.target()); - } - return; - } - if (binding instanceof RandomPoolAlias random) { - if (random.alias().equals(startPoolKey)) { - random.targets().unwrap().forEach(entry -> targets.add(entry.value())); - } - return; - } - if (binding instanceof RandomGroupPoolAlias group) { - group.groups().unwrap().forEach(entry -> entry.value().forEach( - nested -> collectTargets(nested, startPoolKey, targets))); - return; - } - throw new IllegalStateException("Unsupported jigsaw pool alias type: " - + binding.getClass().getName()); - } - - static int horizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager) { - Set visited = Collections.newSetFromMap(new IdentityHashMap<>()); - return horizontalSpan(pool, templateManager, visited); - } - - private static int horizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager, - Set visited) { - if (!visited.add(pool)) { - return 0; - } - int maximumSpan = directHorizontalSpan(pool, templateManager); - Holder fallback = pool.getFallback(); - if (fallback == null || !fallback.isBound()) { - throw new IllegalStateException("Template pool has an unresolved fallback"); - } - return Math.max(maximumSpan, horizontalSpan(fallback.value(), templateManager, visited)); - } - - private static int directHorizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager) { - int maximumSpan = 0; - for (Pair entry : pool.getTemplates()) { - StructurePoolElement element = entry.getFirst(); - validateElement(element, templateManager); - maximumSpan = Math.max(maximumSpan, horizontalSpan(element, templateManager)); - } - return maximumSpan; - } - - private static void validateElement(StructurePoolElement element, - StructureTemplateManager templateManager) { - if (element == EmptyPoolElement.INSTANCE || element instanceof FeaturePoolElement) { - return; - } - if (element instanceof SinglePoolElement single) { - Identifier template; - try { - template = single.getTemplateLocation(); - } catch (RuntimeException inlineTemplate) { - return; - } - if (templateManager.get(template).isEmpty()) { - throw new IllegalStateException("Template pool element does not resolve a structure template: " - + template); - } - return; - } - if (element instanceof ListPoolElement list) { - for (StructurePoolElement child : list.getElements()) { - validateElement(child, templateManager); - } - return; - } - } - - private static int horizontalSpan(StructurePoolElement element, - StructureTemplateManager templateManager) { - if (element == EmptyPoolElement.INSTANCE) { - return 0; - } - long maximumSpan = 0L; - for (Rotation rotation : Rotation.values()) { - BoundingBox box; - try { - box = element.getBoundingBox(templateManager, BlockPos.ZERO, rotation); - } catch (RuntimeException | LinkageError error) { - throw new IllegalStateException("Template pool element " - + element.getClass().getName() - + " could not provide bounded geometry for rotation " + rotation, error); - } - if (box == null) { - throw new IllegalStateException("Template pool element " - + element.getClass().getName() - + " returned no bounded geometry for rotation " + rotation); - } - long xSpan = (long) box.maxX() - box.minX() + 1L; - long zSpan = (long) box.maxZ() - box.minZ() + 1L; - maximumSpan = Math.max(maximumSpan, Math.max(xSpan, zSpan)); - } - if (maximumSpan < 0L || maximumSpan > Integer.MAX_VALUE) { - throw new IllegalStateException("Template pool element has an unbounded horizontal span"); - } - return (int) maximumSpan; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTerrainIntegrator.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTerrainIntegrator.java deleted file mode 100644 index e16922700..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureTerrainIntegrator.java +++ /dev/null @@ -1,1096 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.generation.mantle.StructureCarveEnvelope; -import art.arcane.iris.generation.mantle.StructureCarvingFootprint; -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.volmlib.util.noise.CNG; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.TerrainAdjustment; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.pools.LegacySinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; - -public final class NativeStructureTerrainIntegrator { - private static final long CARVE_CEILING_ROLL_SIGNATURE = 0x2A17L; - private static final long CARVE_FLOOR_ROLL_SIGNATURE = 0x5B3DL; - private static final long CARVE_LOBE_SIGNATURE = 0x7C41L; - private static final int MAX_CACHED_CARVE_CELLS = 2_000_000; - private static final int MAX_CARVE_COLUMNS = 2_000_000; - private static final int MAX_TEMPLATE_OCCUPANCY_CELLS = 4_194_304; - private static final int SOURCE_BURY_HORIZONTAL_RADIUS = 6; - private static final int SOURCE_BURY_VERTICAL_RADIUS = 12; - private static final int SOURCE_ENCAPSULATE_RADIUS = 12; - private static final int SOURCE_JUNCTION_RADIUS = 12; - private static final int SOURCE_MATERIAL_SAMPLE_RADIUS = 8; - private static final List TEMPLATE_VOID_BLOCKS = List.of(Blocks.AIR, Blocks.STRUCTURE_VOID); - private static final Set VANILLA_SOURCE_TERRAIN_BLOCKS = Set.of( - Blocks.STONE, Blocks.GRANITE, Blocks.DIORITE, Blocks.ANDESITE, - Blocks.TUFF, Blocks.DEEPSLATE, Blocks.NETHERRACK, Blocks.BASALT, - Blocks.BLACKSTONE, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.ROOTED_DIRT, - Blocks.GRASS_BLOCK, Blocks.PODZOL, Blocks.MYCELIUM, Blocks.MUD, - Blocks.MOSS_BLOCK, Blocks.SAND, Blocks.RED_SAND, Blocks.TERRACOTTA, - Blocks.CRIMSON_NYLIUM, Blocks.WARPED_NYLIUM, Blocks.SNOW_BLOCK, - Blocks.ICE, Blocks.PACKED_ICE, Blocks.BLUE_ICE, Blocks.SOUL_SAND, - Blocks.SOUL_SOIL, Blocks.END_STONE, Blocks.GRAVEL, Blocks.CLAY, - Blocks.CALCITE, Blocks.DRIPSTONE_BLOCK, Blocks.SANDSTONE, - Blocks.RED_SANDSTONE, Blocks.SCULK); - // Weakly keyed on the StructureStart so retired starts (world unload, generation done) - // are collectable instead of pinned forever by a process-wide static; values hold only - // primitives, so there is no value-to-key cycle defeating the weak keys. - private static final Map> CARVE_FOOTPRINTS = - new WeakHashMap<>(16); - private static final ConcurrentHashMap> - CARVE_FOOTPRINT_BUILDS = new ConcurrentHashMap<>(); - private static int cachedCarveCells; - - private NativeStructureTerrainIntegrator() { - } - - public static IrisStructureTerrain resolveNativeTerrain(StructureStart start, - IrisStructureTerrain configuredTerrain) { - return configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - } - - static void integrateTerrain(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain configuredTerrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver) { - SourceTerrainSnapshot sourceTerrain = requiresSourceTerrainFill(start, configuredTerrain) - ? captureSourceTerrain(world, area, start, configuredTerrain) : null; - integrateTerrain(world, area, structureId, start, configuredTerrain, - paletteBlockResolver, sourceTerrain); - } - - static void integrateTerrain(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain configuredTerrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver, - SourceTerrainSnapshot sourceTerrain) { - IrisStructureTerrain terrain = configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - IrisStructureTerrainMode mode = terrain.resolvedMode(); - if (mode == IrisStructureTerrainMode.SOURCE) { - integrateSourceTerrain(world, area, start, sourceTerrain); - return; - } - if (mode == IrisStructureTerrainMode.PRESERVE) { - return; - } - if (mode == IrisStructureTerrainMode.VACUUM || mode == IrisStructureTerrainMode.FLATTEN) { - return; - } - if (mode == IrisStructureTerrainMode.ENCASE) { - encasePieces(world, area, structureId, start, terrain, paletteBlockResolver); - return; - } - if (mode != IrisStructureTerrainMode.BORE && mode != IrisStructureTerrainMode.FORCE_CARVE) { - throw new IllegalStateException("Native structure terrain mode " + mode - + " is not implemented for '" + structureId + "'"); - } - IrisStructureCarveShape shape = mode == IrisStructureTerrainMode.BORE - ? IrisStructureCarveShape.BOX : terrain.resolvedShape(); - if (shape == IrisStructureCarveShape.BOX) { - carvePieceBoxes(world, area, start, terrain); - return; - } - carveOrganicColumns(world, area, organicCarve( - carveFootprint(start, Math.max(0, terrain.getHorizontalPadding()), - () -> world.getLevel().getStructureTemplateManager()), - terrain, shape, carveNoiseIdentity(world, structureId, start))); - } - - static SourceTerrainSnapshot captureSourceTerrain( - WorldGenLevel world, BoundingBox area, List targets) { - BitSet requiredLayers = new BitSet(area.getYSpan()); - boolean requiresSnapshot = false; - for (TerrainTarget target : targets) { - if (target != null && requiresSourceTerrainFill(target.start(), target.terrain())) { - requiresSnapshot = true; - markSourceTerrainLayers(area, target.start(), target.terrain(), requiredLayers); - } - } - return requiresSnapshot ? SourceTerrainSnapshot.capture(world, area, requiredLayers) : null; - } - - private static SourceTerrainSnapshot captureSourceTerrain( - WorldGenLevel world, BoundingBox area, StructureStart start, - IrisStructureTerrain configuredTerrain) { - BitSet requiredLayers = new BitSet(area.getYSpan()); - markSourceTerrainLayers(area, start, configuredTerrain, requiredLayers); - return SourceTerrainSnapshot.capture(world, area, requiredLayers); - } - - private static void markSourceTerrainLayers( - BoundingBox area, StructureStart start, IrisStructureTerrain configuredTerrain, - BitSet requiredLayers) { - if (!requiresSourceTerrainFill(start, configuredTerrain)) { - return; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - long horizontalDistanceSquared = minimumHorizontalDistanceSquared(area, bounds); - if (adjustment == TerrainAdjustment.BURY) { - long remainingDistanceSquared = (long) SOURCE_BURY_VERTICAL_RADIUS - * SOURCE_BURY_VERTICAL_RADIUS - 1L - horizontalDistanceSquared * 4L; - if (remainingDistanceSquared < 0L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - long groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - } - markLayers(area, requiredLayers, - groundY - verticalRadius, groundY + verticalRadius); - } else { - long remainingDistanceSquared = (long) SOURCE_ENCAPSULATE_RADIUS - * SOURCE_ENCAPSULATE_RADIUS - 1L - horizontalDistanceSquared; - if (remainingDistanceSquared < 0L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - markLayers(area, requiredLayers, - (long) bounds.minY() - verticalRadius, - (long) bounds.maxY() + verticalRadius); - } - } - markSourceJunctionLayers(area, start, requiredLayers); - } - - private static void markSourceJunctionLayers( - BoundingBox area, StructureStart start, BitSet requiredLayers) { - Set anchors = sourceJunctionAnchors(start); - for (JunctionAnchor anchor : anchors) { - long deltaX = intervalDistance(area.minX(), area.maxX(), anchor.x(), anchor.x()); - long deltaZ = intervalDistance(area.minZ(), area.maxZ(), anchor.z(), anchor.z()); - long horizontalDistanceSquared = deltaX * deltaX + deltaZ * deltaZ; - long remainingDistanceSquared = (long) SOURCE_JUNCTION_RADIUS - * SOURCE_JUNCTION_RADIUS - 1L - horizontalDistanceSquared; - if (remainingDistanceSquared < 1L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - markLayers(area, requiredLayers, - (long) anchor.y() - verticalRadius, (long) anchor.y() - 1L); - } - } - - private static long minimumHorizontalDistanceSquared(BoundingBox area, BoundingBox bounds) { - long deltaX = intervalDistance(area.minX(), area.maxX(), bounds.minX(), bounds.maxX()); - long deltaZ = intervalDistance(area.minZ(), area.maxZ(), bounds.minZ(), bounds.maxZ()); - return deltaX * deltaX + deltaZ * deltaZ; - } - - private static long intervalDistance(int firstMin, int firstMax, int secondMin, int secondMax) { - if (firstMax < secondMin) { - return (long) secondMin - firstMax; - } - if (firstMin > secondMax) { - return (long) firstMin - secondMax; - } - return 0L; - } - - private static void markLayers( - BoundingBox area, BitSet requiredLayers, long minimumY, long maximumY) { - long clippedMinimumY = Math.max(area.minY(), minimumY); - long clippedMaximumY = Math.min(area.maxY(), maximumY); - if (clippedMinimumY > clippedMaximumY) { - return; - } - int fromIndex = Math.toIntExact(clippedMinimumY - area.minY()); - int toIndex = Math.toIntExact(clippedMaximumY - area.minY() + 1L); - requiredLayers.set(fromIndex, toIndex); - } - - private static boolean requiresSourceTerrainFill( - StructureStart start, IrisStructureTerrain configuredTerrain) { - if (start == null || !start.isValid()) { - return false; - } - IrisStructureTerrain terrain = configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - if (terrain.resolvedMode() != IrisStructureTerrainMode.SOURCE) { - return false; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - return adjustment == TerrainAdjustment.BURY - || adjustment == TerrainAdjustment.ENCAPSULATE; - } - - static boolean clearsLegacyTemplateAir(StructureStart start, IrisStructureTerrain terrain) { - if (start == null || !start.isValid() || terrain == null) { - return false; - } - IrisStructureTerrainMode mode = terrain.resolvedMode(); - return mode == IrisStructureTerrainMode.ENCASE - || mode == IrisStructureTerrainMode.SOURCE - && start.getStructure().terrainAdaptation() != TerrainAdjustment.NONE; - } - - private static void integrateSourceTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - if (start == null || !start.isValid()) { - return; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - if (adjustment == TerrainAdjustment.BURY) { - fillBuriedTerrain(world, area, start, requireSourceTerrain( - world, area, start, sourceTerrain)); - } else if (adjustment == TerrainAdjustment.ENCAPSULATE) { - fillEncapsulatedTerrain(world, area, start, requireSourceTerrain( - world, area, start, sourceTerrain)); - } - } - - private static SourceTerrainSnapshot requireSourceTerrain( - WorldGenLevel world, BoundingBox area, StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - return sourceTerrain == null - ? captureSourceTerrain(world, area, start, null) : sourceTerrain; - } - - private static void fillBuriedTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - } - int minX = Math.max(area.minX(), bounds.minX() - SOURCE_BURY_HORIZONTAL_RADIUS); - int maxX = Math.min(area.maxX(), bounds.maxX() + SOURCE_BURY_HORIZONTAL_RADIUS); - int minY = Math.max(area.minY(), groundY - SOURCE_BURY_VERTICAL_RADIUS); - int maxY = Math.min(area.maxY(), groundY + SOURCE_BURY_VERTICAL_RADIUS); - int minZ = Math.max(area.minZ(), bounds.minZ() - SOURCE_BURY_HORIZONTAL_RADIUS); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + SOURCE_BURY_HORIZONTAL_RADIUS); - for (int x = minX; x <= maxX; x++) { - int outX = outset(x, bounds.minX(), bounds.maxX()); - for (int z = minZ; z <= maxZ; z++) { - int outZ = outset(z, bounds.minZ(), bounds.maxZ()); - for (int y = minY; y <= maxY; y++) { - int vertical = Math.abs(y - groundY); - if (!insideBurialEnvelope(outX, vertical, outZ)) { - continue; - } - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - fillSourceJunctionTerrain(world, area, start, position, sourceTerrain); - } - - private static void fillEncapsulatedTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int minX = Math.max(area.minX(), bounds.minX() - SOURCE_ENCAPSULATE_RADIUS); - int maxX = Math.min(area.maxX(), bounds.maxX() + SOURCE_ENCAPSULATE_RADIUS); - int minY = Math.max(area.minY(), bounds.minY() - SOURCE_ENCAPSULATE_RADIUS); - int maxY = Math.min(area.maxY(), bounds.maxY() + SOURCE_ENCAPSULATE_RADIUS); - int minZ = Math.max(area.minZ(), bounds.minZ() - SOURCE_ENCAPSULATE_RADIUS); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + SOURCE_ENCAPSULATE_RADIUS); - for (int x = minX; x <= maxX; x++) { - int outX = outset(x, bounds.minX(), bounds.maxX()); - for (int z = minZ; z <= maxZ; z++) { - int outZ = outset(z, bounds.minZ(), bounds.maxZ()); - for (int y = minY; y <= maxY; y++) { - int outY = outset(y, bounds.minY(), bounds.maxY()); - if (!insideEncapsulationEnvelope(outX, outY, outZ)) { - continue; - } - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - fillSourceJunctionTerrain(world, area, start, position, sourceTerrain); - } - - static boolean insideBurialEnvelope(int outX, int verticalDistance, int outZ) { - long horizontalSquared = (long) outX * outX + (long) outZ * outZ; - long verticalSquared = (long) verticalDistance * verticalDistance; - return horizontalSquared * 4L + verticalSquared - < (long) SOURCE_BURY_VERTICAL_RADIUS * SOURCE_BURY_VERTICAL_RADIUS; - } - - static boolean insideEncapsulationEnvelope(int outX, int outY, int outZ) { - return (long) outX * outX + (long) outY * outY + (long) outZ * outZ - < (long) SOURCE_ENCAPSULATE_RADIUS * SOURCE_ENCAPSULATE_RADIUS; - } - - static boolean isSourceRigidPiece(StructurePiece piece) { - if (piece == null) { - return false; - } - return !(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID; - } - - static boolean insideSourceJunctionEnvelope(int deltaX, int deltaY, int deltaZ) { - if (deltaY >= 0) { - return false; - } - return (long) deltaX * deltaX + (long) deltaY * deltaY + (long) deltaZ * deltaZ - < (long) SOURCE_JUNCTION_RADIUS * SOURCE_JUNCTION_RADIUS; - } - - private static void fillSourceJunctionTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - BlockPos.MutableBlockPos position, - SourceTerrainSnapshot sourceTerrain) { - Set anchors = sourceJunctionAnchors(start); - for (JunctionAnchor anchor : anchors) { - int minX = Math.max(area.minX(), anchor.x() - SOURCE_JUNCTION_RADIUS + 1); - int maxX = Math.min(area.maxX(), anchor.x() + SOURCE_JUNCTION_RADIUS - 1); - int minY = Math.max(area.minY(), anchor.y() - SOURCE_JUNCTION_RADIUS + 1); - int maxY = Math.min(area.maxY(), anchor.y() - 1); - int minZ = Math.max(area.minZ(), anchor.z() - SOURCE_JUNCTION_RADIUS + 1); - int maxZ = Math.min(area.maxZ(), anchor.z() + SOURCE_JUNCTION_RADIUS - 1); - for (int x = minX; x <= maxX; x++) { - int deltaX = x - anchor.x(); - for (int z = minZ; z <= maxZ; z++) { - int deltaZ = z - anchor.z(); - for (int y = minY; y <= maxY; y++) { - if (insideSourceJunctionEnvelope(deltaX, y - anchor.y(), deltaZ)) { - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - } - } - - private static Set sourceJunctionAnchors(StructureStart start) { - Set anchors = new HashSet<>(); - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece)) { - continue; - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - anchors.add(new JunctionAnchor( - junction.getSourceX(), junction.getSourceGroundY(), junction.getSourceZ())); - } - } - return anchors; - } - - private static int outset(int value, int minimum, int maximum) { - if (value < minimum) { - return minimum - value; - } - return Math.max(0, value - maximum); - } - - private static void fillEncaseable(WorldGenLevel world, BlockPos position, - SourceTerrainSnapshot sourceTerrain) { - if (isEncaseable(world.getBlockState(position))) { - world.setBlock(position, sourceEncaseBlock(world, position, sourceTerrain), 2); - } - } - - static BlockState sourceEncaseBlock(WorldGenLevel world, BlockPos position) { - int minX = Math.subtractExact(position.getX(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int minZ = Math.subtractExact(position.getZ(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int maxX = Math.addExact(position.getX(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int maxZ = Math.addExact(position.getZ(), SOURCE_MATERIAL_SAMPLE_RADIUS); - BoundingBox sampleArea = new BoundingBox(minX, position.getY(), minZ, - maxX, position.getY(), maxZ); - BitSet requiredLayers = new BitSet(1); - requiredLayers.set(0); - SourceTerrainSnapshot sourceTerrain = SourceTerrainSnapshot.capture( - world, sampleArea, requiredLayers); - return sourceEncaseBlock(world, position, sourceTerrain); - } - - static BlockState sourceEncaseBlock(WorldGenLevel world, BlockPos position, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos probe = new BlockPos.MutableBlockPos(); - for (int distance = 1; distance <= SOURCE_MATERIAL_SAMPLE_RADIUS; distance++) { - BlockState sampled = sourceTerrain.stateAt(probe.set( - position.getX() - distance, position.getY(), position.getZ())); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX() + distance, position.getY(), position.getZ())); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX(), position.getY(), position.getZ() - distance)); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX(), position.getY(), position.getZ() + distance)); - if (sampled != null) { - return sampled; - } - } - return defaultEncaseBlock(world, position.getY()); - } - - private static BlockState sourceTerrainBlock(BlockState state) { - if (!state.isSolid() || NativeStructureVegetationClearer.isTreeBlock(state)) { - return null; - } - return VANILLA_SOURCE_TERRAIN_BLOCKS.contains(state.getBlock()) - || state.is(BlockTags.BASE_STONE_OVERWORLD) - || state.is(BlockTags.BASE_STONE_NETHER) - || state.is(BlockTags.SUBSTRATE_OVERWORLD) - || state.is(BlockTags.DIRT) - || state.is(BlockTags.SAND) - || state.is(BlockTags.TERRACOTTA) - || state.is(BlockTags.MUD) - || state.is(BlockTags.MOSS_BLOCKS) - || state.is(BlockTags.GRASS_BLOCKS) - || state.is(BlockTags.NYLIUM) - || state.is(BlockTags.SNOW) - || state.is(BlockTags.ICE) - || state.is(BlockTags.CORAL_BLOCKS) - || state.is(BlockTags.SOUL_FIRE_BASE_BLOCKS) - ? state : null; - } - - static List contentPieceBounds(StructureStart start) { - List bounds = new ArrayList<>(start.getPieces().size()); - for (StructurePiece piece : start.getPieces()) { - bounds.add(piece.getBoundingBox()); - } - return List.copyOf(bounds); - } - - /** - * Per-column occupancy of the whole start, cached because a single structure spans many chunks and - * every one of them carves against the same shrinkwrapped footprint. - */ - static StructureCarvingFootprint carveFootprint(StructureStart start, int horizontalPadding, - Supplier templates) { - CarveFootprintKey key = new CarveFootprintKey(start, horizontalPadding); - StructureCarvingFootprint cached = cachedCarveFootprint(key); - if (cached != null) { - return cached; - } - CompletableFuture build = new CompletableFuture<>(); - CompletableFuture active = CARVE_FOOTPRINT_BUILDS.putIfAbsent(key, build); - if (active != null) { - return awaitCarveFootprint(active); - } - // Close the check-then-claim window: a racer whose cache check missed before the - // previous builder cached can claim the build slot after that builder retired it, - // and would rebuild a second instance without this re-check. - StructureCarvingFootprint published = cachedCarveFootprint(key); - if (published != null) { - build.complete(published); - CARVE_FOOTPRINT_BUILDS.remove(key, build); - return published; - } - try { - StructureCarvingFootprint footprint = StructureCarvingFootprint.fromColumns( - sink -> emitCarveColumns(start, templates, sink), horizontalPadding, MAX_CARVE_COLUMNS); - if (footprint == null) { - throw new IllegalStateException("Native structure carve footprint is empty or exceeds " - + MAX_CARVE_COLUMNS + " columns"); - } - cacheCarveFootprint(key, footprint); - build.complete(footprint); - return footprint; - } catch (RuntimeException | Error error) { - build.completeExceptionally(error); - throw error; - } finally { - CARVE_FOOTPRINT_BUILDS.remove(key, build); - } - } - - static int cachedCarveFootprintCells() { - synchronized (CARVE_FOOTPRINTS) { - cachedCarveCells = liveCarveCells(); - return cachedCarveCells; - } - } - - private static int liveCarveCells() { - int total = 0; - for (Map perStart : CARVE_FOOTPRINTS.values()) { - for (CachedCarveFootprint cached : perStart.values()) { - total += cached.cells(); - } - } - return total; - } - - static int maximumCachedCarveFootprintCells() { - return MAX_CACHED_CARVE_CELLS; - } - - private static StructureCarvingFootprint cachedCarveFootprint(CarveFootprintKey key) { - synchronized (CARVE_FOOTPRINTS) { - Map perStart = CARVE_FOOTPRINTS.get(key.start()); - CachedCarveFootprint cached = perStart == null ? null : perStart.get(key.padding()); - return cached == null ? null : cached.footprint(); - } - } - - private static StructureCarvingFootprint awaitCarveFootprint( - CompletableFuture future) { - return NativeBuildFutures.awaitBuild(future, "Native structure carve footprint build"); - } - - private static void cacheCarveFootprint(CarveFootprintKey key, - StructureCarvingFootprint footprint) { - int cells = Math.multiplyExact(footprint.width(), footprint.depth()); - synchronized (CARVE_FOOTPRINTS) { - // Weak keys expunge silently as the GC drops retired starts; re-derive the live - // total so the cell budget tracks reality instead of drifting. - cachedCarveCells = liveCarveCells(); - Map perStart = - CARVE_FOOTPRINTS.computeIfAbsent(key.start(), ignored -> new LinkedHashMap<>(4)); - CachedCarveFootprint previous = perStart.remove(key.padding()); - if (previous != null) { - cachedCarveCells -= previous.cells(); - } - Iterator>> eviction = - CARVE_FOOTPRINTS.entrySet().iterator(); - while (cachedCarveCells + cells > MAX_CACHED_CARVE_CELLS && eviction.hasNext()) { - Map.Entry> victim = eviction.next(); - if (victim.getKey() == key.start()) { - continue; - } - for (CachedCarveFootprint evicted : victim.getValue().values()) { - cachedCarveCells -= evicted.cells(); - } - eviction.remove(); - } - perStart.put(key.padding(), new CachedCarveFootprint(footprint, cells)); - cachedCarveCells += cells; - } - } - - static OrganicCarve organicCarve(StructureCarvingFootprint footprint, IrisStructureTerrain terrain, - IrisStructureCarveShape shape, long identity) { - double strength = terrain.resolvedErosionStrength(); - double lobeStrength = terrain.resolvedLobeStrength(); - RNG noiseRng = new RNG(identity); - CNG blob = null; - CNG ceilingRoll = null; - CNG floorRoll = null; - CNG lobe = null; - if (shape == IrisStructureCarveShape.ERODED && strength > 0D) { - blob = CNG.signature(noiseRng); - ceilingRoll = CNG.signature(noiseRng.nextParallelRNG(CARVE_CEILING_ROLL_SIGNATURE)); - floorRoll = CNG.signature(noiseRng.nextParallelRNG(CARVE_FLOOR_ROLL_SIGNATURE)); - } - if (shape == IrisStructureCarveShape.ERODED && lobeStrength > 0D) { - // A plain single octave channel: the fractured signature noise has no usable low frequency band. - lobe = new CNG(noiseRng.nextParallelRNG(CARVE_LOBE_SIGNATURE), 1D, 1); - } - return new OrganicCarve(footprint, shape, - Math.max(0, terrain.getHorizontalPadding()), - Math.max(0, terrain.getCeilingPadding()), - Math.max(0, terrain.getFloorPadding()), - strength, terrain.resolvedErosionFrequency(), blob, ceilingRoll, floorRoll, - lobe, terrain.resolvedLobeFrequency(), lobeStrength); - } - - static void carveOrganicColumns(WorldGenLevel world, BoundingBox area, OrganicCarve carve) { - StructureCarvingFootprint footprint = carve.footprint(); - int minX = Math.max(area.minX(), footprint.minX()); - int maxX = Math.min(area.maxX(), footprint.maxX()); - int minZ = Math.max(area.minZ(), footprint.minZ()); - int maxZ = Math.min(area.maxZ(), footprint.maxZ()); - if (minX > maxX || minZ > maxZ) { - return; - } - boolean eroded = carve.blob() != null; - BlockState air = Blocks.AIR.defaultBlockState(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - int index = footprint.indexAt(x, z); - long horizontalDistanceSquared = footprint.distanceSquaredAt(index); - double sideReach = StructureCarveEnvelope.lobedSideReach(carve.lobe(), - carve.lobeFrequency(), carve.lobeStrength(), x, z, carve.horizontalPadding()); - double sideReachSquared = sideReach * sideReach; - if (horizontalDistanceSquared > sideReachSquared) { - continue; - } - double normalizedHorizontal = sideReachSquared == 0D - ? 0D : horizontalDistanceSquared / sideReachSquared; - int sourceMinY = footprint.sourceMinYAt(index); - int sourceMaxY = footprint.sourceMaxYAt(index); - double upReach = eroded - ? StructureCarveEnvelope.lobedUpReach(carve.lobe(), carve.lobeFrequency(), - carve.lobeStrength(), x, z, - StructureCarveEnvelope.erodedUpReach(carve.ceilingRoll(), - carve.frequency(), carve.strength(), x, z, - carve.ceilingPadding())) - : Math.max(1D, carve.ceilingPadding()); - double floorReach = eroded - ? StructureCarveEnvelope.erodedDownReach(carve.floorRoll(), carve.frequency(), - carve.strength(), x, z, carve.floorPadding()) - : carve.floorPadding(); - int columnMinY = Math.max(area.minY(), sourceMinY - (int) Math.floor(floorReach)); - int columnMaxY = Math.min(area.maxY(), - sourceMaxY + (eroded ? (int) Math.ceil(upReach) : carve.ceilingPadding())); - double downReach = Math.max(1D, floorReach); - for (int y = columnMinY; y <= columnMaxY; y++) { - double normalizedVertical = StructureCarveEnvelope.normalizedVerticalDistance( - y, sourceMinY, sourceMaxY, upReach, downReach); - double distanceSquared = normalizedHorizontal - + normalizedVertical * normalizedVertical; - if (distanceSquared > 1D) { - continue; - } - if (distanceSquared > 0D && eroded) { - double noise = carve.blob().fitDouble(0D, 1D, - x * carve.frequency(), y * carve.frequency(), z * carve.frequency()); - if (!StructureCarveEnvelope.shouldCarveOverboreCell( - carve.shape(), distanceSquared, noise, carve.strength())) { - continue; - } - } - world.setBlock(position.set(x, y, z), air, 2); - } - } - } - } - - private static boolean emitCarveColumns(StructureStart start, - Supplier templates, - StructureCarvingFootprint.ColumnSink sink) { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || !emitTemplateColumns(pieceTemplates(poolPiece, templates), - poolPiece.getPosition(), poolPiece.getRotation(), bounds, sink)) { - emitBoxColumns(bounds, sink); - } - } - return true; - } - - /** - * Derives per-column vertical extents from the complement of the template's air and structure-void - * cells, so a carve tracks the actual silhouette instead of the piece's rectangular bounding box. - */ - static boolean emitTemplateColumns(List templates, BlockPos position, - Rotation rotation, BoundingBox bounds, - StructureCarvingFootprint.ColumnSink sink) { - if (templates.isEmpty()) { - return false; - } - int width = bounds.getXSpan(); - int depth = bounds.getZSpan(); - long cells = (long) width * depth * bounds.getYSpan(); - if (cells < 1L || cells > MAX_TEMPLATE_OCCUPANCY_CELLS) { - return false; - } - StructurePlaceSettings settings = new StructurePlaceSettings().setRotation(rotation); - boolean[] voidCells = null; - for (StructureTemplate template : templates) { - boolean[] templateVoid = new boolean[(int) cells]; - for (Block ignored : TEMPLATE_VOID_BLOCKS) { - for (StructureTemplate.StructureBlockInfo info - : template.filterBlocks(position, settings, ignored)) { - BlockPos voidPosition = info.pos(); - if (bounds.isInside(voidPosition)) { - templateVoid[templateCellIndex(bounds, width, depth, - voidPosition.getX(), voidPosition.getY(), voidPosition.getZ())] = true; - } - } - } - if (voidCells == null) { - voidCells = templateVoid; - continue; - } - for (int cell = 0; cell < voidCells.length; cell++) { - voidCells[cell] &= templateVoid[cell]; - } - } - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - int minY = Integer.MAX_VALUE; - int maxY = Integer.MIN_VALUE; - for (int y = bounds.minY(); y <= bounds.maxY(); y++) { - if (voidCells[templateCellIndex(bounds, width, depth, x, y, z)]) { - continue; - } - if (minY == Integer.MAX_VALUE) { - minY = y; - } - maxY = y; - } - if (minY != Integer.MAX_VALUE) { - sink.column(x, z, minY, maxY); - } - } - } - return true; - } - - private static void emitBoxColumns(BoundingBox bounds, StructureCarvingFootprint.ColumnSink sink) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - sink.column(x, z, bounds.minY(), bounds.maxY()); - } - } - } - - private static int templateCellIndex(BoundingBox bounds, int width, int depth, - int x, int y, int z) { - return ((y - bounds.minY()) * depth + z - bounds.minZ()) * width + x - bounds.minX(); - } - - private static List pieceTemplates(PoolElementStructurePiece poolPiece, - Supplier templates) { - List resolved = new ArrayList<>(1); - collectElementTemplates(poolPiece.getElement(), templates, resolved); - return resolved; - } - - private static void collectElementTemplates(StructurePoolElement element, - Supplier templates, - List resolved) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - collectElementTemplates(child, templates, resolved); - } - return; - } - if (element instanceof SinglePoolElement singleElement) { - resolved.add(NativeStructureReflection.resolveTemplate(singleElement, templates)); - } - } - - private static long carveNoiseIdentity(WorldGenLevel world, String structureId, - StructureStart start) { - return world.getSeed() - ^ ((long) start.getChunkPos().x() * 341873128712L) - ^ ((long) start.getChunkPos().z() * 132897987541L) - ^ (structureId == null ? 0 : structureId.hashCode()); - } - - private static void encasePieces(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain terrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver) { - IrisMaterialPalette palette = terrain.getEncasePalette(); - RNG rng = null; - if (palette != null) { - Objects.requireNonNull(paletteBlockResolver, - "Native structure encase palette requires a platform block resolver"); - rng = new RNG(world.getSeed() ^ (structureId == null ? 0 : structureId.hashCode())); - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (BoundingBox bounds : contentPieceBounds(start)) { - BoundingBox shell = paddedPieceArea(area, bounds, terrain); - if (shell == null) { - continue; - } - for (int x = shell.minX(); x <= shell.maxX(); x++) { - for (int z = shell.minZ(); z <= shell.maxZ(); z++) { - for (int y = shell.minY(); y <= shell.maxY(); y++) { - BlockState existing = world.getBlockState(position.set(x, y, z)); - if (!isEncaseable(existing)) { - continue; - } - BlockState fill = palette == null - ? defaultEncaseBlock(world, y) - : Objects.requireNonNull( - paletteBlockResolver.resolve(palette, rng, x, y, z), - "Encase palette returned no block for " + structureId + " at " - + x + "," + y + "," + z); - world.setBlock(position, fill, 2); - } - } - } - } - } - - static boolean isEncaseable(BlockState state) { - return state.isAir() || !state.getFluidState().isEmpty(); - } - - static BlockState defaultEncaseBlock(int y) { - return y < 0 ? Blocks.DEEPSLATE.defaultBlockState() : Blocks.STONE.defaultBlockState(); - } - - static BlockState defaultEncaseBlock(WorldGenLevel world, int y) { - ServerLevel level = world == null ? null : world.getLevel(); - if (level == null) { - return defaultEncaseBlock(y); - } - Holder dimensionType = level.dimensionTypeRegistration(); - ResourceKey dimensionTypeKey = dimensionType.unwrapKey().orElse(null); - return defaultEncaseBlock(level.dimension(), dimensionTypeKey, dimensionType.value(), y); - } - - static BlockState defaultEncaseBlock(ResourceKey dimension, int y) { - return defaultEncaseBlock(dimension, null, null, y); - } - - static BlockState defaultEncaseBlock(ResourceKey dimension, - ResourceKey dimensionTypeKey, - DimensionType dimensionType, int y) { - if (Level.NETHER.equals(dimension)) { - return Blocks.NETHERRACK.defaultBlockState(); - } - if (Level.END.equals(dimension)) { - return Blocks.END_STONE.defaultBlockState(); - } - if (BuiltinDimensionTypes.NETHER.equals(dimensionTypeKey) - || dimensionType != null && dimensionType.hasCeiling() && !dimensionType.hasSkyLight()) { - return Blocks.NETHERRACK.defaultBlockState(); - } - if (BuiltinDimensionTypes.END.equals(dimensionTypeKey) - || dimensionType != null && (dimensionType.hasEnderDragonFight() - || dimensionType.hasEndFlashes())) { - return Blocks.END_STONE.defaultBlockState(); - } - return defaultEncaseBlock(y); - } - - private static void carvePieceBoxes(WorldGenLevel world, BoundingBox area, StructureStart start, - IrisStructureTerrain terrain) { - BlockState air = Blocks.AIR.defaultBlockState(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (BoundingBox bounds : contentPieceBounds(start)) { - BoundingBox carve = paddedPieceArea(area, bounds, terrain); - if (carve == null) { - continue; - } - for (int x = carve.minX(); x <= carve.maxX(); x++) { - for (int z = carve.minZ(); z <= carve.maxZ(); z++) { - for (int y = carve.minY(); y <= carve.maxY(); y++) { - world.setBlock(position.set(x, y, z), air, 2); - } - } - } - } - } - - private static BoundingBox paddedPieceArea(BoundingBox area, BoundingBox bounds, - IrisStructureTerrain terrain) { - int horizontalPadding = Math.max(0, terrain.getHorizontalPadding()); - int minX = Math.max(area.minX(), bounds.minX() - horizontalPadding); - int minY = Math.max(area.minY(), bounds.minY() - Math.max(0, terrain.getFloorPadding())); - int minZ = Math.max(area.minZ(), bounds.minZ() - horizontalPadding); - int maxX = Math.min(area.maxX(), bounds.maxX() + horizontalPadding); - int maxY = Math.min(area.maxY(), bounds.maxY() + Math.max(0, terrain.getCeilingPadding())); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + horizontalPadding); - if (minX > maxX || minY > maxY || minZ > maxZ) { - return null; - } - return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ); - } - - static void clearLegacyTemplateAir(WorldGenLevel world, BoundingBox area, - StructureStart start, - Supplier templates) { - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() != StructureTemplatePool.Projection.RIGID - || !intersects(poolPiece.getBoundingBox(), area)) { - continue; - } - int groundY = poolPiece.getBoundingBox().minY() + poolPiece.getGroundLevelDelta(); - StructurePlaceSettings settings = new StructurePlaceSettings() - .setRotation(poolPiece.getRotation()) - .setBoundingBox(area); - clearLegacyTemplateAir(world, poolPiece.getElement(), poolPiece.getPosition(), - groundY, settings, templates); - } - } - - private static void clearLegacyTemplateAir(WorldGenLevel world, StructurePoolElement element, - BlockPos position, int groundY, - StructurePlaceSettings settings, - Supplier templates) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - clearLegacyTemplateAir(world, child, position, groundY, settings, templates); - } - return; - } - if (!(element instanceof LegacySinglePoolElement legacyElement)) { - return; - } - StructureTemplate template = NativeStructureReflection.resolveTemplate(legacyElement, templates); - clearTemplateAir(world, template, position, groundY, settings); - } - - static void clearTemplateAir(WorldGenLevel world, StructureTemplate template, - BlockPos position, int groundY, - StructurePlaceSettings settings) { - List airBlocks = template.filterBlocks( - position, settings, Blocks.AIR); - BlockState air = Blocks.AIR.defaultBlockState(); - for (StructureTemplate.StructureBlockInfo airBlock : airBlocks) { - BlockPos airPosition = airBlock.pos(); - BlockState existingState = world.getBlockState(airPosition); - if (shouldClearLegacyAir( - airPosition.getY(), groundY, existingState.isAir()) - && !NativeStructureVegetationClearer.isTreeBlock(existingState)) { - world.setBlock(airPosition, air, 2); - } - } - } - - static boolean shouldClearLegacyAir(int airY, int groundY, boolean existingAir) { - return airY >= groundY && !existingAir; - } - - static boolean intersects(BoundingBox first, BoundingBox second) { - return first.maxX() >= second.minX() && first.minX() <= second.maxX() - && first.maxY() >= second.minY() && first.minY() <= second.maxY() - && first.maxZ() >= second.minZ() && first.minZ() <= second.maxZ(); - } - - // StructureStart has no value equality, so the key pins the exact start instance a chunk carves against. - private record CarveFootprintKey(StructureStart start, int padding) { - } - - private record CachedCarveFootprint(StructureCarvingFootprint footprint, int cells) { - } - - private record JunctionAnchor(int x, int y, int z) { - } - - static final class SourceTerrainSnapshot { - private final BoundingBox area; - private final int width; - private final BlockState[][] statesByLayer; - private final int sampledCells; - - private SourceTerrainSnapshot(BoundingBox area, int width, - BlockState[][] statesByLayer, int sampledCells) { - this.area = area; - this.width = width; - this.statesByLayer = statesByLayer; - this.sampledCells = sampledCells; - } - - static SourceTerrainSnapshot capture( - WorldGenLevel world, BoundingBox area, BitSet requiredLayers) { - Objects.requireNonNull(world, "Source terrain snapshot requires a generation level"); - Objects.requireNonNull(area, "Source terrain snapshot requires writable bounds"); - Objects.requireNonNull(requiredLayers, "Source terrain snapshot requires sampled layers"); - int width = area.getXSpan(); - int depth = area.getZSpan(); - int height = area.getYSpan(); - int horizontalCells = Math.multiplyExact(width, depth); - BlockState[][] statesByLayer = new BlockState[height][]; - int sampledCells = 0; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int layer = requiredLayers.nextSetBit(0); - layer >= 0; layer = requiredLayers.nextSetBit(layer + 1)) { - if (layer >= height) { - throw new IllegalArgumentException( - "Source terrain snapshot layer exceeds writable bounds: " + layer); - } - BlockState[] states = new BlockState[horizontalCells]; - statesByLayer[layer] = states; - sampledCells = Math.addExact(sampledCells, horizontalCells); - int y = Math.addExact(area.minY(), layer); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int index = (z - area.minZ()) * width + x - area.minX(); - states[index] = sourceTerrainBlock( - world.getBlockState(position.set(x, y, z))); - } - } - } - return new SourceTerrainSnapshot(new BoundingBox( - area.minX(), area.minY(), area.minZ(), - area.maxX(), area.maxY(), area.maxZ()), width, - statesByLayer, sampledCells); - } - - BlockState stateAt(BlockPos position) { - if (!area.isInside(position)) { - return null; - } - BlockState[] states = statesByLayer[position.getY() - area.minY()]; - if (states == null) { - return null; - } - int index = (position.getZ() - area.minZ()) * width - + position.getX() - area.minX(); - return states[index]; - } - - int sampledCells() { - return sampledCells; - } - } - - record OrganicCarve(StructureCarvingFootprint footprint, IrisStructureCarveShape shape, - int horizontalPadding, int ceilingPadding, int floorPadding, - double strength, double frequency, CNG blob, CNG ceilingRoll, CNG floorRoll, - CNG lobe, double lobeFrequency, double lobeStrength) { - } - - public record TerrainTarget(String structureId, StructureStart start, - IrisStructureTerrain terrain) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVanillaLocator.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVanillaLocator.java deleted file mode 100644 index aab945dcc..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVanillaLocator.java +++ /dev/null @@ -1,224 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.SharedConstants; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.SectionPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureCheckResult; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -public final class NativeStructureVanillaLocator { - private NativeStructureVanillaLocator() { - } - - public static Candidate predict(ServerLevel level, HolderSet holders, - BlockPos origin, int radius, boolean findUnexplored) { - if (SharedConstants.DEBUG_DISABLE_FEATURES - || !level.getServer().getWorldGenSettings().options().generateStructures()) { - return null; - } - ChunkGeneratorStructureState state = level.getChunkSource().getGeneratorState(); - Map>> byPlacement = new LinkedHashMap<>(); - for (Holder holder : holders) { - for (StructurePlacement placement : state.getPlacementsForStructure(holder)) { - byPlacement.computeIfAbsent(placement, ignored -> new LinkedHashSet<>()).add(holder); - } - } - if (byPlacement.isEmpty()) { - return null; - } - - StructureManager structureManager = level.structureManager(); - Candidate best = null; - double bestDistance = Double.MAX_VALUE; - List>>> randomPlacements = - new ArrayList<>(byPlacement.size()); - for (Map.Entry>> entry : byPlacement.entrySet()) { - StructurePlacement placement = entry.getKey(); - if (placement instanceof ConcentricRingsStructurePlacement concentric) { - Candidate candidate = predictConcentric( - entry.getValue(), level, structureManager, origin, findUnexplored, - state, concentric); - if (candidate != null) { - double distance = origin.distSqr(candidate.result().getFirst()); - if (distance < bestDistance) { - best = candidate; - bestDistance = distance; - } - } - } else if (placement instanceof RandomSpreadStructurePlacement randomSpread) { - randomPlacements.add(Map.entry(randomSpread, entry.getValue())); - } - } - - int centerChunkX = SectionPos.blockToSectionCoord(origin.getX()); - int centerChunkZ = SectionPos.blockToSectionCoord(origin.getZ()); - int searchRadius = Math.max(0, radius); - for (int ring = 0; ring <= searchRadius; ring++) { - boolean foundInRing = false; - for (Map.Entry>> entry - : randomPlacements) { - Candidate candidate = predictRandomSpread( - entry.getValue(), level, structureManager, - centerChunkX, centerChunkZ, ring, findUnexplored, - state.getLevelSeed(), entry.getKey()); - if (candidate == null) { - continue; - } - foundInRing = true; - double distance = origin.distSqr(candidate.result().getFirst()); - if (distance < bestDistance) { - best = candidate; - bestDistance = distance; - } - } - if (foundInRing) { - return best; - } - } - return best; - } - - private static Candidate predictConcentric(Set> holders, - ServerLevel level, - StructureManager structureManager, - BlockPos origin, - boolean findUnexplored, - ChunkGeneratorStructureState state, - ConcentricRingsStructurePlacement placement) { - List positions = state.getRingPositionsFor(placement); - if (positions == null) { - throw new IllegalStateException( - "Tried to locate structures for an unavailable concentric placement"); - } - Candidate best = null; - double bestDistance = Double.MAX_VALUE; - for (ChunkPos position : positions) { - BlockPos locatePosition = new BlockPos( - SectionPos.sectionToBlockCoord(position.x(), 8), - 32, - SectionPos.sectionToBlockCoord(position.z(), 8)); - double distance = locatePosition.distSqr(origin); - if (best != null && distance >= bestDistance) { - continue; - } - Candidate candidate = predictAt( - holders, level, structureManager, findUnexplored, placement, position); - if (candidate != null) { - best = candidate; - bestDistance = distance; - } - } - return best; - } - - private static Candidate predictRandomSpread(Set> holders, - ServerLevel level, - StructureManager structureManager, - int centerChunkX, int centerChunkZ, - int ring, boolean findUnexplored, - long seed, - RandomSpreadStructurePlacement placement) { - int spacing = placement.spacing(); - for (int offsetX = -ring; offsetX <= ring; offsetX++) { - boolean edgeX = offsetX == -ring || offsetX == ring; - for (int offsetZ = -ring; offsetZ <= ring; offsetZ++) { - boolean edgeZ = offsetZ == -ring || offsetZ == ring; - if (!edgeX && !edgeZ) { - continue; - } - int gridChunkX = centerChunkX + spacing * offsetX; - int gridChunkZ = centerChunkZ + spacing * offsetZ; - ChunkPos candidatePosition = placement.getPotentialStructureChunk( - seed, gridChunkX, gridChunkZ); - Candidate candidate = predictAt( - holders, level, structureManager, - findUnexplored, placement, candidatePosition); - if (candidate != null) { - return candidate; - } - } - } - return null; - } - - private static Candidate predictAt(Set> holders, - ServerLevel level, - StructureManager structureManager, - boolean findUnexplored, - StructurePlacement placement, - ChunkPos candidatePosition) { - for (Holder holder : holders) { - Structure structure = holder.value(); - StructureCheckResult result = structureManager.checkStructurePresence( - candidatePosition, structure, placement, findUnexplored); - if (result == StructureCheckResult.START_NOT_PRESENT) { - continue; - } - if (!findUnexplored && result == StructureCheckResult.START_PRESENT) { - return new Candidate( - Pair.of(placement.getLocatePos(candidatePosition), holder), null); - } - ChunkAccess chunk = level.getChunk( - candidatePosition.x(), candidatePosition.z(), ChunkStatus.STRUCTURE_STARTS); - StructureStart start = structureManager.getStartForStructure( - structure, chunk); - if (start == null || !start.isValid() - || findUnexplored && !start.canBeReferenced()) { - continue; - } - return new Candidate( - Pair.of(placement.getLocatePos(start.getChunkPos()), holder), - findUnexplored ? start : null); - } - return null; - } - - public static final class Candidate { - private final Pair> result; - private final StructureStart referenceStart; - private final AtomicBoolean committed; - - private Candidate(Pair> result, - StructureStart referenceStart) { - this.result = result; - this.referenceStart = referenceStart; - this.committed = new AtomicBoolean(); - } - - public Pair> result() { - return result; - } - - public boolean reference(StructureManager structureManager) { - if (referenceStart == null || !committed.compareAndSet(false, true)) { - return false; - } - if (!referenceStart.canBeReferenced()) { - return false; - } - structureManager.addReference(referenceStart); - return true; - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVegetationClearer.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVegetationClearer.java deleted file mode 100644 index 5eeb76029..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVegetationClearer.java +++ /dev/null @@ -1,174 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.Arrays; -import java.util.BitSet; -import java.util.List; - -public final class NativeStructureVegetationClearer { - private NativeStructureVegetationClearer() { - } - - public static boolean isUndergroundStep(GenerationStep.Decoration step) { - return step == GenerationStep.Decoration.UNDERGROUND_STRUCTURES - || step == GenerationStep.Decoration.UNDERGROUND_DECORATION - || step == GenerationStep.Decoration.STRONGHOLDS; - } - - public static void clearIntersectingVegetation(WorldGenLevel world, ChunkAccess chunk, BoundingBox area, - List starts) { - if (starts == null || starts.isEmpty()) { - return; - } - VegetationSnapshot snapshot = captureVegetation(chunk, area); - if (snapshot.treeBlockCount() == 0) { - return; - } - int columns = area.getXSpan() * area.getZSpan(); - int[] clearMinY = new int[columns]; - int[] clearMaxY = new int[columns]; - Arrays.fill(clearMinY, Integer.MAX_VALUE); - Arrays.fill(clearMaxY, Integer.MIN_VALUE); - for (StructureStart start : starts) { - if (shouldProcessStart(start)) { - markVegetationColumns(area, snapshot, start, clearMinY, clearMaxY); - } - } - clearVegetationColumns(world, area, snapshot, clearMinY, clearMaxY); - } - - static boolean shouldProcessStart(StructureStart start) { - return start != null && start.isValid(); - } - - private static VegetationSnapshot captureVegetation(ChunkAccess chunk, BoundingBox area) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - BitSet[] columns = new BitSet[width * depth]; - int treeBlockCount = 0; - LevelChunkSection[] sections = chunk.getSections(); - int chunkMinX = chunk.getPos().getMinBlockX(); - int chunkMinZ = chunk.getPos().getMinBlockZ(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - int sectionMinY = chunk.getSectionYFromSectionIndex(sectionIndex) << 4; - int minY = Math.max(area.minY(), sectionMinY); - int maxY = Math.min(area.maxY(), sectionMinY + 15); - if (minY > maxY || section.hasOnlyAir() - || !section.maybeHas(NativeStructureVegetationClearer::isTreeBlock)) { - continue; - } - for (int y = minY; y <= maxY; y++) { - int localY = y - sectionMinY; - for (int z = area.minZ(); z <= area.maxZ(); z++) { - int localZ = z - chunkMinZ; - for (int x = area.minX(); x <= area.maxX(); x++) { - int localX = x - chunkMinX; - if (!isTreeBlock(section.getBlockState(localX, localY, localZ))) { - continue; - } - int column = (z - area.minZ()) * width + x - area.minX(); - BitSet treeBlocks = columns[column]; - if (treeBlocks == null) { - treeBlocks = new BitSet(area.getYSpan()); - columns[column] = treeBlocks; - } - treeBlocks.set(y - area.minY()); - treeBlockCount++; - } - } - } - } - return new VegetationSnapshot(columns, treeBlockCount); - } - - private static void markVegetationColumns(BoundingBox area, VegetationSnapshot snapshot, - StructureStart start, int[] clearMinY, int[] clearMaxY) { - int width = area.getXSpan(); - int[] pieceTops = new int[clearMinY.length]; - int[] pieceBottoms = new int[clearMinY.length]; - Arrays.fill(pieceTops, Integer.MIN_VALUE); - Arrays.fill(pieceBottoms, Integer.MAX_VALUE); - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - int minX = Math.max(area.minX(), bounds.minX()); - int maxX = Math.min(area.maxX(), bounds.maxX()); - int minZ = Math.max(area.minZ(), bounds.minZ()); - int maxZ = Math.min(area.maxZ(), bounds.maxZ()); - if (minX > maxX || minZ > maxZ) { - continue; - } - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - pieceTops[column] = Math.max(pieceTops[column], bounds.maxY()); - pieceBottoms[column] = Math.min(pieceBottoms[column], bounds.minY()); - } - } - } - for (int column = 0; column < pieceTops.length; column++) { - if (snapshot.columns()[column] == null || pieceTops[column] == Integer.MIN_VALUE) { - continue; - } - clearMinY[column] = Math.min(clearMinY[column], pieceBottoms[column] - 1); - clearMaxY[column] = Math.max(clearMaxY[column], pieceTops[column]); - } - } - - private static void clearVegetationColumns(WorldGenLevel world, BoundingBox area, - VegetationSnapshot snapshot, int[] clearMinY, int[] clearMaxY) { - int width = area.getXSpan(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - BlockState air = Blocks.AIR.defaultBlockState(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - BitSet treeBlocks = snapshot.columns()[column]; - if (treeBlocks == null || clearMaxY[column] < clearMinY[column]) { - continue; - } - int firstBit = Math.max(0, clearMinY[column] - area.minY()); - int lastBit = Math.min(area.getYSpan() - 1, clearMaxY[column] - area.minY()); - if (firstBit > lastBit) { - continue; - } - for (int bit = treeBlocks.nextSetBit(firstBit); bit >= 0 && bit <= lastBit; - bit = treeBlocks.nextSetBit(bit + 1)) { - int y = area.minY() + bit; - position.set(x, y, z); - BlockState state = world.getBlockState(position); - if (isTreeBlock(state)) { - world.setBlock(position, air, 2); - } - } - } - } - } - - static boolean isTreeBlock(BlockState state) { - if (state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES)) { - return true; - } - // Field reads instead of a registry byValue map hit — this predicate runs per block - // probe on the hottest native-structure scans. - String path = state.getBlock().builtInRegistryHolder().key().identifier().getPath(); - return path.endsWith("_log") || path.endsWith("_wood") - || path.endsWith("_stem") || path.endsWith("_hyphae") - || path.endsWith("_leaves"); - } - - private record VegetationSnapshot(BitSet[] columns, int treeBlockCount) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVerticalPlacer.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVerticalPlacer.java deleted file mode 100644 index 60aeead42..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVerticalPlacer.java +++ /dev/null @@ -1,384 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.structure.placement.StructureVerticalBounds; -import art.arcane.iris.structure.placement.IrisStructureYBand; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.ScatteredFeaturePiece; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidPiece; -import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure; -import net.minecraft.world.level.levelgen.structure.structures.JungleTemplePiece; -import net.minecraft.world.level.levelgen.structure.structures.JungleTempleStructure; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureVerticalPlacer { - private static final String DESERT_PYRAMID_ID = "minecraft:desert_pyramid"; - private static final String JUNGLE_PYRAMID_ID = "minecraft:jungle_pyramid"; - private static final int MAX_BURIAL_COLUMNS = 2_000_000; - private static final int MONUMENT_BASE_BELOW_SEA_LEVEL = 24; - private static final String OCEAN_MONUMENT_ID = "minecraft:monument"; - private static final int UNDERGROUND_SURFACE_CLEARANCE = 1; - - private NativeStructureVerticalPlacer() { - } - - public static int applyVerticalPlacement(StructureStart start, String structureId, int requestedOffset, - int seaLevel, int worldMinY, int worldMaxYExclusive, - boolean underground, boolean preserveSourceY, - IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - if (isOceanMonument(start, structureId)) { - return alignOceanMonumentToSeaLevel( - start, requestedOffset, seaLevel, worldMinY, worldMaxYExclusive); - } - if (isAdjustedScatteredStructure(start, structureId)) { - return alignScatteredStructureToSurface( - start, structureId, requestedOffset, worldMinY, worldMaxYExclusive, surfaceHeight); - } - return applyVerticalShift(start, requestedOffset, worldMinY, worldMaxYExclusive, - underground, preserveSourceY, yBand, surfaceHeight); - } - - public static StructureStart relocateToMinY(StructureStart start, Structure source, int targetMinY, - LevelHeightAccessor heightAccessor) { - Objects.requireNonNull(start, "Native structure start must not be null"); - Objects.requireNonNull(source, "Native structure source must not be null"); - Objects.requireNonNull(heightAccessor, "Native structure height accessor must not be null"); - if (!start.isValid()) { - return StructureStart.INVALID_START; - } - List pieces = start.getPieces(); - int minY = Integer.MAX_VALUE; - for (StructurePiece piece : pieces) { - minY = Math.min(minY, piece.getBoundingBox().minY()); - } - if (minY == Integer.MAX_VALUE) { - return StructureStart.INVALID_START; - } - int offsetY = Math.subtractExact(targetMinY, minY); - if (offsetY != 0) { - for (StructurePiece piece : pieces) { - moveStructurePiece(piece, offsetY); - } - } - int worldMinY = heightAccessor.getMinY() + 1; - int worldMaxYExclusive = Math.addExact( - heightAccessor.getMinY(), heightAccessor.getHeight()); - for (StructurePiece piece : pieces) { - BoundingBox bounds = piece.getBoundingBox(); - if (bounds.minY() < worldMinY || bounds.maxY() >= worldMaxYExclusive) { - throw new IllegalStateException("Native structure cannot fit target minimum Y " - + targetMinY + " inside world bounds [" + worldMinY + "," - + worldMaxYExclusive + ")"); - } - } - return new StructureStart( - source, - start.getChunkPos(), - start.getReferences(), - new PiecesContainer(List.copyOf(pieces)) - ); - } - - static int alignScatteredStructureToSurface(StructureStart start, String structureId, - int configuredOffset, int worldMinY, - int worldMaxYExclusive, - IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(surfaceHeight, "Scattered native structure requires a terrain height resolver"); - ScatteredFeaturePiece piece = requireAdjustedScatteredPiece(start, structureId); - BoundingBox bounds = start.getBoundingBox(); - BoundingBox pieceBounds = piece.getBoundingBox(); - int surfaceY = representativeScatteredSurfaceY(structureId, pieceBounds, surfaceHeight); - int targetMinY = Math.addExact(Math.addExact(surfaceY, 1), configuredOffset); - int requestedMove = Math.subtractExact(targetMinY, pieceBounds.minY()); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), requestedMove, worldMinY, worldMaxYExclusive); - if (offsetY != 0) { - moveStructureStart(start, bounds, offsetY); - } - setScatteredHeightPosition(piece, Math.max(0, piece.getBoundingBox().minY())); - return offsetY; - } - - public static int applyVerticalShift(StructureStart start, int requestedOffset, int worldMinY, - int worldMaxYExclusive, boolean underground, - boolean preserveSourceY, IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - BoundingBox bounds = start.getBoundingBox(); - int resolvedOffset = resolveShiftOffset(start, bounds, requestedOffset, worldMinY, - worldMaxYExclusive, underground, preserveSourceY, yBand, surfaceHeight); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), resolvedOffset, worldMinY, worldMaxYExclusive); - if (offsetY == 0) { - return 0; - } - moveStructureStart(start, bounds, offsetY); - return offsetY; - } - - private static int resolveShiftOffset(StructureStart start, BoundingBox bounds, int requestedOffset, - int worldMinY, int worldMaxYExclusive, boolean underground, - boolean preserveSourceY, IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - if (preserveSourceY) { - return requestedOffset; - } - if (yBand != null) { - return resolveYBandOffset(bounds, yBand, start.getChunkPos()); - } - if (underground) { - return resolveBuriedOffset( - bounds, requestedOffset, worldMinY, worldMaxYExclusive, surfaceHeight); - } - return requestedOffset; - } - - static int alignOceanMonumentToSeaLevel(StructureStart start, int configuredOffset, int seaLevel, - int worldMinY, int worldMaxYExclusive) { - OceanMonumentPieces.MonumentBuilding building = requireOceanMonumentBuilding(start); - BoundingBox bounds = start.getBoundingBox(); - int targetMinY = Math.addExact( - Math.subtractExact(seaLevel, MONUMENT_BASE_BELOW_SEA_LEVEL), configuredOffset); - int requestedOffset = Math.subtractExact(targetMinY, building.getBoundingBox().minY()); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), requestedOffset, worldMinY, worldMaxYExclusive); - if (offsetY != requestedOffset) { - throw new IllegalStateException("Ocean monument cannot align to sea level " + seaLevel - + " with configured offset " + configuredOffset + " inside world bounds [" - + worldMinY + "," + worldMaxYExclusive + ")"); - } - if (offsetY == 0) { - return 0; - } - moveStructureStart(start, bounds, offsetY); - return offsetY; - } - - static void ensureMonumentSeaLevelAlignment(StructureStart start, String structureId, - int configuredOffset, int seaLevel, - int worldMinY, int worldMaxYExclusive) { - if (!isOceanMonument(start, structureId)) { - return; - } - synchronized (start) { - alignOceanMonumentToSeaLevel( - start, configuredOffset, seaLevel, worldMinY, worldMaxYExclusive); - } - } - - private static boolean isOceanMonument(StructureStart start, String structureId) { - return OCEAN_MONUMENT_ID.equals(structureId) - && start != null - && start.getStructure() instanceof OceanMonumentStructure; - } - - private static boolean isAdjustedScatteredStructure(StructureStart start, String structureId) { - if (start == null) { - return false; - } - Structure source = start.getStructure(); - return (DESERT_PYRAMID_ID.equals(structureId) && source instanceof DesertPyramidStructure) - || (JUNGLE_PYRAMID_ID.equals(structureId) && source instanceof JungleTempleStructure); - } - - private static ScatteredFeaturePiece requireAdjustedScatteredPiece(StructureStart start, - String structureId) { - Objects.requireNonNull(start, "Scattered native structure start must not be null"); - List pieces = start.getPieces(); - if (pieces.size() != 1) { - throw new IllegalStateException(structureId + " must contain exactly one scattered piece, found " - + pieces.size()); - } - StructurePiece piece = pieces.get(0); - if (DESERT_PYRAMID_ID.equals(structureId) && piece instanceof DesertPyramidPiece desertPyramid) { - return desertPyramid; - } - if (JUNGLE_PYRAMID_ID.equals(structureId) && piece instanceof JungleTemplePiece jungleTemple) { - return jungleTemple; - } - throw new IllegalStateException(structureId + " contains unexpected piece " - + piece.getClass().getName()); - } - - private static int representativeScatteredSurfaceY(String structureId, BoundingBox bounds, - IntBinaryOperator surfaceHeight) { - if (DESERT_PYRAMID_ID.equals(structureId)) { - return lowestSurfaceY(bounds, surfaceHeight); - } - if (JUNGLE_PYRAMID_ID.equals(structureId)) { - return averageSurfaceY(bounds, surfaceHeight); - } - throw new IllegalStateException("Unsupported scattered native structure " + structureId); - } - - private static int lowestSurfaceY(BoundingBox bounds, IntBinaryOperator surfaceHeight) { - int lowestY = Integer.MAX_VALUE; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - lowestY = Math.min(lowestY, surfaceHeight.applyAsInt(x, z)); - } - } - if (lowestY == Integer.MAX_VALUE) { - throw new IllegalStateException("Scattered native structure has an empty terrain footprint"); - } - return lowestY; - } - - private static int averageSurfaceY(BoundingBox bounds, IntBinaryOperator surfaceHeight) { - long totalY = 0L; - long columns = 0L; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - totalY += surfaceHeight.applyAsInt(x, z); - columns++; - } - } - if (columns == 0L) { - throw new IllegalStateException("Scattered native structure has an empty terrain footprint"); - } - return Math.toIntExact(totalY / columns); - } - - private static void setScatteredHeightPosition(ScatteredFeaturePiece piece, int heightPosition) { - try { - NativeStructureReflection.ScatteredHeightPositionAccess.FIELD.setInt(piece, heightPosition); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot lock scattered native structure height", error); - } - } - - private static OceanMonumentPieces.MonumentBuilding requireOceanMonumentBuilding(StructureStart start) { - Objects.requireNonNull(start, "Ocean monument start must not be null"); - List pieces = start.getPieces(); - OceanMonumentPieces.MonumentBuilding building = null; - for (StructurePiece piece : pieces) { - if (piece instanceof OceanMonumentPieces.MonumentBuilding monumentBuilding) { - building = monumentBuilding; - } - } - if (pieces.size() != 1 || building == null) { - throw new IllegalStateException("minecraft:monument must contain exactly one MonumentBuilding, found " - + pieces.size() + " pieces"); - } - return building; - } - - private static void moveStructureStart(StructureStart start, BoundingBox cachedBounds, int offsetY) { - for (StructurePiece piece : start.getPieces()) { - moveStructurePiece(piece, offsetY); - } - cachedBounds.move(0, offsetY, 0); - } - - private static void moveStructurePiece(StructurePiece piece, int offsetY) { - piece.move(0, offsetY, 0); - if (piece instanceof OceanMonumentPieces.MonumentBuilding building) { - for (StructurePiece child : monumentChildPieces(building)) { - child.move(0, offsetY, 0); - } - } - if (piece instanceof PoolElementStructurePiece poolPiece) { - List junctions = poolPiece.getJunctions(); - for (int i = 0; i < junctions.size(); i++) { - JigsawJunction junction = junctions.get(i); - junctions.set(i, new JigsawJunction( - junction.getSourceX(), - junction.getSourceGroundY() + offsetY, - junction.getSourceZ(), - junction.getDeltaY(), - junction.getDestProjection())); - } - } - } - - static List monumentChildPieces(OceanMonumentPieces.MonumentBuilding building) { - Object value; - try { - value = NativeStructureReflection.MonumentChildPiecesAccess.FIELD.get(building); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read Ocean Monument child pieces", error); - } - if (!(value instanceof List children)) { - throw new IllegalStateException("Ocean Monument child-pieces field is not a list"); - } - List pieces = new ArrayList<>(children.size()); - for (Object child : children) { - if (!(child instanceof StructurePiece monumentPiece) - || child.getClass().getEnclosingClass() != OceanMonumentPieces.class) { - throw new IllegalStateException("Ocean Monument child-pieces list contains " - + (child == null ? "null" : child.getClass().getName())); - } - pieces.add(monumentPiece); - } - return List.copyOf(pieces); - } - - static int resolveYBandOffset(BoundingBox bounds, IrisStructureYBand yBand, ChunkPos startChunk) { - Objects.requireNonNull(bounds, "Native structure bounds must not be null"); - Objects.requireNonNull(startChunk, "Native structure Y band requires a start chunk"); - int target = yBandTargetMidpointY( - bounds.minY(), bounds.maxY(), yBand.resolvedMin(), yBand.resolvedMax(), startChunk); - return Math.subtractExact(target, midpointY(bounds.minY(), bounds.maxY())); - } - - static int yBandTargetMidpointY(int minY, int maxY, int bandMin, int bandMax, ChunkPos startChunk) { - int height = Math.subtractExact(maxY, minY); - int lowest = Math.addExact(bandMin, height / 2); - int highest = Math.subtractExact(bandMax, height - height / 2); - if (lowest > highest) { - // The band is shorter than the structure, so only its midpoint can be honoured. - return Math.floorDiv(Math.addExact(bandMin, bandMax), 2); - } - int span = highest - lowest + 1; - if (span == 1) { - return lowest; - } - long identity = (long) startChunk.x() * 341873128712L ^ (long) startChunk.z() * 132897987541L; - return lowest + new RNG(identity).nextInt(span); - } - - private static int midpointY(int minY, int maxY) { - return minY + (maxY - minY) / 2; - } - - static int resolveBuriedOffset(BoundingBox bounds, int requestedOffset, int worldMinY, - int worldMaxYExclusive, IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(bounds, "Native structure bounds must not be null"); - Objects.requireNonNull(surfaceHeight, "Underground native structure requires a terrain height resolver"); - long width = (long) bounds.maxX() - bounds.minX() + 1L; - long depth = (long) bounds.maxZ() - bounds.minZ() + 1L; - if (width <= 0L || depth <= 0L || width > MAX_BURIAL_COLUMNS / depth) { - throw new IllegalStateException("Underground native structure burial footprint is invalid or exceeds " - + MAX_BURIAL_COLUMNS + " columns"); - } - int maximumOffset = requestedOffset; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int allowedTopY = surfaceHeight.applyAsInt(x, z) - UNDERGROUND_SURFACE_CLEARANCE; - maximumOffset = Math.min(maximumOffset, allowedTopY - bounds.maxY()); - } - } - int clampedOffset = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), maximumOffset, worldMinY, worldMaxYExclusive); - if (clampedOffset > maximumOffset) { - IrisLogging.warn("Native structure burial at " + bounds.minX() + "," + bounds.minZ() - + " clamped to world floor: wanted " + maximumOffset + ", used " + clampedOffset); - } - return clampedOffset; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVolumeIndex.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVolumeIndex.java deleted file mode 100644 index 024c3bb69..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeStructureVolumeIndex.java +++ /dev/null @@ -1,643 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.volmlib.util.collection.KList; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.io.IOException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ForkJoinPool; -import java.util.concurrent.ForkJoinTask; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.IntBinaryOperator; -import java.util.function.IntConsumer; -import java.util.function.Supplier; - -/** - * Resolves the world-space piece bounds of every native structure that will generate around a query rect. - * - *

The answer is derived from the seed, the registries and this pack's structure policy only: candidate origins - * come from the placement grid and from vanilla structure placements, and each candidate is assembled through the - * same {@link NativeStructureFactory} path chunk generation uses. Nothing here reads chunk state, ownership records - * or generation progress, so the same query answers identically no matter which chunks already exist. - */ -public final class NativeStructureVolumeIndex { - private static final int ORIGIN_REACH_CHUNKS = NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS; - private static final int ORIGIN_WINDOW_DIAMETER = ORIGIN_REACH_CHUNKS * 2 + 1; - private static final int ORIGIN_WINDOW_STRIPE_COUNT = Long.SIZE; - private static final int MAX_CACHED_ORIGIN_CHUNKS = 16_384; - private static final int MAX_CACHED_QUERY_CHUNKS = 4_096; - private static final Map INDEXES = - Collections.synchronizedMap(new WeakHashMap<>()); - private static final Set WARNED_RESOLUTION_FAILURES = ConcurrentHashMap.newKeySet(); - - private final Context context; - private final OriginResolver origins; - private final Map> originCache = lru(MAX_CACHED_ORIGIN_CHUNKS); - private final Map> queryCache = lru(MAX_CACHED_QUERY_CHUNKS); - private final ConcurrentHashMap>> originBuilds = - new ConcurrentHashMap<>(); - private final ConcurrentHashMap>> queryBuilds = - new ConcurrentHashMap<>(); - private final ReentrantLock[] originWindowStripes = createOriginWindowStripes(); - private final IntConsumer retirementListener = this::evictRuntime; - - private NativeStructureVolumeIndex(Context context) { - this.context = Objects.requireNonNull(context, "Native structure volume context must not be null"); - this.origins = this::buildOriginVolumes; - } - - private NativeStructureVolumeIndex(OriginResolver origins) { - this.context = null; - this.origins = Objects.requireNonNull(origins, "Native structure origin resolver must not be null"); - } - - public static void install(Engine engine, Context context) { - Engine requiredEngine = Objects.requireNonNull(engine, "Native structure volume index requires an engine"); - NativeStructureVolumeIndex installed = new NativeStructureVolumeIndex(context); - synchronized (INDEXES) { - NativeStructureVolumeIndex previous = INDEXES.put(requiredEngine, installed); - if (previous != null) { - previous.detachRetirementListener(requiredEngine); - } - installed.attachRetirementListener(requiredEngine); - } - } - - public static void uninstall(Engine engine) { - if (engine != null) { - synchronized (INDEXES) { - NativeStructureVolumeIndex removed = INDEXES.remove(engine); - if (removed != null) { - removed.detachRetirementListener(engine); - } - } - } - } - - public static void invalidate(Engine engine) { - if (engine == null) { - return; - } - synchronized (INDEXES) { - NativeStructureVolumeIndex current = INDEXES.get(engine); - if (current != null) { - NativeStructureVolumeIndex replacement = current.fresh(); - current.detachRetirementListener(engine); - INDEXES.put(engine, replacement); - replacement.attachRetirementListener(engine); - } - } - } - - public static KList volumes(Engine engine, int minX, int minZ, int maxX, int maxZ) { - NativeStructureVolumeIndex index = engine == null ? null : INDEXES.get(engine); - return index == null ? NativeStructureVolume.NONE : index.resolve(engine, minX, minZ, maxX, maxZ); - } - - static NativeStructureVolumeIndex forTesting(OriginResolver origins) { - return new NativeStructureVolumeIndex(origins); - } - - static int originReachChunks() { - return ORIGIN_REACH_CHUNKS; - } - - private NativeStructureVolumeIndex fresh() { - return context == null - ? new NativeStructureVolumeIndex(origins) - : new NativeStructureVolumeIndex(context); - } - - KList resolve(Engine engine, int minX, int minZ, int maxX, int maxZ) { - int runtimeId = runtimeId(engine); - int fromChunkX = Math.min(minX, maxX) >> 4; - int toChunkX = Math.max(minX, maxX) >> 4; - int fromChunkZ = Math.min(minZ, maxZ) >> 4; - int toChunkZ = Math.max(minZ, maxZ) >> 4; - KList matches = null; - for (int chunkX = fromChunkX; chunkX <= toChunkX; chunkX++) { - for (int chunkZ = fromChunkZ; chunkZ <= toChunkZ; chunkZ++) { - for (NativeStructureVolume volume : chunkVolumes(engine, runtimeId, chunkX, chunkZ)) { - if (!volume.intersectsRect(minX, minZ, maxX, maxZ)) { - continue; - } - if (matches == null) { - matches = new KList<>(); - } - if (!matches.contains(volume)) { - matches.add(volume); - } - } - } - } - return matches == null ? NativeStructureVolume.NONE : matches; - } - - private KList chunkVolumes( - Engine engine, - int runtimeId, - int chunkX, - int chunkZ - ) { - return cached(queryCache, queryBuilds, new RuntimeChunkKey(runtimeId, chunkKey(chunkX, chunkZ)), - () -> buildChunkVolumes(engine, chunkX, chunkZ)); - } - - private KList buildChunkVolumes(Engine engine, int chunkX, int chunkZ) { - long stripeMask = originWindowStripeMask(chunkX, chunkZ); - lockOriginWindow(stripeMask); - try { - return buildChunkVolumesCoordinated(engine, chunkX, chunkZ); - } finally { - unlockOriginWindow(stripeMask); - } - } - - private KList buildChunkVolumesCoordinated(Engine engine, int chunkX, int chunkZ) { - int minX = chunkX << 4; - int minZ = chunkZ << 4; - int maxX = minX + 15; - int maxZ = minZ + 15; - Integer pinnedRuntimeId = pinnedRuntimeId(engine); - KList volumes = null; - for (int originX = chunkX - ORIGIN_REACH_CHUNKS; originX <= chunkX + ORIGIN_REACH_CHUNKS; originX++) { - for (int originZ = chunkZ - ORIGIN_REACH_CHUNKS; originZ <= chunkZ + ORIGIN_REACH_CHUNKS; originZ++) { - for (NativeStructureVolume volume : scopedOriginVolumes(engine, originX, originZ, pinnedRuntimeId)) { - if (!volume.intersectsRect(minX, minZ, maxX, maxZ)) { - continue; - } - if (volumes == null) { - volumes = new KList<>(); - } - volumes.add(volume); - } - } - } - return volumes == null ? NativeStructureVolume.NONE : volumes; - } - - private static Integer pinnedRuntimeId(Engine engine) { - if (!(engine instanceof IrisEngine irisEngine) || !irisEngine.hasGenerationRuntimeScope()) { - return null; - } - GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); - if (router == null) { - return null; - } - GenerationHistoryRuntimeRouter.RuntimeOwnership ownership = router.currentRuntimeOwnership().orElse(null); - int runtimeId = runtimeId(engine); - return ownership != null && ownership.binding().runtimeId() == runtimeId ? runtimeId : null; - } - - private KList scopedOriginVolumes( - Engine engine, int chunkX, int chunkZ, Integer pinnedRuntimeId - ) { - if (pinnedRuntimeId != null) { - synchronized (originCache) { - KList cached = originCache.get( - new RuntimeChunkKey(pinnedRuntimeId, chunkKey(chunkX, chunkZ))); - if (cached != null) { - return cached; - } - } - } - return originVolumes(engine, chunkX, chunkZ); - } - - static long originWindowStripeMask(int chunkX, int chunkZ) { - int minCellX = Math.floorDiv(chunkX - ORIGIN_REACH_CHUNKS, ORIGIN_WINDOW_DIAMETER); - int maxCellX = Math.floorDiv(chunkX + ORIGIN_REACH_CHUNKS, ORIGIN_WINDOW_DIAMETER); - int minCellZ = Math.floorDiv(chunkZ - ORIGIN_REACH_CHUNKS, ORIGIN_WINDOW_DIAMETER); - int maxCellZ = Math.floorDiv(chunkZ + ORIGIN_REACH_CHUNKS, ORIGIN_WINDOW_DIAMETER); - long mask = 0L; - for (int cellX = minCellX; cellX <= maxCellX; cellX++) { - for (int cellZ = minCellZ; cellZ <= maxCellZ; cellZ++) { - mask |= 1L << originWindowStripe(cellX, cellZ); - } - } - return mask; - } - - KList originVolumes(Engine engine, int chunkX, int chunkZ) { - try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = - openHistoryCoordinateScope(engine, chunkX, chunkZ)) { - int runtimeId = runtimeId(engine); - return cached( - originCache, - originBuilds, - new RuntimeChunkKey(runtimeId, chunkKey(chunkX, chunkZ)), - () -> origins.volumesAt(engine, chunkX, chunkZ) - ); - } - } - - private KList buildOriginVolumes(Engine engine, int chunkX, int chunkZ) { - ChunkGeneratorStructureState state = context.structureState().get(); - ChunkGenerator generator = context.generator().get(); - BiomeSource biomeSource = context.biomeSource().get(); - if (state == null || generator == null || biomeSource == null) { - return NativeStructureVolume.NONE; - } - Registry registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Bindings bindings = new Bindings(state, generator, biomeSource); - KList volumes = appendPlannedVolumes(engine, bindings, registry, chunkX, chunkZ, null); - volumes = appendVanillaVolumes(engine, bindings, registry, chunkX, chunkZ, volumes); - return volumes == null ? NativeStructureVolume.NONE : volumes; - } - - private KList appendPlannedVolumes(Engine engine, Bindings bindings, - Registry registry, int chunkX, int chunkZ, - KList volumes) { - KList plans; - try { - plans = NativeStructurePlacementPlanner.plansAt(engine, chunkX, chunkZ); - } catch (RuntimeException | Error error) { - warnOnce("iris-placements", error); - return volumes; - } - - KList target = volumes; - for (NativeStructureStartPlan plan : plans) { - IrisNativeStructureDecision decision = NativeStructurePlacementPlanner.decisionFor(plan); - if (!decision.generate()) { - continue; - } - Identifier identifier = Identifier.tryParse(plan.source().getStructure()); - if (identifier == null) { - continue; - } - Structure structure = registry.getValue(identifier); - if (structure == null) { - continue; - } - String structureId = identifier.toString(); - try { - StructureStart generated = NativeStructureFactory.generate( - generationContext(engine, bindings), registry.wrapAsHolder(structure), plan, 0); - target = appendPieces(target, structureId, generated); - } catch (RuntimeException | Error error) { - warnOnce(structureId, error); - } - } - return target; - } - - private KList appendVanillaVolumes(Engine engine, Bindings bindings, - Registry registry, int chunkX, int chunkZ, - KList volumes) { - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - IntBinaryOperator surfaceHeight = surfaceHeight(engine); - int worldMinY = context.heightAccessor().getMinY(); - int worldMaxYExclusive = worldMinY + context.heightAccessor().getHeight(); - KList target = volumes; - for (Holder setHolder : bindings.state().possibleStructureSets()) { - StructureSet set = setHolder.value(); - if (!set.placement().isStructureChunk(bindings.state(), chunkX, chunkZ)) { - continue; - } - for (StructureSet.StructureSelectionEntry entry : set.structures()) { - Holder holder = entry.structure(); - Structure structure = holder.value(); - Identifier identifier = registry.getKey(structure); - if (identifier == null) { - continue; - } - String structureId = identifier.toString(); - boolean undergroundStep = NativeStructureVegetationClearer.isUndergroundStep(structure.step()); - try { - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureId, undergroundStep); - if (!decision.generate()) { - continue; - } - StructureStart generated = structure.generate( - holder, - context.levelKey(), - context.registryAccess(), - bindings.generator(), - bindings.biomeSource(), - bindings.state().randomState().createClimateSampler(SamplerContext.EMPTY_UNCACHED), - bindings.state().randomState(), - context.templateManager(), - bindings.state().getLevelSeed(), - chunkPos, - 0, - context.heightAccessor(), - structure.biomes()::contains - ); - if (generated == null || !generated.isValid()) { - continue; - } - NativeStructureVerticalPlacer.applyVerticalPlacement( - generated, - structureId, - decision.yShift(), - bindings.generator().getSeaLevel(), - worldMinY, - worldMaxYExclusive, - undergroundStep, - decision.preserveSourceY(), - decision.yBand(), - surfaceHeight); - target = appendPieces(target, structureId, generated); - } catch (RuntimeException | Error error) { - warnOnce(structureId, error); - } - } - } - return target; - } - - private NativeStructureFactory.GenerationContext generationContext(Engine engine, Bindings bindings) { - return new NativeStructureFactory.GenerationContext( - context.registryAccess(), - bindings.generator(), - bindings.biomeSource(), - bindings.state().randomState(), - context.templateManager(), - bindings.state().getLevelSeed(), - context.levelKey(), - context.heightAccessor(), - biome -> true, - bindings.generator().getSeaLevel(), - surfaceHeight(engine) - ); - } - - private record Bindings(ChunkGeneratorStructureState state, ChunkGenerator generator, BiomeSource biomeSource) { - } - - @FunctionalInterface - interface OriginResolver { - KList volumesAt(Engine engine, int chunkX, int chunkZ); - } - - private static IntBinaryOperator surfaceHeight(Engine engine) { - return (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight(); - } - - static KList appendPieces(KList volumes, - String structureId, StructureStart start) { - if (start == null || !start.isValid()) { - return volumes; - } - KList target = volumes; - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (target == null) { - target = new KList<>(); - } - target.add(new NativeStructureVolume(structureId, - bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); - } - return target; - } - - void evictRuntime(int runtimeId) { - synchronized (originCache) { - originBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId); - originCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - } - synchronized (queryCache) { - queryBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId); - queryCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - } - } - - private KList cached( - Map> cache, - ConcurrentHashMap>> builds, - RuntimeChunkKey key, - Supplier> loader - ) { - synchronized (cache) { - KList hit = cache.get(key); - if (hit != null) { - return hit; - } - } - - CompletableFuture> future = new CompletableFuture<>(); - CompletableFuture> existing = builds.putIfAbsent(key, future); - if (existing != null) { - return NativeBuildFutures.awaitBuild(existing, "Native structure volume build"); - } - - // Close the check-then-claim window: a racer whose cache check missed before the - // previous builder cached could claim the build slot after it retired and rebuild. - synchronized (cache) { - KList published = cache.get(key); - if (published != null) { - future.complete(published); - builds.remove(key, future); - return published; - } - } - - try { - KList built = loader.get(); - synchronized (cache) { - if (builds.get(key) == future) { - cache.put(key, built); - } - } - future.complete(built); - return built; - } catch (RuntimeException | Error error) { - future.completeExceptionally(error); - throw error; - } finally { - builds.remove(key, future); - } - } - - private static void warnOnce(String structureId, Throwable error) { - if (WARNED_RESOLUTION_FAILURES.add(structureId)) { - IrisLogging.reportError("Native structure volume resolution failed for '" + structureId - + "'; objects will not be vetoed against it.", error); - } - } - - private static long chunkKey(int chunkX, int chunkZ) { - return ((long) chunkX << 32) ^ (chunkZ & 0xffffffffL); - } - - private static int originWindowStripe(int cellX, int cellZ) { - long mixed = chunkKey(cellX, cellZ); - mixed ^= mixed >>> 33; - mixed *= 0xff51afd7ed558ccdL; - mixed ^= mixed >>> 33; - mixed *= 0xc4ceb9fe1a85ec53L; - mixed ^= mixed >>> 33; - return (int) mixed & (ORIGIN_WINDOW_STRIPE_COUNT - 1); - } - - private static ReentrantLock[] createOriginWindowStripes() { - ReentrantLock[] stripes = new ReentrantLock[ORIGIN_WINDOW_STRIPE_COUNT]; - for (int i = 0; i < stripes.length; i++) { - stripes[i] = new ReentrantLock(); - } - return stripes; - } - - private void lockOriginWindow(long stripeMask) { - long pending = stripeMask; - while (pending != 0L) { - int stripe = Long.numberOfTrailingZeros(pending); - lockOriginStripe(originWindowStripes[stripe]); - pending &= pending - 1L; - } - } - - private static void lockOriginStripe(ReentrantLock lock) { - if (lock.tryLock()) { - return; - } - if (!ForkJoinTask.inForkJoinPool()) { - lock.lock(); - return; - } - OriginLockBlocker blocker = new OriginLockBlocker(lock); - try { - ForkJoinPool.managedBlock(blocker); - } catch (InterruptedException interruption) { - Thread.currentThread().interrupt(); - blocker.block(); - } catch (RejectedExecutionException exhaustedPool) { - blocker.block(); - } - } - - private void unlockOriginWindow(long stripeMask) { - long pending = stripeMask; - while (pending != 0L) { - int stripe = Long.SIZE - 1 - Long.numberOfLeadingZeros(pending); - originWindowStripes[stripe].unlock(); - pending &= ~(1L << stripe); - } - } - - private static final class OriginLockBlocker implements ForkJoinPool.ManagedBlocker { - private final ReentrantLock lock; - private boolean acquired; - - private OriginLockBlocker(ReentrantLock lock) { - this.lock = lock; - } - - @Override - public boolean block() { - if (!acquired) { - lock.lock(); - acquired = true; - } - return true; - } - - @Override - public boolean isReleasable() { - if (!acquired) { - acquired = lock.tryLock(); - } - return acquired; - } - } - - private static GenerationHistoryRuntimeRouter.CoordinateScope openHistoryCoordinateScope( - Engine engine, - int chunkX, - int chunkZ - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(chunkX << 4, chunkZ << 4); - } catch (IOException failure) { - throw new IllegalStateException("Unable to route native structure origin " - + chunkX + "," + chunkZ + " through generation history.", failure); - } - } - - private static int runtimeId(Engine engine) { - return engine == null ? 0 : engine.getCacheID(); - } - - private void attachRetirementListener(Engine engine) { - if (engine instanceof IrisEngine irisEngine) { - irisEngine.addGenerationRuntimeRetirementListener(retirementListener); - } - } - - private void detachRetirementListener(Engine engine) { - if (engine instanceof IrisEngine irisEngine) { - irisEngine.removeGenerationRuntimeRetirementListener(retirementListener); - } - } - - private static Map> lru(int capacity) { - return new LinkedHashMap<>(16, 0.75F, true) { - @Override - protected boolean removeEldestEntry( - Map.Entry> eldest - ) { - return size() > capacity; - } - }; - } - - private record RuntimeChunkKey(int runtimeId, long chunkKey) { - } - - /** - * The generator, biome source and structure state arrive as suppliers so an installed index never holds the - * level (and through it the engine) strongly. That keeps the weakly keyed registry collectable on world unload. - */ - public record Context( - RegistryAccess registryAccess, - StructureTemplateManager templateManager, - ResourceKey levelKey, - LevelHeightAccessor heightAccessor, - Supplier generator, - Supplier biomeSource, - Supplier structureState - ) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTerrainHeightCache.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTerrainHeightCache.java deleted file mode 100644 index bd54b6f61..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTerrainHeightCache.java +++ /dev/null @@ -1,42 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; -import net.minecraft.world.level.levelgen.Heightmap; - -import java.util.OptionalInt; -import java.util.function.Supplier; - -public final class NativeTerrainHeightCache { - private static final int MAXIMUM_CACHED_QUERIES = 65_536; - - private final Object2IntLinkedOpenHashMap heights = new Object2IntLinkedOpenHashMap<>(); - - public OptionalInt resolvedHeight(Query query, Supplier resolver) { - synchronized (heights) { - if (heights.containsKey(query)) { - return OptionalInt.of(heights.getAndMoveToFirst(query)); - } - } - OptionalInt resolved = resolver.get(); - if (resolved.isEmpty()) { - return resolved; - } - int height = resolved.getAsInt(); - synchronized (heights) { - heights.putAndMoveToFirst(query, height); - while (heights.size() > MAXIMUM_CACHED_QUERIES) { - heights.removeLastInt(); - } - } - return resolved; - } - - public void evictRuntime(int runtimeId) { - synchronized (heights) { - heights.keySet().removeIf(query -> query.runtimeId() == runtimeId); - } - } - - public record Query(int runtimeId, int x, int z, Heightmap.Types type, int minimumY, int height) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTransitionColumn.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTransitionColumn.java deleted file mode 100644 index 50b3138ad..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/NativeTransitionColumn.java +++ /dev/null @@ -1,63 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import art.arcane.iris.world.history.TerrainBoundarySignature; -import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.commands.arguments.blocks.BlockStateParser; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap; - -import java.util.HashMap; -import java.util.Map; - -public final class NativeTransitionColumn { - private NativeTransitionColumn() { - } - - public static NoiseColumn column(TerrainBoundarySignature signature, LevelHeightAccessor height) { - BlockState[] states = new BlockState[height.getHeight()]; - Map palette = new HashMap<>(); - for (int offset = 0; offset < states.length; offset++) { - String state = signature.geometry().voxelAt(height.getMinY() + offset).stateKey(); - states[offset] = palette.computeIfAbsent(state, NativeTransitionColumn::parse); - } - return new NoiseColumn(height.getMinY(), states); - } - - public static int height(TerrainBoundarySignature signature, Heightmap.Types type, LevelHeightAccessor height) { - Map palette = new HashMap<>(); - for (int offset = height.getHeight() - 1; offset >= 0; offset--) { - String state = signature.geometry().voxelAt(height.getMinY() + offset).stateKey(); - if (type.isOpaque().test(palette.computeIfAbsent(state, NativeTransitionColumn::parse))) { - return height.getMinY() + offset + 1; - } - } - return height.getMinY(); - } - - private static BlockState parse(String key) { - String nativeKey = key; - if (!key.startsWith("minecraft:") && IrisPlatforms.isBound()) { - PlatformBlockState logicalState = IrisPlatforms.get().registries().blockOrNull(key); - PlatformBlockState baseState = logicalState == null ? null : logicalState.placementBaseState(); - if (baseState != null) { - nativeKey = baseState.key(); - } - } - StringReader reader = new StringReader(nativeKey); - try { - BlockState state = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK, reader, false).blockState(); - if (reader.canRead()) { - throw new IllegalArgumentException("Unexpected terrain block state suffix: " + reader.getRemaining()); - } - return state; - } catch (CommandSyntaxException failure) { - throw new IllegalArgumentException("Saved terrain block state cannot be resolved: " + key, failure); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/WorldgenTerrainHeightmaps.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/WorldgenTerrainHeightmaps.java deleted file mode 100644 index d02958a48..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/nativegen/v26_3_R1/WorldgenTerrainHeightmaps.java +++ /dev/null @@ -1,113 +0,0 @@ -package art.arcane.iris.nativegen.v26_3_R1; - -import net.minecraft.core.SectionPos; -import net.minecraft.util.Mth; -import net.minecraft.util.SimpleBitStorage; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.IntBinaryOperator; - -/** - * Writes WORLD_SURFACE_WG and OCEAN_FLOOR_WG from Iris engine heights. - * - * Iris writes its terrain straight into LevelChunkSection, so neither vanilla's noise fill nor - * ProtoChunk.setBlockState ever updates the two worldgen heightmaps. Without these writes - * ChunkAccess.getHeight lazily primes the map from finished blocks - Iris trees, leaves and snow - * included - and vanilla pieces that self-snap against WORLD_SURFACE_WG (igloo) or OCEAN_FLOOR_WG - * (ocean ruins, buried treasure, mineshaft interiors) anchor to the canopy instead of the terrain. - * - * Both resolvers take world block coordinates and return the absolute first free Y, matching - * Heightmap's internal "first available" semantics: WORLD_SURFACE_WG counts fluid, OCEAN_FLOOR_WG - * does not. - */ -public final class WorldgenTerrainHeightmaps { - private static final int COLUMNS = 256; - private static final int PLACEMENT_CHUNK_MARGIN = 1; - - private WorldgenTerrainHeightmaps() { - } - - public static void primeTerrain(ChunkAccess chunk, IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY) { - Objects.requireNonNull(chunk, "Iris worldgen heightmap priming requires a chunk"); - Objects.requireNonNull(surfaceFirstFreeY, - "Iris worldgen heightmap priming requires a surface height resolver"); - Objects.requireNonNull(floorFirstFreeY, - "Iris worldgen heightmap priming requires an ocean floor height resolver"); - write(chunk, Heightmap.Types.WORLD_SURFACE_WG, surfaceFirstFreeY); - write(chunk, Heightmap.Types.OCEAN_FLOOR_WG, floorFirstFreeY); - } - - public static void primeStructurePlacement(WorldGenLevel world, ChunkPos generationCenter, - List starts, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY) { - Objects.requireNonNull(world, - "Iris worldgen heightmap priming requires a generation level"); - Objects.requireNonNull(generationCenter, - "Iris worldgen heightmap priming requires a generation center"); - if (starts == null || starts.isEmpty()) { - return; - } - Set primed = new HashSet<>(); - for (StructureStart start : starts) { - if (start == null || !start.isValid()) { - continue; - } - BoundingBox bounds = start.getBoundingBox(); - int minChunkX = SectionPos.blockToSectionCoord(bounds.minX()) - PLACEMENT_CHUNK_MARGIN; - int maxChunkX = SectionPos.blockToSectionCoord(bounds.maxX()) + PLACEMENT_CHUNK_MARGIN; - int minChunkZ = SectionPos.blockToSectionCoord(bounds.minZ()) - PLACEMENT_CHUNK_MARGIN; - int maxChunkZ = SectionPos.blockToSectionCoord(bounds.maxZ()) + PLACEMENT_CHUNK_MARGIN; - for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { - for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { - if (Math.abs(chunkX - generationCenter.x()) > PLACEMENT_CHUNK_MARGIN - || Math.abs(chunkZ - generationCenter.z()) > PLACEMENT_CHUNK_MARGIN) { - continue; - } - if (!primed.add(ChunkPos.pack(chunkX, chunkZ))) { - continue; - } - if (!world.hasChunk(chunkX, chunkZ)) { - continue; - } - primeTerrain(world.getChunk(chunkX, chunkZ), surfaceFirstFreeY, floorFirstFreeY); - } - } - } - } - - private static void write(ChunkAccess chunk, Heightmap.Types type, IntBinaryOperator firstFreeY) { - ChunkPos pos = chunk.getPos(); - int minY = chunk.getMinY(); - int height = chunk.getHeight(); - int baseX = pos.getMinBlockX(); - int baseZ = pos.getMinBlockZ(); - SimpleBitStorage storage = new SimpleBitStorage(Mth.ceillog2(height + 1), COLUMNS); - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - int firstFree = firstFreeY.applyAsInt(baseX + x, baseZ + z) - minY; - storage.set(x + z * 16, Mth.clamp(firstFree, 0, height)); - } - } - Heightmap heightmap = chunk.getOrCreateHeightmapUnprimed(type); - long[] packed = storage.getRaw(); - int expected = heightmap.getRawData().length; - if (expected != packed.length) { - throw new IllegalStateException("Iris cannot prime " + type + " for chunk " - + pos.x() + "," + pos.z() + ": packed " + packed.length - + " words for a heightmap storing " + expected - + "; Heightmap.setRawData would silently fall back to block scanning"); - } - heightmap.setRawData(chunk, type, packed); - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSource.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSource.java deleted file mode 100644 index d243a7510..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSource.java +++ /dev/null @@ -1,1287 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import net.minecraft.world.level.biome.BiomeResolver; -import net.minecraft.world.level.levelgen.RandomState; -import art.arcane.iris.platform.bukkit.BukkitWorldBinding; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.MapCodec; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.generation.runtime.DimensionTerrainContext; -import art.arcane.iris.generation.cache.AtomicCache; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.GenerationSessionException; -import art.arcane.iris.generation.runtime.GenerationSessionLease; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; -import art.arcane.iris.platform.generation.BukkitChunkGenerator; -import art.arcane.iris.generation.context.IrisContext; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.QuartPos; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.util.RandomSource; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Climate; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftWorld; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; - -public class CustomBiomeSource extends BiomeSource implements BiomeResolver { - private static final int NOISE_BIOME_CACHE_MAX = 262144; - private static final int STRONGHOLD_RING_SEARCH_Y = 0; - private static final int STRONGHOLD_RING_SEARCH_RADIUS = 112; - private static final int STRONGHOLD_RING_SEARCH_QUART_STEP = 4; - - private final long seed; - private final Engine engine; - private final BukkitChunkGenerator platformGenerator; - private final Registry biomeCustomRegistry; - private final Registry biomeRegistry; - private final AtomicCache registryAccess = new AtomicCache<>(); - private final Holder fallbackBiome; - private final ConcurrentHashMap> noiseBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> structureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> surfaceStructureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> naturalSurfaceStructureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap runtimeBiomeStates = new ConcurrentHashMap<>(); - - public CustomBiomeSource(long seed, Engine engine, World world) { - this.engine = engine; - this.platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator generator - ? generator - : null; - this.seed = seed; - this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null); - this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null); - this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry); - if (engine instanceof IrisEngine irisEngine) { - irisEngine.addGenerationRuntimeRetirementListener(this::evictRuntimeCaches); - } - runtimeBiomeState(); - } - - private static List> getAllBiomes( - Registry customRegistry, - Registry registry, - Engine engine, - boolean includeDimensionStack - ) { - LinkedHashSet> biomes = new LinkedHashSet<>(); - - for (OwnedBiome ownedBiome : getHostOwnedBiomes(engine)) { - IrisBiome i = ownedBiome.biome(); - Holder vanillaHolder = resolveBiomeHolder(registry, i.getStructureDerivativeKey()); - if (vanillaHolder == null) { - throw new IllegalStateException("Iris structure biome derivative '" - + i.getStructureDerivativeKey() + "' is not registered for biome '" + i.getLoadKey() + "'"); - } - biomes.add(vanillaHolder); - } - - if (includeDimensionStack) { - biomes.addAll(getVisibleBiomes( - customRegistry, - registry, - engine, - resolveFallbackBiome(registry, customRegistry) - )); - } - - if (biomes.isEmpty()) { - throw new IllegalStateException("Iris pack '" + engine.getName() - + "' has no registered structure biomes"); - } - return new ArrayList<>(biomes); - } - - private static List> getVisibleBiomes( - Registry customRegistry, - Registry registry, - Engine engine, - Holder fallback - ) { - LinkedHashSet> biomes = new LinkedHashSet<>(); - boolean fallbackPossible = false; - for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) { - IrisBiome irisBiome = ownedBiome.biome(); - if (irisBiome.isCustom()) { - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - Holder customHolder = resolveCustomBiomeHolder( - customRegistry, engine, ownedBiome.dimension(), customBiome.getId()); - if (customHolder == null) { - throw new IllegalStateException("Iris custom visible biome '" - + customBiomeKey(ownedBiome.dimension(), customBiome.getId()) - + "' is not registered"); - } - biomes.add(customHolder); - } - continue; - } - fallbackPossible |= !addVisibleBiomeHolder( - biomes, registry, irisBiome.getDerivativeKey()); - for (String scatter : irisBiome.getBiomeScatter()) { - fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter); - } - for (String scatter : irisBiome.getBiomeSkyScatter()) { - fallbackPossible |= !addVisibleBiomeHolder(biomes, registry, scatter); - } - } - if (fallbackPossible && fallback != null) { - biomes.add(fallback); - } - return new ArrayList<>(biomes); - } - - private static boolean addVisibleBiomeHolder( - Set> biomes, - Registry registry, - String biomeKey - ) { - Holder holder = resolveBiomeHolder(registry, biomeKey); - if (holder == null) { - return false; - } - biomes.add(holder); - return true; - } - - private static Object getFor(Class type, Object source) { - Object o = fieldFor(type, source); - - if (o != null) { - return o; - } - - o = invokeFor(type, source); - - if (o != null) { - return o; - } - - throw new IllegalStateException("Iris cannot resolve a " + type.getName() - + " from " + source.getClass().getName() + " on this server version"); - } - - private static Object fieldFor(Class returns, Object in) { - for (Class sourceType = in.getClass(); sourceType != null; sourceType = sourceType.getSuperclass()) { - Object o = fieldForClass(returns, sourceType, in); - - if (o != null) { - return o; - } - } - - return null; - } - - private static Object invokeFor(Class returns, Object in) { - for (Method i : in.getClass().getMethods()) { - if (i.getReturnType().equals(returns)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); - return i.invoke(in); - } catch (ReflectiveOperationException | RuntimeException e) { - throw new IllegalStateException("Iris failed to invoke " + in.getClass().getName() + "." - + i.getName() + "() for " + returns.getName(), e); - } - } - } - - return null; - } - - @SuppressWarnings("unchecked") - private static T fieldForClass(Class returnType, Class sourceType, Object in) { - for (Field i : sourceType.getDeclaredFields()) { - if (i.getType().equals(returnType)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); - return (T) i.get(in); - } catch (IllegalAccessException | RuntimeException e) { - throw new IllegalStateException("Iris failed to read " + sourceType.getName() + "." - + i.getName() + " for " + returnType.getName(), e); - } - } - } - return null; - } - - @Override - protected Stream> collectPossibleBiomes() { - return possibleBiomes(true).stream(); - } - - @Override - public Set> possibleBiomes() { - return possibleBiomes(true); - } - - Set> possibleStructureBiomes() { - return possibleBiomes(false); - } - - private Set> possibleBiomes(boolean includeDimensionStack) { - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_possible_biomes"); - if (lease == null) { - throw new IllegalStateException("Iris possible biome lookup was rejected during an engine transition"); - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - throw new IllegalStateException("Iris possible biome lookup has no active engine runtime"); - } - runtimeBiomeState(); - World world = BukkitWorldBinding.world(engine.getWorld()); - if (world == null) { - throw new IllegalStateException("Iris biome source has no bound Bukkit world"); - } - Registry customRegistry = ((RegistryAccess) getFor( - RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) - .lookup(Registries.BIOME).orElse(null); - Registry worldRegistry = ((CraftWorld) world).getHandle().registryAccess() - .lookup(Registries.BIOME).orElse(null); - return Set.copyOf(getAllBiomes( - customRegistry, worldRegistry, engine, includeDimensionStack)); - } - } - - private KMap> fillCustomBiomes(Registry customRegistry, Engine engine, Holder fallback) { - KMap> m = new KMap<>(); - if (customRegistry == null) { - return m; - } - - for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) { - IrisBiome i = ownedBiome.biome(); - if (i.isCustom()) { - for (IrisBiomeCustom j : i.getCustomDerivitives()) { - String key = customBiomeKey(ownedBiome.dimension(), j.getId()); - Holder holder = resolveCustomBiomeHolder( - customRegistry, engine, ownedBiome.dimension(), j.getId()); - if (holder == null) { - if (fallback != null) { - m.put(key, fallback); - } - IrisLogging.error("Cannot find biome for IrisBiomeCustom " + key - + " from engine " + engine.getName()); - continue; - } - m.put(key, holder); - } - } - } - - return m; - } - - private Map> fillVanillaSpawnBiomes(Registry customRegistry, Registry registry, Engine engine) { - IdentityHashMap> spawnBiomes = new IdentityHashMap<>(); - if (customRegistry == null || registry == null) { - return Collections.unmodifiableMap(spawnBiomes); - } - - for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) { - IrisBiome irisBiome = ownedBiome.biome(); - if (!irisBiome.isCustom()) { - continue; - } - Holder vanillaHolder = NMSBinding.biomeToBiomeBase(registry, irisBiome.getVanillaDerivative()); - if (vanillaHolder == null) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - Holder customHolder = resolveCustomBiomeHolder( - customRegistry, engine, ownedBiome.dimension(), customBiome.getId()); - if (customHolder != null) { - spawnBiomes.putIfAbsent(customHolder.value(), vanillaHolder); - } - } - } - - return Collections.unmodifiableMap(spawnBiomes); - } - - Holder getRetainedVanillaSpawnBiome(String derivativeKey) { - return resolveBiomeHolder(biomeRegistry, derivativeKey); - } - - Holder getVanillaSpawnBiome(Holder biome) { - if (biome == null) { - return null; - } - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_spawn_biome"); - if (lease == null) { - return null; - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - return null; - } - return runtimeBiomeState().vanillaSpawnBiomes().get(biome.value()); - } - } - - Holder getVisibleSurfaceBiome(int blockX, int blockZ) { - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_surface_spawn_biome"); - if (lease == null) { - return null; - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - return null; - } - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return fallbackBiome; - } - runtimeBiomeState(); - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - int sampleX = QuartPos.toBlock(quartX); - int sampleZ = QuartPos.toBlock(quartZ); - boolean cacheable = isBiomeCacheable(engine, sampleX, sampleZ); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - DimensionStackLayout.Layer layer = stackContext == null - ? null - : stackContext.getLayout(sampleX, sampleZ).surfaceLayer(); - int internalY = layer == null - ? Engine.hostHeight(engine, sampleX, sampleZ, true) - : layer.clippedSurfaceY(); - int quartY = QuartPos.fromBlock(internalY + engine.getWorld().minHeight()); - return getVisibleNoiseBiomeWithActiveGenerationLease( - quartX, quartY, quartZ, null, null, cacheable); - } - } - - private RegistryAccess registry() { - RegistryAccess access = registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); - - if (access == null) { - throw new IllegalStateException("Iris cannot resolve the Minecraft registry access on this server version"); - } - - return access; - } - - @Override - protected MapCodec codec() { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public BiomeResolver createResolver(Climate.Sampler sampler) { - return this; - } - - @Override - public Holder getNoiseBiome(int x, int y, int z) { - int blockX = x << 2; - int blockZ = z << 2; - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = - openHistoryCoordinateScope(blockX, blockZ, "bukkit_structure_biome"); - GenerationSessionLease lease = requireGenerationLease( - "bukkit_structure_biome", - "Iris structure biome lookup was rejected during an engine transition"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - throw new IllegalStateException("Iris structure biome lookup has no active engine runtime"); - } - return getStructureNoiseBiomeWithActiveGenerationLease(x, y, z); - } - } - - private Holder getStructureNoiseBiomeWithActiveGenerationLease(int x, int y, int z) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return fallbackBiome; - } - if (isGuaranteedSurfaceBiome(y)) { - return getSurfaceStructureBiomeHolder(x, z); - } - - boolean cacheable = isBiomeCacheable(x, z); - RuntimeNoiseKey cacheKey = new RuntimeNoiseKey( - engine.getCacheID(), packNoiseKey(x, y, z)); - if (cacheable) { - Holder cachedHolder = structureBiomeCache.get(cacheKey); - if (cachedHolder != null) { - return cachedHolder; - } - } - - Holder resolvedHolder = resolveStructureBiomeHolder(x, y, z); - if (!cacheable) { - return resolvedHolder; - } - Holder existingHolder = structureBiomeCache.putIfAbsent(cacheKey, resolvedHolder); - if (existingHolder != null) { - return existingHolder; - } - - if (structureBiomeCache.size() > NOISE_BIOME_CACHE_MAX) { - structureBiomeCache.clear(); - } - - return resolvedHolder; - } - - @Override - public Pair> findBiomeHorizontal( - int x, - int y, - int z, - int searchRadius, - Predicate> allowed, - RandomSource random, - RandomState sampler - ) { - int quartStep = horizontalBiomeSearchQuartStep(y, searchRadius); - if (quartStep == 1) { - return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler); - } - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_structure_ring_biome"); - if (lease == null) { - throw new IllegalStateException("Iris structure ring biome lookup was rejected during an engine transition"); - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - throw new IllegalStateException("Iris structure ring biome lookup has no active engine runtime"); - } - return findNaturalSurfaceBiomeHorizontal( - x, y, z, searchRadius, quartStep, allowed, random); - } - } - - @Override - public Pair> findClosestBiome3d( - BlockPos origin, - int searchRadius, - int sampleResolutionHorizontal, - int sampleResolutionVertical, - Predicate> allowed, - RandomState sampler, - LevelReader level - ) { - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_locate_visible_biome"); - if (lease == null) { - throw new IllegalStateException("Iris visible biome search was rejected during an engine transition"); - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - prepareVisibleBiomeBatch(); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - if (stackContext == null) { - return super.findClosestBiome3d( - origin, searchRadius, sampleResolutionHorizontal, sampleResolutionVertical, - allowed, sampler, level); - } - Set> candidates = new HashSet<>(); - for (Holder biome : getVisibleBiomes( - biomeCustomRegistry, biomeRegistry, engine, fallbackBiome)) { - if (allowed.test(biome)) { - candidates.add(biome); - } - } - if (candidates.isEmpty()) { - return null; - } - int sampleRadius = Math.floorDiv(searchRadius, sampleResolutionHorizontal); - int[] sampleYs = Mth.outFromOrigin( - origin.getY(), level.getMinY() + 1, level.getMaxY() + 1, sampleResolutionVertical).toArray(); - for (BlockPos.MutableBlockPos sampleColumn : BlockPos.spiralAround( - BlockPos.ZERO, sampleRadius, Direction.EAST, Direction.SOUTH)) { - int blockX = origin.getX() + sampleColumn.getX() * sampleResolutionHorizontal; - int blockZ = origin.getZ() + sampleColumn.getZ() * sampleResolutionHorizontal; - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - DimensionStackLayout stackLayout = stackContext.sample( - QuartPos.toBlock(quartX), QuartPos.toBlock(quartZ)); - for (int blockY : sampleYs) { - int quartY = QuartPos.fromBlock(blockY); - Holder visibleBiome = resolveVisibleBiomeHolder( - quartX, quartY, quartZ, null, stackLayout); - if (candidates.contains(visibleBiome)) { - return Pair.of(new BlockPos(blockX, blockY, blockZ), visibleBiome); - } - } - } - return null; - } - } - - static int horizontalBiomeSearchQuartStep(int blockY, int searchRadius) { - return blockY == STRONGHOLD_RING_SEARCH_Y && searchRadius == STRONGHOLD_RING_SEARCH_RADIUS - ? STRONGHOLD_RING_SEARCH_QUART_STEP - : 1; - } - - @Override - public Set> getBiomesWithin(int x, int y, int z, int radius) { - GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_biomes_within"); - if (lease == null) { - throw new IllegalStateException("Iris biome radius lookup was rejected during an engine transition"); - } - try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!isRuntimeAvailable()) { - throw new IllegalStateException("Iris biome radius lookup has no active engine runtime"); - } - int minQuartY = QuartPos.fromBlock(y - radius); - boolean monumentQuery = radius == 29 - && y == engine.getMinHeight() + engine.getDimension().getFluidHeight(); - if (!monumentQuery && !isGuaranteedSurfaceBiome(minQuartY)) { - return BiomeResolver.super.getBiomesWithin(x, y, z, radius); - } - int minQuartX = QuartPos.fromBlock(x - radius); - int maxQuartX = QuartPos.fromBlock(x + radius); - int minQuartZ = QuartPos.fromBlock(z - radius); - int maxQuartZ = QuartPos.fromBlock(z + radius); - int columns = (maxQuartX - minQuartX + 1) * (maxQuartZ - minQuartZ + 1); - Set> biomes = new HashSet<>(columns); - for (int quartZ = minQuartZ; quartZ <= maxQuartZ; quartZ++) { - for (int quartX = minQuartX; quartX <= maxQuartX; quartX++) { - biomes.add(getSurfaceStructureBiomeHolder(quartX, quartZ)); - } - } - return biomes; - } - } - - private Holder getSurfaceStructureBiomeHolder(int x, int z) { - int blockX = x << 2; - int blockZ = z << 2; - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = - openHistoryCoordinateScope(blockX, blockZ, "bukkit_surface_structure_biome")) { - RuntimeColumnKey columnKey = new RuntimeColumnKey( - engine.getCacheID(), packColumnKey(x, z)); - Holder surfaceHolder = surfaceStructureBiomeCache.get(columnKey); - if (surfaceHolder != null) { - return surfaceHolder; - } - Holder resolvedSurfaceHolder = resolveSurfaceStructureBiomeHolder(x, z); - Holder existingSurfaceHolder = surfaceStructureBiomeCache.putIfAbsent( - columnKey, resolvedSurfaceHolder); - if (existingSurfaceHolder != null) { - return existingSurfaceHolder; - } - if (surfaceStructureBiomeCache.size() > NOISE_BIOME_CACHE_MAX) { - surfaceStructureBiomeCache.clear(); - } - return resolvedSurfaceHolder; - } - } - - private Pair> findNaturalSurfaceBiomeHorizontal( - int x, - int y, - int z, - int searchRadius, - int quartStep, - Predicate> allowed, - RandomSource random - ) { - int centerQuartX = QuartPos.fromBlock(x); - int centerQuartZ = QuartPos.fromBlock(z); - int quartRadius = QuartPos.fromBlock(searchRadius); - Pair> selected = null; - int matches = 0; - for (int radius = 0; radius <= quartRadius; radius += quartStep) { - for (int offsetZ = -radius; offsetZ <= radius; offsetZ += quartStep) { - for (int offsetX = -radius; offsetX <= radius; offsetX += quartStep) { - int quartX = centerQuartX + offsetX; - int quartZ = centerQuartZ + offsetZ; - Holder holder = getNaturalSurfaceStructureBiomeHolder(quartX, quartZ); - if (!allowed.test(holder)) { - continue; - } - if (selected == null || random.nextInt(matches + 1) == 0) { - selected = Pair.of(new BlockPos( - QuartPos.toBlock(quartX), - y, - QuartPos.toBlock(quartZ)), holder); - } - matches++; - } - } - } - return selected; - } - - private Holder getNaturalSurfaceStructureBiomeHolder(int x, int z) { - int blockX = x << 2; - int blockZ = z << 2; - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = - openHistoryCoordinateScope(blockX, blockZ, "bukkit_natural_structure_biome")) { - RuntimeColumnKey columnKey = new RuntimeColumnKey( - engine.getCacheID(), packColumnKey(x, z)); - Holder cachedHolder = naturalSurfaceStructureBiomeCache.get(columnKey); - if (cachedHolder != null) { - return cachedHolder; - } - Holder resolvedHolder = resolveNaturalSurfaceStructureBiomeHolder(x, z); - Holder existingHolder = naturalSurfaceStructureBiomeCache.putIfAbsent( - columnKey, resolvedHolder); - if (existingHolder != null) { - return existingHolder; - } - if (naturalSurfaceStructureBiomeCache.size() > NOISE_BIOME_CACHE_MAX) { - naturalSurfaceStructureBiomeCache.clear(); - } - return resolvedHolder; - } - } - - private boolean isGuaranteedSurfaceBiome(int quartY) { - if (engine == null || engine.isClosed() || engine.getComplex() == null) { - return false; - } - int worldMinHeight = engine.getWorld().minHeight(); - int internalY = (quartY << 2) - worldMinHeight; - int caveSwitchInternalY = Math.max(-8 - worldMinHeight, 40); - return internalY > caveSwitchInternalY; - } - - private Holder resolveSurfaceStructureBiomeHolder(int x, int z) { - int blockX = x << 2; - int blockZ = z << 2; - IrisBiome irisBiome = resolveSurfaceStructureBiome(engine, blockX, blockZ); - if (irisBiome == null) { - throw new IllegalStateException("Iris returned no surface structure biome at block " - + blockX + "," + blockZ); - } - Holder holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); - if (holder == null) { - throw new IllegalStateException("Iris structure biome derivative '" - + irisBiome.getStructureDerivativeKey() + "' is not registered at block " - + blockX + "," + blockZ); - } - return holder; - } - - private Holder resolveNaturalSurfaceStructureBiomeHolder(int x, int z) { - int blockX = x << 2; - int blockZ = z << 2; - IrisBiome irisBiome = engine.getComplex().getNaturalTrueBiomeStream().get(blockX, blockZ); - if (irisBiome == null) { - throw new IllegalStateException("Iris returned no natural structure biome at block " - + blockX + "," + blockZ); - } - Holder holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); - if (holder == null) { - throw new IllegalStateException("Iris natural structure biome derivative '" - + irisBiome.getStructureDerivativeKey() + "' is not registered at block " - + blockX + "," + blockZ); - } - return holder; - } - - public Holder getVisibleNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - x << 2, z << 2, "bukkit_visible_biome"); - GenerationSessionLease lease = requireGenerationLease( - "bukkit_visible_biome", - "Iris visible biome lookup was rejected during an engine transition"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - prepareVisibleBiomeBatch(); - return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, sampler); - } - } - - void prepareVisibleBiomeBatch() { - if (!isRuntimeAvailable()) { - throw new IllegalStateException("Iris visible biome lookup has no active engine runtime"); - } - runtimeBiomeState(); - } - - Holder getVisibleNoiseBiomeWithActiveGenerationLease( - int x, - int y, - int z, - Climate.Sampler sampler - ) { - return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, sampler, null); - } - - Holder getVisibleNoiseBiomeWithActiveGenerationLease( - int x, - int y, - int z, - Climate.Sampler sampler, - IrisDimensionCarvingResolver.State resolverState - ) { - boolean cacheable = isBiomeCacheable(x, z); - return getVisibleNoiseBiomeWithActiveGenerationLease( - x, y, z, sampler, resolverState, cacheable); - } - - private Holder getVisibleNoiseBiomeWithActiveGenerationLease( - int x, - int y, - int z, - Climate.Sampler sampler, - IrisDimensionCarvingResolver.State resolverState, - boolean cacheable - ) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return fallbackBiome; - } - RuntimeNoiseKey cacheKey = new RuntimeNoiseKey( - engine.getCacheID(), packNoiseKey(x, y, z)); - if (cacheable) { - Holder cachedHolder = noiseBiomeCache.get(cacheKey); - if (cachedHolder != null) { - return cachedHolder; - } - } - - Holder resolvedHolder = resolveVisibleBiomeHolder(x, y, z, resolverState); - if (!cacheable) { - return resolvedHolder; - } - Holder existingHolder = noiseBiomeCache.putIfAbsent(cacheKey, resolvedHolder); - if (existingHolder != null) { - return existingHolder; - } - - if (noiseBiomeCache.size() > NOISE_BIOME_CACHE_MAX) { - noiseBiomeCache.clear(); - } - - return resolvedHolder; - } - - private boolean isBiomeCacheable(int quartX, int quartZ) { - return isBiomeCacheable( - engine, - QuartPos.toBlock(quartX), - QuartPos.toBlock(quartZ) - ); - } - - static boolean isBiomeCacheable(Engine engine, int blockX, int blockZ) { - return !engine.answersFromNaturalTerrain(blockX, blockZ); - } - - private GenerationSessionLease tryAcquireGenerationLease(String operation) { - if (engine.isClosed()) { - return null; - } - try { - return engine.acquireGenerationLease(operation); - } catch (GenerationSessionException e) { - if (engine.isClosing() || e.isExpectedTeardown()) { - return null; - } - throw new IllegalStateException("Iris biome source could not acquire generation session for " - + operation + ".", e); - } - } - - private GenerationSessionLease requireGenerationLease(String operation, String rejectionMessage) { - GenerationSessionLease lease = tryAcquireGenerationLease(operation); - if (lease == null) { - throw new IllegalStateException(rejectionMessage); - } - return lease; - } - - private boolean isRuntimeAvailable() { - return !engine.isClosed() && engine.getComplex() != null; - } - - private GenerationHistoryRuntimeRouter.CoordinateScope openHistoryCoordinateScope( - int blockX, - int blockZ, - String operation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(blockX, blockZ); - } catch (IOException failure) { - throw new IllegalStateException("Iris " + operation - + " could not route generation history at " - + blockX + "," + blockZ + ".", failure); - } - } - - private void evictRuntimeCaches(int runtimeId) { - runtimeBiomeStates.remove(runtimeId); - noiseBiomeCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - structureBiomeCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - surfaceStructureBiomeCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - naturalSurfaceStructureBiomeCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); - } - - private RuntimeBiomeState runtimeBiomeState() { - IrisDimension dimension = engine.getDimension(); - int runtimeId = engine.getCacheID(); - RuntimeBiomeState existing = runtimeBiomeStates.get(runtimeId); - if (existing != null) { - if (existing.dimension() != dimension) { - throw new IllegalStateException("Iris biome runtime cache ID is shared by different dimensions."); - } - return existing; - } - synchronized (runtimeBiomeStates) { - existing = runtimeBiomeStates.get(runtimeId); - if (existing != null) { - if (existing.dimension() != dimension) { - throw new IllegalStateException("Iris biome runtime cache ID is shared by different dimensions."); - } - return existing; - } - KMap> refreshedCustomBiomes = fillCustomBiomes( - biomeCustomRegistry, engine, fallbackBiome); - Map> refreshedSpawnBiomes = fillVanillaSpawnBiomes( - biomeCustomRegistry, biomeRegistry, engine); - RuntimeBiomeState created = new RuntimeBiomeState( - dimension, - refreshedCustomBiomes, - refreshedSpawnBiomes); - runtimeBiomeStates.put(runtimeId, created); - return created; - } - } - - private Holder resolveStructureBiomeHolder(int x, int y, int z) { - BiomeResolution resolution = resolveStructureBiomeResolution(x, y, z); - if (resolution == null) { - throw new IllegalStateException("Iris returned no structure biome at quart " - + x + "," + y + "," + z); - } - - Holder holder = resolveBiomeHolder( - biomeRegistry, resolution.irisBiome.getStructureDerivativeKey()); - if (holder == null) { - throw new IllegalStateException("Iris structure biome derivative '" - + resolution.irisBiome.getStructureDerivativeKey() + "' is not registered at block " - + resolution.blockX + "," + resolution.blockY + "," + resolution.blockZ); - } - return holder; - } - - private Holder resolveVisibleBiomeHolder( - int x, - int y, - int z, - IrisDimensionCarvingResolver.State resolverState - ) { - return resolveVisibleBiomeHolder(x, y, z, resolverState, null); - } - - private Holder resolveVisibleBiomeHolder( - int x, - int y, - int z, - IrisDimensionCarvingResolver.State resolverState, - DimensionStackLayout stackLayout - ) { - int blockX = x << 2; - int blockY = y << 2; - int blockZ = z << 2; - Optional historicalKey = engine.getComplex().historicalPhysicalBiomeKeyAt( - blockX, blockY, blockZ); - if (historicalKey.isPresent()) { - return resolvePhysicalBiomeHolder(historicalKey.get()); - } - BiomeResolution resolution = resolveBiomeResolution( - x, y, z, resolverState, true, stackLayout); - if (resolution == null) { - return getFallbackBiome(); - } - - return resolveVisibleBiomeHolder(resolution); - } - - private Holder resolveVisibleBiomeHolder(BiomeResolution resolution) { - if (resolution.irisBiome.isCustom()) { - return resolveCustomHolder(resolution); - } - - org.bukkit.block.Biome vanillaBiome = resolution.underground - ? resolution.irisBiome.getGroundBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ) - : resolution.irisBiome.getSkyBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ); - Holder holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome); - if (holder != null) { - return holder; - } - - return getFallbackBiome(); - } - - private Holder resolveCustomHolder(BiomeResolution resolution) { - IrisBiomeCustom customBiome = resolution.irisBiome.getCustomBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ); - if (customBiome != null) { - Holder holder = runtimeBiomeState().customBiomes().get(customBiomeKey( - resolution.dimension, customBiome.getId())); - if (holder != null) { - return holder; - } - } - - return getFallbackBiome(); - } - - private BiomeResolution resolveStructureBiomeResolution(int x, int y, int z) { - return resolveBiomeResolution(x, y, z, null, false); - } - - private Holder resolvePhysicalBiomeHolder(String physicalKey) { - Holder holder = resolveBiomeHolder(biomeCustomRegistry, physicalKey); - if (holder == null) { - holder = resolveBiomeHolder(biomeRegistry, physicalKey); - } - if (holder == null) { - throw new IllegalStateException("Historical Iris biome '" + physicalKey - + "' is not registered in the active world registry."); - } - return holder; - } - - private BiomeResolution resolveBiomeResolution( - int x, - int y, - int z, - IrisDimensionCarvingResolver.State resolverState - ) { - return resolveBiomeResolution(x, y, z, resolverState, true); - } - - private BiomeResolution resolveBiomeResolution( - int x, - int y, - int z, - IrisDimensionCarvingResolver.State resolverState, - boolean includeDimensionStack - ) { - return resolveBiomeResolution( - x, y, z, resolverState, includeDimensionStack, null); - } - - private BiomeResolution resolveBiomeResolution( - int x, - int y, - int z, - IrisDimensionCarvingResolver.State resolverState, - boolean includeDimensionStack, - DimensionStackLayout stackLayout - ) { - if (engine == null || engine.isClosed()) { - return null; - } - - if (engine.getComplex() == null) { - return null; - } - - int blockX = x << 2; - int blockZ = z << 2; - int blockY = y << 2; - int worldMinHeight = engine.getWorld().minHeight(); - int internalY = blockY - worldMinHeight; - int caveSwitchInternalY = Math.max(-8 - worldMinHeight, 40); - DimensionStackLayout.Layer stackLayer = includeDimensionStack - ? stackLayout == null - ? resolveDimensionStackLayer(engine, blockX, internalY, blockZ) - : stackLayout.layerAt(internalY) - : null; - IrisDimension owningDimension = stackLayer == null - ? engine.getDimension() - : stackLayer.terrainContext().getDimension(); - if (stackLayer != null && !stackLayer.terrainContext().isSelfReferencing()) { - IrisBiome stackedBiome = stackLayer.biome(); - return stackedBiome == null - ? null - : createBiomeResolution( - stackedBiome, false, owningDimension, blockX, blockY, blockZ); - } - boolean deepUnderground = internalY <= caveSwitchInternalY; - boolean underground = false; - IrisBiome irisBiome; - if (stackLayer != null) { - int surfaceInternalY = stackLayer.surfaceY(); - underground = internalY <= surfaceInternalY - 8; - irisBiome = underground - ? engine.getCaveBiome( - blockX, - internalY, - blockZ, - resolverState, - stackLayer.biome(), - surfaceInternalY - ) - : stackLayer.biome(); - } else if (deepUnderground) { - int surfaceInternalY = Engine.hostHeight(engine, blockX, blockZ, true); - underground = internalY <= surfaceInternalY - 8; - irisBiome = underground - ? engine.getCaveBiome(blockX, internalY, blockZ, resolverState) - : engine.getHostSurfaceBiome(blockX, blockZ); - } else { - irisBiome = engine.getHostSurfaceBiome(blockX, blockZ); - } - if (irisBiome == null && underground) { - irisBiome = stackLayer == null - ? engine.getHostSurfaceBiome(blockX, blockZ) - : stackLayer.biome(); - } - if (irisBiome == null) { - return null; - } - - return createBiomeResolution( - irisBiome, underground, owningDimension, blockX, blockY, blockZ); - } - - private BiomeResolution createBiomeResolution( - IrisBiome irisBiome, - boolean underground, - IrisDimension dimension, - int blockX, - int blockY, - int blockZ - ) { - RNG noiseRng = new RNG(seed - ^ (((long) blockX) * 341873128712L) - ^ (((long) blockY) * 132897987541L) - ^ (((long) blockZ) * 42317861L)); - return new BiomeResolution( - irisBiome, underground, dimension, blockX, blockY, blockZ, noiseRng); - } - - private Holder getFallbackBiome() { - if (fallbackBiome != null) { - return fallbackBiome; - } - - Holder holder = resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); - if (holder != null) { - return holder; - } - - throw new IllegalStateException("Unable to resolve any biome holder fallback for Iris biome source"); - } - - private static long packNoiseKey(int x, int y, int z) { - return (((long) x & 67108863L) << 38) - | (((long) z & 67108863L) << 12) - | ((long) y & 4095L); - } - - private static long packColumnKey(int x, int z) { - return ((long) x << 32) ^ ((long) z & 4294967295L); - } - - private static Holder resolveCustomBiomeHolder( - Registry customRegistry, - Engine engine, - IrisDimension dimension, - String customBiomeId - ) { - if (customRegistry == null || engine == null || dimension == null - || customBiomeId == null || customBiomeId.isBlank()) { - return null; - } - - String physicalKey = engine.getData().customBiomeResourceKey(dimension, customBiomeId); - Identifier resourceLocation = Identifier.tryParse(physicalKey); - if (resourceLocation == null) { - throw new IllegalStateException("Invalid Iris custom biome resource key '" + physicalKey + "'."); - } - Biome biome = customRegistry.getValue(resourceLocation); - if (biome == null) { - return null; - } - - Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); - if (optionalBiomeKey.isEmpty()) { - return null; - } - - Optional> optionalReferenceHolder = customRegistry.get(optionalBiomeKey.get()); - if (optionalReferenceHolder.isEmpty()) { - return null; - } - - return optionalReferenceHolder.get(); - } - - static String customBiomeKey(IrisDimension dimension, String customBiomeId) { - return dimension.getCustomBiomeKey(customBiomeId); - } - - private static List getOwnedBiomes(Engine engine) { - ArrayList biomes = new ArrayList<>(getHostOwnedBiomes(engine)); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - if (stackContext == null) { - return biomes; - } - for (DimensionTerrainContext terrainContext : stackContext.getLayersBottomToTop()) { - if (terrainContext.isSelfReferencing()) { - continue; - } - IrisDimension dimension = terrainContext.getDimension(); - addOwnedBiomes( - biomes, - dimension, - dimension.getReachableBiomes(terrainContext) - ); - } - return biomes; - } - - private static List getHostOwnedBiomes(Engine engine) { - IrisDimension hostDimension = engine.getDimension(); - ArrayList biomes = new ArrayList<>(); - addOwnedBiomes(biomes, hostDimension, hostDimension.getReachableBiomes(engine)); - return biomes; - } - - private static void addOwnedBiomes( - List target, - IrisDimension dimension, - Iterable biomes - ) { - for (IrisBiome biome : biomes) { - if (biome != null) { - target.add(new OwnedBiome(dimension, biome)); - } - } - } - - private static DimensionStackLayout.Layer resolveDimensionStackLayer( - Engine engine, - int blockX, - int internalY, - int blockZ - ) { - DimensionStackContext stackContext = engine.getDimensionStackContext(); - return stackContext == null ? null : stackContext.getLayerAt(blockX, internalY, blockZ); - } - - private static IrisBiome resolveSurfaceStructureBiome(Engine engine, int blockX, int blockZ) { - return engine.getComplex().getTrueBiomeStream().get(blockX, blockZ); - } - - private static Holder resolveBiomeHolder(Registry registry, String biomeKey) { - if (registry == null || biomeKey == null || biomeKey.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(biomeKey); - if (identifier == null) { - return null; - } - return registry.get(ResourceKey.create(Registries.BIOME, identifier)).orElse(null); - } - - private static Holder resolveFallbackBiome(Registry registry, Registry customRegistry) { - Holder plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS); - if (plains != null) { - return plains; - } - - Holder vanilla = firstHolder(registry); - if (vanilla != null) { - return vanilla; - } - - return firstHolder(customRegistry); - } - - private static Holder firstHolder(Registry registry) { - if (registry == null) { - return null; - } - - for (Biome biome : registry) { - Optional> optionalBiomeKey = registry.getResourceKey(biome); - if (optionalBiomeKey.isEmpty()) { - continue; - } - - Optional> optionalHolder = registry.get(optionalBiomeKey.get()); - if (optionalHolder.isPresent()) { - return optionalHolder.get(); - } - } - - return null; - } - - private static final class BiomeResolution { - private final IrisBiome irisBiome; - private final boolean underground; - private final IrisDimension dimension; - private final int blockX; - private final int blockY; - private final int blockZ; - private final RNG rng; - - private BiomeResolution( - IrisBiome irisBiome, - boolean underground, - IrisDimension dimension, - int blockX, - int blockY, - int blockZ, - RNG rng - ) { - this.irisBiome = irisBiome; - this.underground = underground; - this.dimension = dimension; - this.blockX = blockX; - this.blockY = blockY; - this.blockZ = blockZ; - this.rng = rng; - } - } - - private record OwnedBiome(IrisDimension dimension, IrisBiome biome) { - } - - private record RuntimeNoiseKey(int runtimeId, long coordinateKey) { - } - - private record RuntimeColumnKey(int runtimeId, long coordinateKey) { - } - - private record RuntimeBiomeState( - IrisDimension dimension, - KMap> customBiomes, - Map> vanillaSpawnBiomes - ) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/DatapackStructureStateFilter.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/DatapackStructureStateFilter.java deleted file mode 100644 index cfcdb820a..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/DatapackStructureStateFilter.java +++ /dev/null @@ -1,513 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement; -import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; -import art.arcane.iris.structure.nativegen.NativeStructureFrequencyScale; -import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderOwner; -import net.minecraft.core.Vec3i; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -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; - -final class DatapackStructureStateFilter { - private DatapackStructureStateFilter() { - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources - ) { - return filter(structureSets, scopeIndex, declaredSources, new IrisImportedStructureControl()); - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures - ) { - return filter( - structureSets, - scopeIndex, - declaredSources, - importedStructures, - keyIndex(structureSets)); - } - - static Selection filter( - List> structureSets, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures, - StructureSetKeyIndex keyIndex - ) { - if (scopeIndex.isEmpty()) { - return new Selection( - scaleFrequencyOverrides(structureSets, importedStructures, keyIndex), - 0, - 0); - } - Map> holdersByKey = new HashMap<>(); - for (Holder holder : structureSets) { - String key = structureSetKey(holder, keyIndex); - if (key != null) { - holdersByKey.putIfAbsent(key, holder); - } - } - Map, Holder> scopedByIdentity = new IdentityHashMap<>(); - Set> visiting = Collections.newSetFromMap(new IdentityHashMap<>()); - List> filteredSets = new ArrayList<>(structureSets.size()); - int retainedManagedSets = 0; - int excludedManagedSets = 0; - for (Holder holder : structureSets) { - String setKey = structureSetKey(holder, keyIndex); - boolean managedSet = setKey != null && scopeIndex.isManagedStructureSet(setKey); - Holder scopedHolder = scopeHolder( - holder, - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - if (scopedHolder == null) { - if (managedSet) { - excludedManagedSets++; - } - continue; - } - if (managedSet) { - retainedManagedSets++; - } - filteredSets.add(scopedHolder); - } - return new Selection( - scaleFrequencyOverrides( - List.copyOf(filteredSets), importedStructures, keyIndex), - retainedManagedSets, - excludedManagedSets); - } - - static String structureSetKey(Holder holder) { - return structureSetKey(holder, keyIndex(List.of(holder))); - } - - static StructureSetKeyIndex keyIndex( - Iterable> registeredStructureSets - ) { - return StructureSetKeyIndex.create(registeredStructureSets); - } - - private static String structureSetKey( - Holder holder, - StructureSetKeyIndex keyIndex - ) { - return keyIndex.resolve(holder) - .map(key -> key.identifier().toString()) - .orElse(null); - } - - private static List filterEntries( - List entries, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources - ) { - List filtered = new ArrayList<>(entries.size()); - for (StructureSet.StructureSelectionEntry entry : entries) { - String structureKey = entry.structure().unwrapKey() - .map(key -> key.identifier().toString()) - .orElse(null); - if (structureKey == null || scopeIndex.allowsStructure(structureKey, declaredSources)) { - filtered.add(entry); - } - } - return filtered; - } - - private static Holder scopeHolder( - Holder holder, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - Map> holdersByKey, - Map, Holder> scopedByIdentity, - Set> visiting, - StructureSetKeyIndex keyIndex - ) { - if (scopedByIdentity.containsKey(holder)) { - return scopedByIdentity.get(holder); - } - if (!visiting.add(holder)) { - return null; - } - try { - String setKey = structureSetKey(holder, keyIndex); - if (setKey != null && scopeIndex.isManagedStructureSet(setKey) - && !scopeIndex.allowsStructureSet(setKey, declaredSources)) { - scopedByIdentity.put(holder, null); - return null; - } - StructureSet originalSet = holder.value(); - List entries = filterEntries( - originalSet.structures(), scopeIndex, declaredSources); - if (entries.isEmpty()) { - scopedByIdentity.put(holder, null); - return null; - } - StructurePlacement placement = scopePlacement( - originalSet.placement(), - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - if (entries.size() == originalSet.structures().size() - && placement == originalSet.placement()) { - scopedByIdentity.put(holder, holder); - return holder; - } - Holder scoped = replacementHolder( - holder, new StructureSet(entries, placement), keyIndex); - scopedByIdentity.put(holder, scoped); - return scoped; - } finally { - visiting.remove(holder); - } - } - - static Optional exclusionZone(StructurePlacement placement) { - if (!(placement instanceof AbstractSpreadingStructurePlacement)) { - return Optional.empty(); - } - Object value = declaredFieldValue(AbstractSpreadingStructurePlacement.class, placement, Optional.class); - if (value instanceof Optional optional && (optional.isEmpty() - || optional.get() instanceof AbstractSpreadingStructurePlacement.ExclusionZone)) { - @SuppressWarnings("unchecked") - Optional resolved = - (Optional) optional; - return resolved; - } - throw new IllegalStateException("Could not read structure placement exclusion zone from " - + placement.getClass().getName()); - } - - private static StructurePlacement scopePlacement( - StructurePlacement placement, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - Map> holdersByKey, - Map, Holder> scopedByIdentity, - Set> visiting, - StructureSetKeyIndex keyIndex - ) { - Optional currentZone = exclusionZone(placement); - if (currentZone.isEmpty()) { - return placement; - } - - Holder target = currentZone.get().otherSet(); - String targetKey = structureSetKey(target, keyIndex); - if (targetKey != null) { - target = holdersByKey.getOrDefault(targetKey, target); - } - Holder scopedTarget = scopeHolder( - target, - scopeIndex, - declaredSources, - holdersByKey, - scopedByIdentity, - visiting, - keyIndex); - Optional scopedZone = Optional.empty(); - if (scopedTarget != null) { - scopedZone = Optional.of(new AbstractSpreadingStructurePlacement.ExclusionZone( - scopedTarget, - currentZone.get().chunkCount())); - } - if (scopedTarget == currentZone.get().otherSet()) { - return placement; - } - return copyPlacement(placement, scopedZone, 1D); - } - - private static List> scaleFrequencyOverrides( - List> structureSets, - IrisImportedStructureControl importedStructures, - StructureSetKeyIndex keyIndex - ) { - if (!importedStructures.hasFrequencyOverrides()) { - return structureSets; - } - Map> holdersByKey = new HashMap<>(); - for (Holder holder : structureSets) { - String key = structureSetKey(holder, keyIndex); - if (key != null) { - holdersByKey.putIfAbsent(key, holder); - } - } - Map, Holder> scaledByIdentity = new IdentityHashMap<>(); - List> scaledSets = new ArrayList<>(structureSets.size()); - boolean changed = false; - for (Holder holder : structureSets) { - Holder scaled = scaleFrequencyHolder( - holder, - importedStructures, - holdersByKey, - scaledByIdentity, - keyIndex); - scaledSets.add(scaled); - changed |= scaled != holder; - } - return changed ? List.copyOf(scaledSets) : structureSets; - } - - private static Holder scaleFrequencyHolder( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey, - Map, Holder> scaledByIdentity, - StructureSetKeyIndex keyIndex - ) { - if (scaledByIdentity.containsKey(holder)) { - return scaledByIdentity.get(holder); - } - if (!dependsOnFrequencyOverride( - holder, importedStructures, holdersByKey, keyIndex)) { - scaledByIdentity.put(holder, holder); - return holder; - } - - ResourceKey holderKey = keyIndex.resolve(holder).orElseThrow(() -> - new IllegalStateException( - "An affected native structure-set exclusion graph has an unkeyed holder")); - ReboundStructureSetHolder scaledHolder = new ReboundStructureSetHolder(holderKey); - scaledByIdentity.put(holder, scaledHolder); - - StructureSet originalSet = holder.value(); - StructurePlacement originalPlacement = originalSet.placement(); - Optional originalZone = exclusionZone(originalPlacement); - Optional scaledZone = originalZone; - if (originalZone.isPresent()) { - Holder target = canonicalHolder( - originalZone.get().otherSet(), holdersByKey, keyIndex); - Holder scaledTarget = scaleFrequencyHolder( - target, - importedStructures, - holdersByKey, - scaledByIdentity, - keyIndex); - if (scaledTarget != originalZone.get().otherSet()) { - scaledZone = Optional.of(new AbstractSpreadingStructurePlacement.ExclusionZone( - scaledTarget, originalZone.get().chunkCount())); - } - } - - double multiplier = importedStructures.frequencyMultiplier( - structureSetKey(holder, keyIndex)); - StructurePlacement scaledPlacement = multiplier == 1D && scaledZone.equals(originalZone) - ? originalPlacement - : copyPlacement(originalPlacement, scaledZone, multiplier); - scaledHolder.bind(new StructureSet(originalSet.structures(), scaledPlacement)); - return scaledHolder; - } - - private static boolean dependsOnFrequencyOverride( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey, - StructureSetKeyIndex keyIndex - ) { - Set> visited = Collections.newSetFromMap(new IdentityHashMap<>()); - Holder current = holder; - while (visited.add(current)) { - if (importedStructures.frequencyMultiplier( - structureSetKey(current, keyIndex)) != 1D) { - return true; - } - Optional zone = - exclusionZone(current.value().placement()); - if (zone.isEmpty()) { - return false; - } - current = canonicalHolder(zone.get().otherSet(), holdersByKey, keyIndex); - } - return false; - } - - private static Holder canonicalHolder( - Holder holder, - Map> holdersByKey, - StructureSetKeyIndex keyIndex - ) { - String key = structureSetKey(holder, keyIndex); - return key == null ? holder : holdersByKey.getOrDefault(key, holder); - } - - private static Holder replacementHolder( - Holder original, - StructureSet replacement, - StructureSetKeyIndex keyIndex - ) { - Optional> key = keyIndex.resolve(original); - if (key.isEmpty()) { - return Holder.direct(replacement); - } - ReboundStructureSetHolder holder = new ReboundStructureSetHolder(key.get()); - holder.bind(replacement); - return holder; - } - - private static StructurePlacement copyPlacement( - StructurePlacement placement, - Optional exclusionZone, - double frequencyMultiplier - ) { - Vec3i locateOffset = (Vec3i) declaredFieldValue( - AbstractSpreadingStructurePlacement.class, placement, Vec3i.class); - AbstractSpreadingStructurePlacement.FrequencyReductionMethod frequencyReductionMethod = - (AbstractSpreadingStructurePlacement.FrequencyReductionMethod) declaredFieldValue( - AbstractSpreadingStructurePlacement.class, - placement, - AbstractSpreadingStructurePlacement.FrequencyReductionMethod.class); - float frequency = (float) declaredFieldValue( - AbstractSpreadingStructurePlacement.class, placement, float.class); - int salt = (int) declaredFieldValue( - AbstractSpreadingStructurePlacement.class, placement, int.class); - - if (placement instanceof RandomSpreadStructurePlacement randomSpread) { - NativeStructureFrequencyScale scale = NativeStructureFrequencyScale.randomSpread( - frequency, randomSpread.spacing(), randomSpread.separation(), frequencyMultiplier); - return new RandomSpreadStructurePlacement( - locateOffset, - frequencyReductionMethod, - scale.frequency(), - salt, - exclusionZone, - scale.spacing(), - randomSpread.separation(), - randomSpread.spreadType()); - } - if (placement.getClass() == ConcentricRingsStructurePlacement.class) { - ConcentricRingsStructurePlacement rings = - (ConcentricRingsStructurePlacement) placement; - float scaledFrequency = NativeStructureFrequencyScale.probability( - frequency, frequencyMultiplier); - return new ConcentricRingsStructurePlacement( - locateOffset, - frequencyReductionMethod, - scaledFrequency, - salt, - exclusionZone, - rings.distance(), - rings.spread(), - rings.count(), - rings.preferredBiomes()); - } - throw new IllegalStateException("Unsupported native structure placement in an affected " - + "frequency-override or datapack-scope graph: " - + placement.getClass().getName()); - } - - private static Object declaredFieldValue( - Class declaringType, - Object target, - Class fieldType - ) { - Field match = null; - for (Field field : declaringType.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != fieldType) { - continue; - } - if (match != null) { - throw new IllegalStateException("Ambiguous " + fieldType.getName() + " field on " - + declaringType.getName()); - } - match = field; - } - if (match == null) { - throw new IllegalStateException("Missing " + fieldType.getName() + " field on " - + declaringType.getName()); - } - try { - match.setAccessible(true); - return match.get(target); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not read " + fieldType.getName() + " field on " - + declaringType.getName(), e); - } - } - - static final class StructureSetKeyIndex { - private final Map, ResourceKey> - keysByEntries; - - private StructureSetKeyIndex( - Map, ResourceKey> - keysByEntries - ) { - this.keysByEntries = keysByEntries; - } - - private static StructureSetKeyIndex create( - Iterable> registeredStructureSets - ) { - Map, ResourceKey> keys = - new IdentityHashMap<>(); - for (Holder holder : registeredStructureSets) { - Optional> key = holder.unwrapKey(); - if (key.isEmpty()) { - continue; - } - List entries = holder.value().structures(); - ResourceKey previous = keys.put(entries, key.get()); - if (previous != null && !previous.equals(key.get())) { - throw new IllegalStateException( - "Multiple registered structure sets share the same entry list"); - } - } - return new StructureSetKeyIndex(keys); - } - - private Optional> resolve(Holder holder) { - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - return key; - } - return Optional.ofNullable(keysByEntries.get(holder.value().structures())); - } - } - - record Selection( - List> structureSets, - int retainedManagedSets, - int excludedManagedSets - ) { - } - - private static final class ReboundStructureSetHolder extends Holder.Reference { - private ReboundStructureSetHolder(ResourceKey key) { - super(Type.STAND_ALONE, new HolderOwner<>() { - }, key, null); - } - - private void bind(StructureSet structureSet) { - bindValue(structureSet); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/ImportedFeatureStage.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/ImportedFeatureStage.java deleted file mode 100644 index a5d81510a..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/ImportedFeatureStage.java +++ /dev/null @@ -1,441 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import net.minecraft.world.level.levelgen.placement.FeaturePlacer; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.nativegen.v26_3_R1.NativeGenerationWriteGuard; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeFeatureGenerationPolicy; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.decoration.IrisDecorationStep; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.structure.nativegen.IrisImportedFeatureControl; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import art.arcane.iris.spi.IrisLogging; -import it.unimi.dsi.fastutil.ints.IntArraySet; -import it.unimi.dsi.fastutil.ints.IntSet; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.FeatureSorter; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.IntBinaryOperator; - -/** - * Bukkit twin of the modded imported-feature stage. Same control, same semantics, same seeds: with - * {@code importedFeatures.enabled} the vanilla placed-feature pass runs over Iris terrain, and with the - * control off nothing here allocates or runs. - * - *

Iris does its own native structure pass and calls the delegate with {@code addVanillaDecorations=false}, - * so the vanilla feature half never runs on its own. This reproduces that half only - structures are never - * placed twice. - * - *

Threading: called from {@code applyBiomeDecoration} on the worldgen thread that owns the chunk. The - * FEATURES chunk step is not parallel-safe. - */ -final class ImportedFeatureStage { - private static final String CYCLE_MARKER = "Feature order cycle found"; - - private final Engine engine; - private final ConcurrentHashMap runtimeStates = new ConcurrentHashMap<>(); - - ImportedFeatureStage(Engine engine) { - this.engine = engine; - } - - /** - * Generation settings for one biome holder, mapping Iris custom biomes onto their vanilla derivative. - * Pass-through while the control is off, which is what keeps {@code BiomeFilter} behaving exactly as it - * does today. - */ - BiomeGenerationSettings generationSettings(Holder biome) { - RuntimeFeatureState state = currentState(); - FeatureTable table = state == null ? null : state.featureTable(); - if (table == null) { - return null; - } - Holder mapped = table.derivatives().get(holderKey(biome)); - return mapped == null ? null : mapped.value().getGenerationSettings(); - } - - /** - * Builds the table on the first decorated chunk, or leaves the stage inert. A feature-order cycle is - * reported once here and degrades to features-off; it never reaches chunk generation as a crash. - */ - void prepare(WorldGenLevel level) { - IrisDimension dimension = engine.getDimension(); - int runtimeId = engine.getCacheID(); - if (settled(runtimeStates.get(runtimeId), dimension)) { - return; - } - synchronized (this) { - if (settled(runtimeStates.get(runtimeId), dimension)) { - return; - } - runtimeStates.put(runtimeId, build(level, dimension)); - } - } - - void evictRuntime(int runtimeId) { - runtimeStates.remove(runtimeId); - } - - private RuntimeFeatureState currentState() { - IrisDimension dimension = engine.getDimension(); - RuntimeFeatureState state = runtimeStates.get(engine.getCacheID()); - return settled(state, dimension) ? state : null; - } - - private static boolean settled(RuntimeFeatureState state, IrisDimension dimension) { - return state != null && state.dimension() == dimension; - } - - private RuntimeFeatureState build(WorldGenLevel level, IrisDimension dimension) { - IrisImportedFeatureControl control; - try { - control = NativeFeatureGenerationPolicy.control(engine); - } catch (RuntimeException error) { - IrisLogging.error("Iris could not read importedFeatures for this dimension; features off: " - + error); - return new RuntimeFeatureState(dimension, null); - } - if (!control.shouldGenerateFeatures()) { - return new RuntimeFeatureState(dimension, null); - } - FeatureTable built; - try { - built = buildTable(level, control, dimension); - } catch (Throwable error) { - IrisLogging.error("Iris importedFeatures is off for " + dimensionKey() - + ": feature table construction failed: " + error); - return new RuntimeFeatureState(dimension, null); - } - if (built == null) { - return new RuntimeFeatureState(dimension, null); - } - IrisLogging.info("Iris importedFeatures on for " + dimensionKey() + ": " + built.biomes().size() - + " biomes, " + built.steps().size() + " steps, " + built.derivatives().size() - + " custom-biome derivative maps"); - return new RuntimeFeatureState(dimension, built); - } - - private FeatureTable buildTable(WorldGenLevel level, IrisImportedFeatureControl control, - IrisDimension dimension) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.BIOME); - Set visibleKeys = visibleBiomeKeys(); - // Registry-ordered walk, never a hash-ordered set: FeatureSorter's cycle detection walks this list and - // an unordered walk makes detection depend on JVM hash order. - List> biomes = new ArrayList<>(); - Map> byKey = new HashMap<>(); - registry.listElements().forEach((Holder.Reference reference) -> { - String key = holderKey(reference); - if (key != null && visibleKeys.contains(key)) { - biomes.add(reference); - byKey.put(key, reference); - } - }); - if (biomes.isEmpty()) { - IrisLogging.error("Iris importedFeatures is on but " + dimensionKey() - + " exposes no registered biomes; features off"); - return null; - } - Map> derivatives = customBiomeDerivatives(registry, byKey); - List steps; - try { - steps = FeatureSorter.buildFeaturesPerStep(biomes, - (Holder biome) -> settingsFor(biome, derivatives).features(), true); - } catch (IllegalStateException error) { - String message = error.getMessage(); - if (message == null || !message.contains(CYCLE_MARKER)) { - throw error; - } - IrisLogging.error("Iris importedFeatures is off for " + dimensionKey() - + ": the registered placed features cannot be ordered. " + message - + ". Remove or reorder the conflicting content, or leave importedFeatures.enabled false."); - return null; - } - boolean filtered = control.getDisabled() != null && !control.getDisabled().isEmpty(); - return new FeatureTable(dimension, control, List.copyOf(biomes), Set.copyOf(biomes), - Map.copyOf(byKey), steps, Map.copyOf(derivatives), filtered); - } - - /** - * Every biome key Iris can write into a chunk section: the structure derivative, the raw derivative, every - * scatter entry, and every generated custom biome. - */ - private Set visibleBiomeKeys() { - Set keys = new LinkedHashSet<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - addKey(keys, irisBiome.getStructureDerivativeKey()); - addKey(keys, irisBiome.getDerivativeKey()); - addKey(keys, irisBiome.getVanillaDerivativeKey()); - for (String scatter : irisBiome.getBiomeScatter()) { - addKey(keys, scatter); - } - for (String scatter : irisBiome.getBiomeSkyScatter()) { - addKey(keys, scatter); - } - if (!irisBiome.isCustom()) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - addKey(keys, engine.getData().customBiomeResourceKey(engine.getDimension(), customBiome)); - } - } - return keys; - } - - /** - * Maps every generated Iris custom biome key onto the registry holder of its Iris biome's vanilla - * derivative. The custom biome's own datapack JSON carries no features by design. - */ - private Map> customBiomeDerivatives(Registry registry, - Map> byKey) { - Map> derivatives = new HashMap<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - if (!irisBiome.isCustom()) { - continue; - } - String derivativeKey = normalizeKey(irisBiome.getVanillaDerivativeKey()); - // Resolve from the registry, not only from the visible biome set: a sea or shore biome's structure - // derivative is rewritten away from its vanilla derivative, so the derivative whose features we - // want is not always a biome Iris can emit. - Holder derivative = byKey.get(derivativeKey); - if (derivative == null) { - derivative = resolveHolder(registry, derivativeKey); - } - if (derivative == null) { - IrisLogging.warn("Iris importedFeatures: vanilla derivative " + derivativeKey + " of biome " - + irisBiome.getLoadKey() - + " is not registered; its custom biomes generate no imported features"); - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - derivatives.put( - engine.getData().customBiomeResourceKey(engine.getDimension(), customBiome), - derivative - ); - } - } - return derivatives; - } - - private static Holder resolveHolder(Registry registry, String key) { - if (registry == null || key == null || key.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return null; - } - return registry.get(identifier).>map((Holder.Reference reference) -> reference) - .orElse(null); - } - - private static BiomeGenerationSettings settingsFor(Holder biome, - Map> derivatives) { - Holder mapped = derivatives.get(holderKey(biome)); - return mapped == null - ? biome.value().getGenerationSettings() - : mapped.value().getGenerationSettings(); - } - - /** - * Runs the vanilla placed-feature pass for one chunk on the calling worldgen thread. A no-op while the - * control is disabled or the table degraded. - */ - void run(WorldGenLevel level, ChunkAccess chunk, ChunkGenerator owner) { - RuntimeFeatureState state = currentState(); - FeatureTable table = state == null ? null : state.featureTable(); - if (table == null) { - return; - } - ChunkPos centerPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(centerPos, level.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry featureRegistry = level.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); - List steps = table.steps(); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ()); - IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); - int staticMinY = engine.getMinHeight(); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - Set> chunkBiomes = chunkBiomes(level, sectionPos, table, stackContext); - IntBinaryOperator surfaceFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, false) + staticMinY + 1; - IntBinaryOperator floorFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, true) + staticMinY + 1; - WorldGenLevel placementLevel = staticObjects.isEmpty() && stackContext == null - ? level - : NativeStructureWorldgenAccess.create( - level, centerPos, - surfaceFirstFreeY, - floorFirstFreeY, - stackContext != null, - NativeGenerationWriteGuard.protectedPositions(staticObjects, stackContext, staticMinY)); - - try { - for (int stepIndex = 0; stepIndex < steps.size(); stepIndex++) { - if (!table.control().shouldGenerateStep(IrisDecorationStep.byOrdinal(stepIndex))) { - continue; - } - placeStep(placementLevel, table, steps.get(stepIndex), featureRegistry, chunkBiomes, owner, - random, decorationSeed, origin, stepIndex); - } - } catch (Throwable error) { - throw new IllegalStateException("Iris imported feature placement failed for chunk " - + centerPos.x() + "," + centerPos.z(), error); - } finally { - level.setCurrentlyGenerating(null); - } - } - - private void placeStep(WorldGenLevel level, FeatureTable table, FeatureSorter.StepFeatureData stepData, - Registry featureRegistry, Set> chunkBiomes, - ChunkGenerator owner, WorldgenRandom random, long decorationSeed, - BlockPos origin, int stepIndex) { - IntSet stepFeatures = new IntArraySet(); - for (Holder biome : chunkBiomes) { - List> biomeFeatures = settingsFor(biome, table.derivatives()).features(); - if (stepIndex >= biomeFeatures.size()) { - continue; - } - for (Holder feature : biomeFeatures.get(stepIndex)) { - stepFeatures.add(stepData.indexMapping().applyAsInt(feature.value())); - } - } - if (stepFeatures.isEmpty()) { - return; - } - // Sorted global indices: identical ordering to vanilla, and each feature's seed comes from its own - // global index, so denying one feature never shifts another. - int[] featureIndices = stepFeatures.toIntArray(); - Arrays.sort(featureIndices); - for (int globalIndex : featureIndices) { - PlacedFeature feature = stepData.features().get(globalIndex); - if (table.filtered()) { - Identifier featureId = featureRegistry.getKey(feature); - if (featureId != null && !table.control().shouldGenerate(featureId.toString())) { - continue; - } - } - random.setFeatureSeed(decorationSeed, globalIndex, stepIndex); - level.setCurrentlyGenerating(() -> describeFeature(featureRegistry, feature)); - new FeaturePlacer(level, owner).placeWithBiomeCheck(feature, random, origin); - } - } - - private static String describeFeature(Registry registry, PlacedFeature feature) { - Identifier id = registry.getKey(feature); - return id == null ? feature.toString() : id.toString(); - } - - private Set> chunkBiomes( - WorldGenLevel level, - SectionPos sectionPos, - FeatureTable table, - DimensionStackContext stackContext - ) { - List> collected = new ArrayList<>(); - ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach((ChunkPos chunkPos) -> { - ChunkAccess neighbour = level.getChunk(chunkPos.x(), chunkPos.z()); - LevelChunkSection[] sections = neighbour.getSections(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - if (stackContext == null) { - section.getBiomes().getAll(collected::add); - continue; - } - int sectionMinimumY = neighbour.getSectionYFromSectionIndex(sectionIndex) << 4; - for (int quartY = 0; quartY < 4; quartY++) { - int internalY = sectionMinimumY + (quartY << 2) - level.getMinY(); - for (int quartX = 0; quartX < 4; quartX++) { - int blockX = chunkPos.getMinBlockX() + (quartX << 2); - for (int quartZ = 0; quartZ < 4; quartZ++) { - int blockZ = chunkPos.getMinBlockZ() + (quartZ << 2); - DimensionStackLayout.Layer layer = stackContext.getLayerAt( - blockX, internalY, blockZ); - if (layer.terrainContext().isSelfReferencing()) { - collected.add(section.getBiomes().get(quartX, quartY, quartZ)); - } - } - } - } - } - }); - Set> present = new LinkedHashSet<>(); - for (Holder biome : collected) { - if (table.biomeSet().contains(biome)) { - present.add(biome); - continue; - } - String key = holderKey(biome); - Holder canonical = key == null ? null : table.byKey().get(key); - if (canonical != null) { - present.add(canonical); - } - } - return present; - } - - private static void addKey(Set keys, String key) { - String normalized = normalizeKey(key); - if (normalized != null) { - keys.add(normalized); - } - } - - private String dimensionKey() { - IrisDimension dimension = engine.getDimension(); - return dimension == null ? "" : dimension.getLoadKey(); - } - - private static String holderKey(Holder holder) { - return holder.unwrapKey() - .map(key -> key.identifier().toString().toLowerCase(Locale.ROOT)) - .orElse(null); - } - - private static String normalizeKey(String key) { - Identifier identifier = key == null ? null : Identifier.tryParse(key); - return identifier == null ? null : identifier.toString().toLowerCase(Locale.ROOT); - } - - private record FeatureTable(IrisDimension dimension, IrisImportedFeatureControl control, - List> biomes, Set> biomeSet, - Map> byKey, - List steps, - Map> derivatives, boolean filtered) { - } - - private record RuntimeFeatureState(IrisDimension dimension, FeatureTable featureTable) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/InitialMobSpawner.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/InitialMobSpawner.java deleted file mode 100644 index e04bdbd37..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/InitialMobSpawner.java +++ /dev/null @@ -1,132 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.attribute.EnvironmentAttributes; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.EntitySpawnReason; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.SpawnGroupData; -import net.minecraft.world.entity.SpawnPlacements; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.gamerules.GameRules; -import org.bukkit.event.entity.CreatureSpawnEvent; -import java.util.Optional; - -final class InitialMobSpawner { - private InitialMobSpawner() { - } - - static void spawn( - WorldGenRegion level, Holder biome, ChunkPos chunkPos, RandomSource random - ) { - if (level.getLevel().getGameRules().get(GameRules.SPAWN_MOBS)) { - MobSpawnSettings mobSettings = biome.value().getAttributes().applyModifier( - EnvironmentAttributes.NATURAL_MOB_SPAWNS, EnvironmentAttributes.NATURAL_MOB_SPAWNS.defaultValue()); - WeightedList mobs = mobSettings.getMobsToSpawn(MobCategory.CREATURE); - if (!mobs.isEmpty()) { - float creatureProbability = biome.value().getAttributes().applyModifier( - EnvironmentAttributes.CREATURE_WORLD_GEN_SPAWN_PROBABILITY, - EnvironmentAttributes.CREATURE_WORLD_GEN_SPAWN_PROBABILITY.defaultValue()); - int xo = chunkPos.getMinBlockX(); - int zo = chunkPos.getMinBlockZ(); - - while (random.nextFloat() < creatureProbability) { - Optional nextSpawnerData = mobs.getRandom(random); - if (!nextSpawnerData.isEmpty()) { - MobSpawnSettings.SpawnerData spawnerData = nextSpawnerData.get(); - int count = spawnerData.count().sample(random); - SpawnGroupData groupSpawnData = null; - int x = xo + random.nextInt(16); - int z = zo + random.nextInt(16); - int startX = x; - int startZ = z; - - for (int i = 0; i < count; i++) { - boolean success = false; - - for (int attempts = 0; !success && attempts < 4; attempts++) { - BlockPos pos = getTopNonCollidingPos(level, spawnerData.type(), x, z); - if (spawnerData.type().canSummon() && SpawnPlacements.isSpawnPositionOk(spawnerData.type(), level, pos)) { - float width = spawnerData.type().getWidth(); - double fx = Mth.clamp(x, (double)xo + width, xo + 16.0 - width); - double fz = Mth.clamp(z, (double)zo + width, zo + 16.0 - width); - if (!level.noCollision(spawnerData.type().getSpawnAABB(fx, pos.getY(), fz)) - || !SpawnPlacements.checkSpawnRules( - spawnerData.type(), - level, - EntitySpawnReason.CHUNK_GENERATION, - BlockPos.containing(fx, pos.getY(), fz), - level.getRandom() - )) { - continue; - } - - Entity entity; - try { - entity = spawnerData.type().create(level.getLevel(), EntitySpawnReason.NATURAL); - } catch (Exception e) { - IrisLogging.reportError("Failed to create initial mob at chunk " + chunkPos, e); - continue; - } - - if (entity == null) { - continue; - } - - entity.snapTo(fx, pos.getY(), fz, random.nextFloat() * 360.0F, 0.0F); - if (entity instanceof Mob mob - && mob.checkSpawnRules(level, EntitySpawnReason.CHUNK_GENERATION) - && mob.checkSpawnObstruction(level)) { - groupSpawnData = mob.finalizeSpawn( - level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, groupSpawnData - ); - level.addFreshEntityWithPassengers(mob, CreatureSpawnEvent.SpawnReason.CHUNK_GEN); - success = true; - } - } - - x += random.nextInt(5) - random.nextInt(5); - - for (z += random.nextInt(5) - random.nextInt(5); - x < xo || x >= xo + 16 || z < zo || z >= zo + 16; - z = startZ + random.nextInt(5) - random.nextInt(5) - ) { - x = startX + random.nextInt(5) - random.nextInt(5); - } - } - } - } - } - } - } - } - - private static BlockPos getTopNonCollidingPos(LevelReader level, EntityType type, int x, int z) { - int levelHeight = level.getHeight(SpawnPlacements.getHeightmapType(type), x, z); - BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(x, levelHeight, z); - if (level.dimensionType().hasCeiling()) { - do { - pos.move(Direction.DOWN); - } while (!level.getBlockState(pos).isAir()); - - do { - pos.move(Direction.DOWN); - } while (level.getBlockState(pos).isAir() && pos.getY() > level.getMinY()); - } - - return SpawnPlacements.getPlacementType(type).adjustSpawnPosition(level, pos.immutable()); - } - -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGenerator.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGenerator.java deleted file mode 100644 index 25e5a9234..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGenerator.java +++ /dev/null @@ -1,1621 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; -import net.minecraft.world.attribute.EnvironmentAttributes; -import net.minecraft.world.level.Level; -import art.arcane.iris.nativegen.v26_3_R1.NativeTransitionColumn; -import art.arcane.iris.nativegen.v26_3_R1.NativeTerrainHeightCache; -import art.arcane.iris.world.history.TerrainBoundarySignature; -import java.util.Optional; -import art.arcane.iris.nativegen.v26_3_R1.NativeGenerationWriteGuard; -import java.util.function.LongPredicate; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.EngineLifecycleTasks; -import art.arcane.iris.generation.runtime.GenerationSessionException; -import art.arcane.iris.generation.runtime.GenerationSessionLease; -import art.arcane.iris.structure.placement.IrisStructureLocator; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.world.history.NativeTerrainReceipt; -import art.arcane.iris.world.history.NativeBiomeSpawnSelection; -import art.arcane.iris.world.history.SavedTerrainChunk; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataType; -import art.arcane.iris.platform.generation.BukkitChunkGenerator; -import art.arcane.iris.studio.generation.JigsawStudioGenerator; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureGenerationException; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureStartInjector; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureReferenceEnvelope; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureLocateResults; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureLocatePersistence; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureOwnershipRecovery; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureReferenceRepair; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureSurfaceFitter; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureTerrainIntegrator; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureVegetationClearer; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureVerticalPlacer; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureVanillaLocator; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureVolumeIndex; -import art.arcane.iris.nativegen.v26_3_R1.WorldgenTerrainHeightmaps; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.iris.platform.reflect.WrappedField; -import art.arcane.iris.platform.reflect.WrappedReturningMethod; -import art.arcane.iris.generation.context.IrisContext; -import art.arcane.volmlib.util.math.RNG; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.util.random.Weighted; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.LegacyRandomSource; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.craftbukkit.generator.CustomChunkGenerator; -import org.bukkit.block.data.BlockData; -import org.spigotmc.SpigotWorldConfig; - -import javax.annotation.Nullable; -import java.lang.ref.WeakReference; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalInt; -import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CompletionException; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; - -public class IrisChunkGenerator extends CustomChunkGenerator implements LongPredicate { - private static final Set AUTHORING_HEIGHTMAPS = Set.of( - Heightmap.Types.WORLD_SURFACE_WG, - Heightmap.Types.OCEAN_FLOOR_WG, - Heightmap.Types.WORLD_SURFACE, - Heightmap.Types.OCEAN_FLOOR, - Heightmap.Types.MOTION_BLOCKING, - Heightmap.Types.MOTION_BLOCKING_NO_LEAVES); - private static final WrappedField BIOME_SOURCE; - private static final WrappedReturningMethod SET_HEIGHT; - private static final Runnable NO_OP = () -> { - }; - private final ChunkGenerator delegate; - private final Engine engine; - private final CustomBiomeSource customBiomeSource; - private final ServerLevel runtimeLevel; - private final @Nullable BukkitChunkGenerator platformGenerator; - private final int runtimeMinY; - private final int runtimeHeight; - private final int runtimeSeaLevel; - private final ConcurrentHashMap> mergedSpawnTables = new ConcurrentHashMap<>(); - private final ImportedFeatureStage importedFeatures; - private final NativeTerrainHeightCache terrainHeights = new NativeTerrainHeightCache(); - private final AtomicReference retainedStudioStructureState = new AtomicReference<>(); - private volatile ReachableStructureCache reachableStructureCache; - private volatile StructureStepCache structureStepCache; - - public IrisChunkGenerator(ChunkGenerator delegate, long seed, Engine engine, World world) { - this(delegate, engine, world, new CustomBiomeSource(seed, engine, world)); - } - - private IrisChunkGenerator(ChunkGenerator delegate, Engine engine, World world, CustomBiomeSource customBiomeSource) { - super(((CraftWorld) world).getHandle(), edit(delegate, customBiomeSource), world.getGenerator()); - this.delegate = delegate; - this.engine = engine; - this.customBiomeSource = customBiomeSource; - this.importedFeatures = new ImportedFeatureStage(engine); - ServerLevel level = ((CraftWorld) world).getHandle(); - this.runtimeLevel = level; - this.platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator - ? bukkitGenerator - : null; - this.runtimeMinY = level.getMinY(); - this.runtimeHeight = level.getHeight(); - this.runtimeSeaLevel = runtimeMinY + engine.getDimension().getFluidHeight(); - if (engine instanceof IrisEngine irisEngine) { - irisEngine.addGenerationRuntimeRetirementListener(this::evictRuntimeCaches); - } - installNativeStructureVolumeIndex(level); - } - - private void evictRuntimeCaches(int runtimeId) { - terrainHeights.evictRuntime(runtimeId); - importedFeatures.evictRuntime(runtimeId); - mergedSpawnTables.keySet().removeIf(key -> key.runtimeId() == runtimeId); - ReachableStructureCache reachable = reachableStructureCache; - if (reachable != null && reachable.runtimeId() == runtimeId) { - reachableStructureCache = null; - } - StructureStepCache steps = structureStepCache; - if (steps != null && steps.runtimeId() == runtimeId) { - structureStepCache = null; - } - } - - private void installNativeStructureVolumeIndex(ServerLevel level) { - WeakReference levelReference = new WeakReference<>(level); - WeakReference generatorReference = new WeakReference<>(this); - WeakReference biomeSourceReference = new WeakReference<>(customBiomeSource); - NativeStructureVolumeIndex.install(engine, new NativeStructureVolumeIndex.Context( - level.registryAccess(), - level.getServer().getStructureTemplateManager(), - level.dimension(), - LevelHeightAccessor.create(runtimeMinY, runtimeHeight), - generatorReference::get, - biomeSourceReference::get, - () -> { - ServerLevel active = levelReference.get(); - return active == null ? null : active.getChunkSource().getGeneratorState(); - })); - } - - @Override - public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return null; - } - if (level != runtimeLevel || level.getChunkSource().getGenerator() != this) { - return null; - } - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - SectionPos.blockToSectionCoord(pos.getX()), - SectionPos.blockToSectionCoord(pos.getZ()), - "bukkit_nms_structure_locate"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_structure_locate"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - HolderSet reachable = filterReachableStructures(level, holders); - NativeStructureVanillaLocator.Candidate nativeCandidate = - reachable == null || reachable.size() == 0 ? null - : NativeStructureVanillaLocator.predict( - level, reachable, pos, radius, findUnexplored); - return findNearestIrisStructure( - level, holders, pos, Math.max(0, radius), - findUnexplored, nativeCandidate); - } - } - - private Pair> findNearestIrisStructure(ServerLevel level, - HolderSet holders, - BlockPos pos, int radius, - boolean findUnexplored, - NativeStructureVanillaLocator.Candidate nativeCandidate) { - Pair> nativeLocated = - nativeCandidate == null ? null : nativeCandidate.result(); - Runnable nativeReference = () -> { - if (nativeCandidate != null) { - nativeCandidate.reference(level.structureManager()); - } - }; - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List searches = new ArrayList<>(holders.size()); - NativeStructureLocatePersistence.ProbeBudget budget = NativeStructureLocatePersistence.probeBudget(); - for (Holder holder : holders) { - Object id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure locate received an unregistered structure holder"); - } - String structureId = id.toString(); - if (!IrisStructureLocator.hasNativePlacement(engine, structureId)) { - continue; - } - NativeStructureLocatePersistence.Probe probe = NativeStructureLocatePersistence.probe( - level, holder.value(), findUnexplored, budget); - searches.add(new IrisNativeLocateSearch( - holder, structureId, NativeStructureLocatePersistence.search( - engine, structureId, pos.getX(), pos.getZ(), radius, probe))); - } - searches.sort(Comparator.comparing(IrisNativeLocateSearch::structureId)); - for (int attempt = 0; attempt < NativeStructureLocatePersistence.MAX_SELECTED_CANDIDATE_RETRIES; attempt++) { - IrisNativeLocateSearch bestSearch = null; - IrisStructureLocator.LocateResult bestResult = null; - long bestDistance = Long.MAX_VALUE; - for (IrisNativeLocateSearch search : searches) { - IrisStructureLocator.LocateResult result = search.search().predict(); - if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) { - throw new IllegalStateException("Iris structure locate reached its safety limit for " - + search.structureId() + " within " + radius + " placement rings"); - } - if (!result.found()) { - continue; - } - long dx = (long) result.originX() - pos.getX(); - long dz = (long) result.originZ() - pos.getZ(); - long distance = dx * dx + dz * dz; - if (distance < bestDistance) { - bestDistance = distance; - bestSearch = search; - bestResult = result; - } - } - if (bestSearch == null) { - return NativeStructureLocateResults.selectAndReference( - pos, null, () -> { }, nativeLocated, nativeReference); - } - Pair> predicted = Pair.of( - new BlockPos(bestResult.originX(), bestResult.baseY(), bestResult.originZ()), - bestSearch.holder()); - if (NativeStructureLocateResults.nearest(pos, predicted, nativeLocated) != predicted) { - return NativeStructureLocateResults.selectAndReference( - pos, predicted, () -> { }, nativeLocated, nativeReference); - } - NativeStructureLocatePersistence.VerifiedStart verified = - bestSearch.search().verify(bestResult); - if (verified == null) { - bestSearch.search().reject(bestResult); - continue; - } - BlockPos located = new BlockPos( - bestResult.originX(), verified.ownership().locatorY(), - bestResult.originZ()); - Pair> irisLocated = Pair.of(located, bestSearch.holder()); - IrisNativeLocateSearch selectedSearch = bestSearch; - NativeStructureLocatePersistence.VerifiedStart selectedStart = verified; - return NativeStructureLocateResults.selectAndReference( - pos, irisLocated, () -> selectedSearch.search().reference(selectedStart), - nativeLocated, nativeReference); - } - throw new IllegalStateException("Iris structure locate rejected too many selected candidates within " - + radius + " placement rings"); - } - - private HolderSet filterReachableStructures(ServerLevel level, HolderSet holders) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List candidates = new ArrayList<>(holders.size()); - for (Holder holder : holders) { - Object id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure filtering received an unregistered structure holder"); - } - String key = id.toString(); - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, - key, NativeStructureVegetationClearer.isUndergroundStep(holder.value().step())); - if (!decision.generate()) { - continue; - } - candidates.add(new NativeLocateCandidate(holder, key)); - } - if (candidates.isEmpty()) { - return HolderSet.direct(List.of()); - } - Set reachable = reachableStructureKeys(level); - List> kept = new ArrayList<>(candidates.size()); - for (NativeLocateCandidate candidate : candidates) { - if (reachable.contains(candidate.key())) { - kept.add(candidate.holder()); - } - } - if (kept.size() == holders.size()) { - return holders; - } - return HolderSet.direct(kept); - } - - private Set reachableStructureKeys(ServerLevel level) { - IrisDimension dimension = engine.getDimension(); - int runtimeId = engine.getCacheID(); - ReachableStructureCache cached = reachableStructureCache; - if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) { - return cached.keys(); - } - synchronized (this) { - cached = reachableStructureCache; - if (cached != null && cached.dimension() == dimension && cached.runtimeId() == runtimeId) { - return cached.keys(); - } - Set reachable = Set.copyOf( - VanillaStructureBiomes.reachableStructureKeys(level, customBiomeSource)); - reachableStructureCache = new ReachableStructureCache(dimension, runtimeId, reachable); - return reachable; - } - } - - @Override - protected MapCodec codec() { - return MapCodec.unit(null); - } - - @Override - public ChunkGenerator getDelegate() { - if (delegate instanceof CustomChunkGenerator chunkGenerator) - return chunkGenerator.getDelegate(); - return delegate; - } - - @Override - public int getMinY() { - return runtimeMinY; - } - - @Override - public int getSeaLevel() { - return runtimeSeaLevel; - } - - @Override - public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess access, StructureTemplateManager templateManager, ResourceKey levelKey) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return; - } - if (runtimeLevel.getChunkSource().getGenerator() != this - || runtimeLevel.getChunkSource().getGeneratorState() != structureState) { - return; - } - ChunkPos chunkPos = access.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_structures"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_structures"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_structures"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!allowsRoutedDiscreteGeneration(access, ChunkStatus.STRUCTURE_STARTS)) { - return; - } - Map previousStarts = new HashMap<>(access.getAllStarts()); - super.createStructures(registryAccess, structureState, structureManager, access, templateManager, levelKey); - Map configuredStarts = NativeStructureStartInjector.inject( - new NativeStructureStartInjector.InjectionContext( - engine, - registryAccess, - structureState, - structureManager, - access, - templateManager, - levelKey, - this, - customBiomeSource - )); - adjustGeneratedStructures( - registryAccess, access, previousStarts, configuredStarts, templateManager); - if (route != null) { - access.persistentDataContainer.set(new NamespacedKey("iris", "structure_activation"), - PersistentDataType.LONG, route.activation().activationId()); - access.markUnsaved(); - } - } - } - - private void adjustGeneratedStructures(RegistryAccess registryAccess, ChunkAccess access, - Map previousStarts, - Map configuredStarts, - StructureTemplateManager templateManager) { - Registry registry = registryAccess.lookupOrThrow(Registries.STRUCTURE); - ChunkPos chunkPos = access.getPos(); - for (Map.Entry entry : access.getAllStarts().entrySet()) { - Structure structure = entry.getKey(); - StructureStart start = entry.getValue(); - if (!start.isValid() || previousStarts.get(structure) == start) { - continue; - } - Identifier id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - BoundingBox footprint = start.getBoundingBox(); - if (!engine.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - access.setStartForStructure(structure, StructureStart.INVALID_START); - if (configuredStarts.containsKey(structure)) { - NativeStructureOwnershipStore.discard( - engine, structureId, chunkPos.x(), chunkPos.z()); - } - continue; - } - if (configuredStarts.containsKey(structure)) { - continue; - } - boolean undergroundStep = NativeStructureVegetationClearer.isUndergroundStep(structure.step()); - IrisNativeStructureDecision decision; - try { - decision = NativeStructureGenerationPolicy.resolve(engine, - structureId, undergroundStep); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "policy resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - if (!decision.generate()) { - access.setStartForStructure(structure, StructureStart.INVALID_START); - continue; - } - try { - NativeStructureVerticalPlacer.applyVerticalPlacement( - start, - structureId, - decision.yShift(), - getSeaLevel(), - access.getMinY(), - access.getMinY() + access.getHeight(), - undergroundStep, - decision.preserveSourceY(), - decision.yBand(), - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - StructureStart wrapped = NativeStructureReferenceEnvelope.wrapForPublication( - start, structure, start.getReferences(), - NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()), - structureId); - access.setStartForStructure(structure, wrapped); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vertical adjustment", structureId, chunkPos.x(), chunkPos.z(), error); - } - } - } - - @Override - public ChunkGeneratorStructureState createState(HolderLookup holderlookup, RandomState randomstate, long i, SpigotWorldConfig conf) { - return delegate.createState(holderlookup, randomstate, i, conf); - } - - void retainStudioStructureState( - ServerLevel level, - ChunkMap chunkMap, - ChunkGeneratorStructureState structureState - ) { - requireCurrentStructureOwner(level, chunkMap); - StudioStructureState retained = new StudioStructureState( - level, - chunkMap, - Objects.requireNonNull(structureState, "Studio native structure state")); - if (!retainedStudioStructureState.compareAndSet(null, retained)) { - throw new IllegalStateException("Studio native structure state is already retained."); - } - } - - StudioStructureState retainedStudioStructureState(ServerLevel level, ChunkMap chunkMap) { - StudioStructureState retained = retainedStudioStructureState.get(); - if (retained == null) { - return null; - } - requireCurrentStructureOwner(level, chunkMap); - if (retained.level() != level || retained.chunkMap() != chunkMap) { - throw new IllegalStateException("Retained Studio native structure state belongs to another world runtime."); - } - if (level.getChunkSource().getGeneratorState() != retained.structureState()) { - throw new IllegalStateException("Studio native structure state is no longer current."); - } - return retained; - } - - void claimStudioStructureState(StudioStructureState retained) { - if (!retainedStudioStructureState.compareAndSet(retained, null)) { - throw new IllegalStateException("Studio native structure state changed before activation began."); - } - } - - void abandonStudioStructureState() { - retainedStudioStructureState.set(null); - } - - CompletableFuture initializeAndPublishStructureState( - ChunkGeneratorStructureState structureState, - StructureStatePublisher publisher - ) { - return startStructureStateBootstrap( - structureState, - NO_OP, - () -> publishStructureState(publisher)); - } - - CompletableFuture activateStudioStructureState(StudioStructureState retained) { - Objects.requireNonNull(retained, "Retained Studio native structure state"); - return startStructureStateBootstrap( - retained.structureState(), - () -> { - StudioStructureState current = retainedStudioStructureState( - retained.level(), retained.chunkMap()); - if (current != retained) { - throw new IllegalStateException("Studio native structure state changed before activation."); - } - claimStudioStructureState(retained); - }, - NO_OP); - } - - private CompletableFuture startStructureStateBootstrap( - ChunkGeneratorStructureState structureState, - Runnable claim, - Runnable activation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - throw new IllegalStateException("Native structure bootstrap requires an IrisEngine runtime."); - } - AtomicReference> registeredCompletion = new AtomicReference<>(); - CompletableFuture completion = irisEngine.startNativeStructureBootstrap( - claim, - () -> { - CompletableFuture rings = initializeStructureState(structureState); - registeredCompletion.set(rings); - return rings; - }, - () -> { - CompletableFuture rings = Objects.requireNonNull( - registeredCompletion.get(), - "Registered native structure ring completion"); - if (rings.isCompletedExceptionally()) { - throw new IllegalStateException( - "Minecraft native structure ring bootstrap failed before activation."); - } - activation.run(); - }); - completion.whenComplete((ignored, failure) -> { - if (failure != null) { - Throwable cause = failure instanceof CompletionException && failure.getCause() != null - ? failure.getCause() - : failure; - IrisLogging.reportError("Native structure ring bootstrap failed for world '" - + runtimeLevel.getWorld().getName() + "'.", cause); - } - }); - return completion; - } - - private CompletableFuture initializeStructureState(ChunkGeneratorStructureState structureState) { - Map ringPositions = structureRingPositions(structureState); - structureState.ensureStructuresGenerated(); - return structureRingCompletion(ringPositions); - } - - private Map structureRingPositions(ChunkGeneratorStructureState structureState) { - try { - Field field = structureRingPositionsField(); - field.setAccessible(true); - Object value = field.get(structureState); - if (!(value instanceof Map ringPositions)) { - throw new IllegalStateException("Minecraft native structure ring state is unavailable."); - } - return ringPositions; - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not bind Minecraft native structure ring completions.", e); - } - } - - private CompletableFuture structureRingCompletion(Map ringPositions) { - List> futures = new ArrayList<>(ringPositions.size()); - for (Object candidate : ringPositions.values()) { - if (candidate instanceof CompletableFuture future) { - futures.add(future); - } else { - throw new IllegalStateException( - "Minecraft native structure ring completion is not a future."); - } - } - CompletableFuture[] completions = futures.toArray(new CompletableFuture[0]); - return CompletableFuture.allOf(completions); - } - - private Field structureRingPositionsField() { - List candidates = new ArrayList<>(1); - for (Field field : ChunkGeneratorStructureState.class.getDeclaredFields()) { - if (!Map.class.isAssignableFrom(field.getType())) { - continue; - } - Type genericType = field.getGenericType(); - if (!(genericType instanceof ParameterizedType parameterizedType)) { - continue; - } - Type[] arguments = parameterizedType.getActualTypeArguments(); - if (arguments.length == 2 - && arguments[1].getTypeName().contains(CompletableFuture.class.getName())) { - candidates.add(field); - } - } - if (candidates.size() != 1) { - throw new IllegalStateException("Expected one Minecraft native structure ring-future map, found " - + candidates.size() + "."); - } - return candidates.getFirst(); - } - - private void requireCurrentStructureOwner(ServerLevel level, ChunkMap chunkMap) { - if (runtimeLevel != level - || level.getChunkSource().chunkMap != chunkMap - || level.getChunkSource().getGenerator() != this) { - throw new IllegalStateException("Iris native structure state no longer belongs to the active world runtime."); - } - } - - private void publishStructureState(StructureStatePublisher publisher) { - try { - publisher.publish(); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Could not publish Minecraft native structure state.", e); - } - } - - @Override - public void createReferences(WorldGenLevel generatoraccessseed, StructureManager structuremanager, ChunkAccess ichunkaccess) { - if (platformGenerator != null && !platformGenerator.shouldGenerateStructures()) { - return; - } - if (runtimeLevel.getChunkSource().getGenerator() != this) { - return; - } - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_references"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_references"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_references"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - NativeStructureReferenceRepair.createReferences( - engine, generatoraccessseed, structuremanager, ichunkaccess); - } - } - - @Override - public CompletableFuture createBiomes(RandomState randomstate, Blender blender, StructureManager structuremanager, ChunkAccess ichunkaccess) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_create_biomes"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_create_biomes"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_create_biomes"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - customBiomeSource.prepareVisibleBiomeBatch(); - IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State(); - ichunkaccess.fillBiomesFromNoise( - (x, y, z) -> customBiomeSource.getVisibleNoiseBiomeWithActiveGenerationLease( - x, y, z, null, resolverState)); - return CompletableFuture.completedFuture(ichunkaccess); - } - } - - @Override - public CompletableFuture buildTerrain(ChunkAccess ichunkaccess, Blender blender, RandomState randomstate, StructureManager structuremanager, BiomeManager biomeManager, WorldGenRegion carverBiomeRegion, Set> possibleBiomes) { - ChunkPos chunkPos = ichunkaccess.getPos(); - BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_chunk_pipeline"); - GenerationHistoryRuntimeRouter.RuntimeRoute route; - try { - route = openHistoryRoute(chunkPos.x(), chunkPos.z(), "bukkit_nms_chunk_pipeline"); - } catch (RuntimeException | Error failure) { - stage.close(); - throw failure; - } - GenerationSessionLease lease; - try { - lease = requireGenerationLease("bukkit_nms_chunk_pipeline"); - } catch (RuntimeException | Error failure) { - closeHistoryRoute(route, failure); - stage.close(); - throw failure; - } - try { - CompletableFuture delegatePipeline; - try (GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - delegatePipeline = delegate.buildTerrain( - ichunkaccess, blender, randomstate, structuremanager, biomeManager, carverBiomeRegion, possibleBiomes); - } - CompletableFuture pipeline = delegatePipeline - .thenApply(filled -> { - try (GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = - openHistoryRuntimeScope(route); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - claimGeneratedSemantics(route, filled, engine.getMinHeight()); - persistNaturalTerrain(filled, route); - primeWorldgenHeightmaps(filled); - return filled; - } - }); - CompletableFuture completion = new CompletableFuture<>(); - pipeline.whenComplete((filled, failure) -> { - boolean cancelled = isCancellationFailure(failure); - Throwable completionFailure = closeNoisePipelineResources( - failure, lease, route, stage); - if (completionFailure == null) { - completion.complete(filled); - } else if (cancelled) { - completion.cancel(false); - } else { - completion.completeExceptionally(completionFailure); - } - }); - lease.detachThread(); - if (route != null) { - route.detachThread(); - } - return completion; - } catch (RuntimeException | Error failure) { - lease.close(); - closeHistoryRoute(route, failure); - stage.close(); - throw failure; - } - } - - private static void persistNaturalTerrain(ChunkAccess chunk, GenerationHistoryRuntimeRouter.RuntimeRoute route) { - if (route == null || route.naturalTerrain().isEmpty()) { - return; - } - try { - SavedTerrainChunk terrain = route.naturalTerrain().orElseThrow(); - chunk.persistentDataContainer.set(new NamespacedKey("iris", "natural_terrain"), PersistentDataType.BYTE_ARRAY, - NativeTerrainReceipt.encode(terrain, route.activation().activationId(), route.epoch().epochId())); - } catch (IOException failure) { - throw new IllegalStateException("Unable to persist natural terrain for chunk " + chunk.getPos(), failure); - } - } - - private static Throwable closeNoisePipelineResources( - Throwable failure, - GenerationSessionLease lease, - GenerationHistoryRuntimeRouter.RuntimeRoute route, - BukkitChunkGenerator.GenerationStagePermit stage - ) { - Throwable result = failure; - try { - lease.close(); - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - try { - if (route != null) { - route.close(); - } - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - try { - stage.close(); - } catch (Throwable closeFailure) { - result = appendFailure(result, closeFailure); - } - return result; - } - - private static void closeHistoryRoute( - GenerationHistoryRuntimeRouter.RuntimeRoute route, - Throwable failure - ) { - if (route == null) { - return; - } - try { - route.close(); - } catch (Throwable closeFailure) { - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - } - } - - private static Throwable appendFailure(Throwable failure, Throwable closeFailure) { - if (failure == null) { - return closeFailure; - } - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - return failure; - } - - private static boolean isCancellationFailure(Throwable failure) { - Throwable current = failure; - while (current instanceof CompletionException && current.getCause() != null) { - current = current.getCause(); - } - return current instanceof CancellationException; - } - - private void primeWorldgenHeightmaps(ChunkAccess chunkAccess) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - primeAuthoringHeightmaps(chunkAccess); - return; - } - WorldgenTerrainHeightmaps.primeTerrain(chunkAccess, worldgenSurfaceHeight(), worldgenFloorHeight()); - } - - static void primeAuthoringHeightmaps(ChunkAccess chunkAccess) { - Heightmap.primeHeightmaps(chunkAccess, AUTHORING_HEIGHTMAPS); - } - - private IntBinaryOperator worldgenSurfaceHeight() { - return (x, z) -> engine.getHeight(x, z, false) + runtimeMinY + 1; - } - - private IntBinaryOperator worldgenFloorHeight() { - return (x, z) -> engine.getHeight(x, z, true) + runtimeMinY + 1; - } - - private IntBinaryOperator hostWorldgenSurfaceHeight() { - return (x, z) -> Engine.hostHeight(engine, x, z, false) + runtimeMinY + 1; - } - - private IntBinaryOperator hostWorldgenFloorHeight() { - return (x, z) -> Engine.hostHeight(engine, x, z, true) + runtimeMinY + 1; - } - - @Override - public WeightedList getMobsAt(Level level, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { - Holder holder = level.getBiome(blockposition); - return EngineLifecycleTasks.call(engine, "bukkit_nms_mob_spawns", - () -> getAdmittedMobsAt(holder, structuremanager, enumcreaturetype, blockposition), - WeightedList.of(List.of())); - } - - private WeightedList getAdmittedMobsAt( - Holder holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) { - NativeBiomeSpawnSelection selection = NativeBiomeSpawnSelection.at( - engine, blockposition.getX(), blockposition.getY(), blockposition.getZ(), - holder.unwrapKey().map(key -> key.identifier().toString()).orElse("")); - if (selection.mode() == NativeBiomeSpawnSelection.Mode.LOADING) { - return WeightedList.of(List.of()); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - blockposition.getX(), blockposition.getZ(), "bukkit_nms_mob_spawns")) { - return getMobsAtWithActiveRuntime(holder, structuremanager, enumcreaturetype, blockposition, selection); - } - } - - private WeightedList getMobsAtWithActiveRuntime( - Holder holder, - StructureManager structuremanager, - MobCategory enumcreaturetype, - BlockPos blockposition, - NativeBiomeSpawnSelection selection - ) { - Holder vanillaSpawnBiome = switch (selection.mode()) { - case RETAINED -> customBiomeSource.getRetainedVanillaSpawnBiome(selection.derivativeKey()); - case CURRENT -> customBiomeSource.getVanillaSpawnBiome(holder); - case NONE, LOADING -> null; - }; - if (vanillaSpawnBiome == null) { - return delegate.getMobsAt(runtimeLevel, structuremanager, enumcreaturetype, blockposition); - } - - WeightedList vanillaSpawns = biomeSpawns(vanillaSpawnBiome, enumcreaturetype); - WeightedList resolvedSpawns = delegate.getMobsAt( - runtimeLevel, structuremanager, enumcreaturetype, blockposition); - WeightedList environmentSpawns = runtimeLevel.environmentAttributes() - .getValue(EnvironmentAttributes.NATURAL_MOB_SPAWNS, blockposition).getMobsToSpawn(enumcreaturetype); - if (resolvedSpawns != environmentSpawns) { - return resolvedSpawns; - } - - WeightedList explicitSpawns = biomeSpawns(holder, enumcreaturetype); - if (explicitSpawns.isEmpty()) { - return vanillaSpawns; - } - if (vanillaSpawns.isEmpty()) { - return explicitSpawns; - } - - int spawnRuntimeId = engine.getCacheID(); - SpawnTableKey key = new SpawnTableKey(spawnRuntimeId, holder.value(), vanillaSpawnBiome.value(), enumcreaturetype); - return mergedSpawnTables.computeIfAbsent(key, ignored -> mergeSpawnTables(vanillaSpawns, explicitSpawns)); - } - - @Override - public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager) { - applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, true); - } - - private static WeightedList biomeSpawns(Holder biome, MobCategory category) { - return biome.value().getAttributes().applyModifier(EnvironmentAttributes.NATURAL_MOB_SPAWNS, - EnvironmentAttributes.NATURAL_MOB_SPAWNS.defaultValue()).getMobsToSpawn(category); - } - - @Override - public void addDebugScreenInfo(List list, RandomState randomstate, BlockPos blockposition, SamplerContext samplerContext) { - delegate.addDebugScreenInfo(list, randomstate, blockposition, samplerContext); - } - - @Override - public void applyBiomeDecoration(WorldGenLevel generatoraccessseed, ChunkAccess ichunkaccess, StructureManager structuremanager, boolean vanilla) { - ChunkPos chunkPos = ichunkaccess.getPos(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_biome_decoration"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - chunkPos.x(), chunkPos.z(), "bukkit_nms_biome_decoration"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_biome_decoration"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - boolean flatStudioTerrain = platformGenerator != null && platformGenerator.usesFlatStudioTerrain(); - if (!flatStudioTerrain && !ichunkaccess.getPersistedStatus().isOrAfter(ChunkStatus.FEATURES)) { - placeVanillaStructures(generatoraccessseed, ichunkaccess, structuremanager); - } - if (allowsRoutedDiscreteGeneration(ichunkaccess, ChunkStatus.FEATURES) - && NativeGenerationWriteGuard.allowsDecoration(engine, generatoraccessseed, chunkPos)) { - if (!flatStudioTerrain) { - // Bind-time equivalent for Bukkit: the table is built on the first decorated chunk, which is where a - // feature-order cycle is reported once and degraded to features-off. - importedFeatures.prepare(generatoraccessseed); - } - addVanillaDecorations(generatoraccessseed, ichunkaccess, structuremanager); - if (!flatStudioTerrain) { - // Vanilla's placed-feature pass, on THIS thread. The delegate is still called with - // addVanillaDecorations=false below, so the vanilla half never runs twice. Inert unless the - // dimension set importedFeatures.enabled. - importedFeatures.run(generatoraccessseed, ichunkaccess, this); - } - delegate.applyBiomeDecoration(generatoraccessseed, ichunkaccess, structuremanager, false); - } - claimGeneratedSemantics(route, ichunkaccess, engine.getMinHeight()); - } - } - - /** - * Iris custom biomes carry no features in their datapack JSON by design; when importedFeatures is on they - * inherit the generation settings of the vanilla biome their Iris biome derives from. This is the gate - * {@code BiomeFilter} consults, so it has to agree with the feature pass. With the control off this is - * exactly the inherited behaviour. - */ - @Override - public BiomeGenerationSettings getBiomeGenerationSettings(Holder holder) { - BiomeGenerationSettings imported = importedFeatures.generationSettings(holder); - return imported == null ? super.getBiomeGenerationSettings(holder) : imported; - } - - private void placeVanillaStructures(WorldGenLevel world, ChunkAccess chunk, StructureManager structureManager) { - if (!structureManager.shouldGenerateStructures()) { - ChunkPos disabledChunk = chunk.getPos(); - throw new IllegalStateException("Iris cannot generate native structures in chunk " - + disabledChunk.x() + "," + disabledChunk.z() - + " because structure generation is disabled outside the pack; enable native structure generation " - + "and deny families through importedStructures.disabled or complete keys through importedStructures.disabledExact"); - } - ChunkPos chunkPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(chunkPos, world.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry registry = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List> byStep = structuresByStep(registry); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decoSeed = random.setDecorationSeed(world.getSeed(), origin.getX(), origin.getZ()); - BoundingBox area = writableArea(chunk); - int steps = GenerationStep.Decoration.values().length; - List placementGroups = new ArrayList<>(); - List heightmapStarts = new ArrayList<>(); - List vegetationTargets = new ArrayList<>(); - List terrainTargets = new ArrayList<>(); - for (int step = 0; step < steps; step++) { - int index = 0; - for (Structure structure : byStep.get(step)) { - Object id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - try { - IrisNativeStructureDecision sourceDecision = NativeStructureGenerationPolicy.resolve(engine, - structureId, NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - List starts = structureManager.startsForStructure(sectionPos.x(), sectionPos.z(), structure); - List resolvedPlacements = new ArrayList<>(starts.size()); - for (StructureStart start : starts) { - BoundingBox footprint = start.getBoundingBox(); - if (!isHistoricalStructureStart(world, start) - && !engine.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - continue; - } - NativeStructureOwnershipRecord ownership = - NativeStructureOwnershipRecovery.resolve( - engine, world.getLevel(), structureId, structure, start); - IrisNativeStructureDecision decision = - ownership == null ? sourceDecision : ownership.restoredDecision(); - if (!decision.generate()) { - continue; - } - resolvedPlacements.add(new NativePlacement(start, decision)); - heightmapStarts.add(start); - terrainTargets.add(new NativeStructureTerrainIntegrator.TerrainTarget( - structureId, start, - NativeStructureTerrainIntegrator.resolveNativeTerrain( - start, decision.terrain()))); - vegetationTargets.add(start); - } - if (!resolvedPlacements.isEmpty()) { - placementGroups.add(new NativePlacementGroup( - structureId, index, step, List.copyOf(resolvedPlacements))); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - index++; - } - } - try { - WorldgenTerrainHeightmaps.primeStructurePlacement( - world, chunkPos, heightmapStarts, - hostWorldgenSurfaceHeight(), hostWorldgenFloorHeight()); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "heightmap priming", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); - Predicate protectedPosition = NativeGenerationWriteGuard.protectedPositions( - staticObjects, engine.getDimensionStackContext(), engine.getMinHeight()); - WorldGenLevel boundedWorld = NativeStructureWorldgenAccess.create( - world, chunkPos, hostWorldgenSurfaceHeight(), hostWorldgenFloorHeight(), - engine.getDimensionStackContext() != null, - protectedPosition); - try { - NativeStructureVegetationClearer.clearIntersectingVegetation( - boundedWorld, chunk, area, vegetationTargets); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vegetation cleanup", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - NativeStructureSurfaceFitter.SurfaceTerrainPlan surfaceTerrainPlan; - try { - surfaceTerrainPlan = NativeStructureSurfaceFitter.prepareSurfaceStructures( - boundedWorld, area, terrainTargets, - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - surfaceTerrainPlan.primeHeightmaps(chunk); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain integration", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - try { - NativeStructurePostProcessor.prepareTerrain( - boundedWorld, area, terrainTargets, this::resolvePaletteBlock); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain preparation", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - for (NativePlacementGroup group : placementGroups) { - random.setFeatureSeed(decoSeed, group.featureIndex(), group.step()); - try { - for (NativePlacement placement : group.placements()) { - placeVanillaStructure(boundedWorld, structureManager, random, area, chunkPos, - group.structureId(), placement.start(), placement.decision()); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "placement", group.structureId(), chunkPos.x(), chunkPos.z(), error); - } - } - try { - NativeStructureSurfaceFitter.repairVacuumFoundations( - boundedWorld, area, surfaceTerrainPlan); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "foundation repair", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - } - - private boolean isHistoricalStructureStart(WorldGenLevel world, StructureStart start) { - ChunkPos origin = start.getChunkPos(); - if (!engine.getComplex().allowsMantleChunkWrite(origin.x(), origin.z())) { - return true; - } - ChunkAccess source = world.getChunk(origin.x(), origin.z(), ChunkStatus.EMPTY, false); - if (source == null) { - return false; - } - long activation = source.persistentDataContainer.getOrDefault( - new NamespacedKey("iris", "structure_activation"), PersistentDataType.LONG, 0L); - return NativeGenerationWriteGuard.isHistoricalStructure(engine, activation); - } - - private static String nativeStructureBatchContext(List placementGroups) { - if (placementGroups.isEmpty()) { - return ""; - } - StringBuilder context = new StringBuilder("["); - for (int i = 0; i < placementGroups.size(); i++) { - if (i > 0) { - context.append(", "); - } - context.append(placementGroups.get(i).structureId()); - } - return context.append(']').toString(); - } - - private void placeVanillaStructure(WorldGenLevel world, StructureManager structureManager, WorldgenRandom random, - BoundingBox area, ChunkPos chunkPos, String structureId, StructureStart start, - IrisNativeStructureDecision decision) { - WorldGenLevel boundedWorld = world instanceof NativeStructureWorldgenAccess ? world : NativeStructureWorldgenAccess.create( - world, - chunkPos, - hostWorldgenSurfaceHeight(), - hostWorldgenFloorHeight(), - engine.getDimensionStackContext() != null, - NativeGenerationWriteGuard.protectedPositions( - engine.getDimension().getStaticObjectLayer(engine.getData()), - engine.getDimensionStackContext(), engine.getMinHeight())); - world.setCurrentlyGenerating(() -> "Iris native structure " + structureId); - try { - NativeStructurePostProcessor.place(boundedWorld, structureManager, this, random, area, chunkPos, - structureId, start, decision, this::resolvePaletteBlock, - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight()); - } finally { - world.setCurrentlyGenerating(null); - } - } - - private List> structuresByStep(Registry registry) { - int runtimeId = engine.getCacheID(); - StructureStepCache cached = structureStepCache; - if (cached != null && cached.runtimeId() == runtimeId && cached.registry() == registry) { - return cached.structures(); - } - synchronized (this) { - cached = structureStepCache; - if (cached != null && cached.runtimeId() == runtimeId && cached.registry() == registry) { - return cached.structures(); - } - int steps = GenerationStep.Decoration.values().length; - List> grouped = new ArrayList<>(steps); - for (int step = 0; step < steps; step++) { - grouped.add(new ArrayList<>()); - } - for (Structure structure : registry) { - grouped.get(structure.step().ordinal()).add(structure); - } - for (int step = 0; step < steps; step++) { - grouped.set(step, List.copyOf(grouped.get(step))); - } - List> resolved = List.copyOf(grouped); - structureStepCache = new StructureStepCache(runtimeId, registry, resolved); - return resolved; - } - } - - private BlockState resolvePaletteBlock(IrisMaterialPalette palette, RNG rng, int x, int y, int z) { - PlatformBlockState platformState = palette.get(rng, x, y, z, engine.getData()); - if (platformState == null || !(platformState.nativeHandle() instanceof BlockData blockData)) { - throw new IllegalStateException("Configured native structure palette did not resolve a Bukkit block at " - + x + "," + y + "," + z); - } - if (blockData instanceof IrisCustomData customData) { - blockData = customData.getBase(); - } - if (blockData instanceof CraftBlockData craftBlockData) { - return craftBlockData.getState(); - } - throw new IllegalStateException("Configured native structure palette resolved unsupported Bukkit block data " - + blockData.getClass().getName() + " at " + x + "," + y + "," + z); - } - - private BoundingBox writableArea(ChunkAccess chunk) { - ChunkPos cp = chunk.getPos(); - int i = cp.getMinBlockX(); - int j = cp.getMinBlockZ(); - int minY = chunk.getMinY() + 1; - int maxY = chunk.getMinY() + chunk.getHeight() - 1; - return new BoundingBox(i, minY, j, i + 15, maxY, j + 15); - } - - @Override - public void addVanillaDecorations(WorldGenLevel level, ChunkAccess chunkAccess, StructureManager structureManager) { - ChunkPos chunkPos = chunkAccess.getPos(); - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - chunkPos.getMinBlockX(), chunkPos.getMinBlockZ(), "bukkit_nms_heightmaps"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_heightmaps"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - SectionPos sectionPos = SectionPos.of(chunkAccess.getPos(), level.getMinSectionY()); - BlockPos blockPos = sectionPos.origin(); - - primeWorldgenHeightmaps(chunkAccess); - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return; - } - - Heightmap motion = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING); - Heightmap motionNoLeaves = chunkAccess.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES); - int minHeight = engine.getMinHeight(); - - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - int wX = x + blockPos.getX(); - int wZ = z + blockPos.getZ(); - - int terrainTop = engine.getHeight(wX, wZ, false) + minHeight + 1; - SET_HEIGHT.invoke(motion, x, z, terrainTop); - SET_HEIGHT.invoke(motionNoLeaves, x, z, terrainTop); - } - } - - Heightmap.primeHeightmaps(chunkAccess, ChunkStatus.FINAL_HEIGHTMAPS); - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris heightmap generation could not acquire its engine runtime.", e); - } - } - - @Override - public void spawnOriginalMobs(WorldGenRegion region) { - ChunkPos center = region.getCenter(); - try (BukkitChunkGenerator.GenerationStagePermit stage = requireGenerationStage("bukkit_nms_spawn_original_mobs"); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - center.x(), center.z(), "bukkit_nms_spawn_original_mobs"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease("bukkit_nms_spawn_original_mobs"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - if (!allowsRoutedDiscreteGeneration(region.getChunk(center.x(), center.z()), ChunkStatus.SPAWN)) { - return; - } - Holder visibleBiome; - if (engine.getDimensionStackContext() == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } else { - visibleBiome = customBiomeSource.getVisibleSurfaceBiome( - center.getMinBlockX() + 8, - center.getMinBlockZ() + 8); - if (visibleBiome == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } - } - Holder vanillaBiome = customBiomeSource.getVanillaSpawnBiome(visibleBiome); - WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(RandomSupport.generateUniqueSeed())); - random.setDecorationSeed(region.getSeed(), center.getMinBlockX(), center.getMinBlockZ()); - InitialMobSpawner.spawn( - region, vanillaBiome == null ? visibleBiome : vanillaBiome, center, random); - } - } - - @Override - public boolean test(long packedChunk) { - return !engine.isClosing() && !engine.isClosed() - && engine.getComplex().allowsMantleChunkWrite(ChunkPos.getX(packedChunk), ChunkPos.getZ(packedChunk)); - } - - private boolean allowsRoutedDiscreteGeneration(ChunkAccess chunk, ChunkStatus stage) { - if (chunk.getPersistedStatus().isOrAfter(stage)) { - return false; - } - ChunkPos chunkPos = chunk.getPos(); - return NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, stage) - || engine.getComplex().allowsNewGenerationChunk(chunkPos.x(), chunkPos.z()); - } - - private static WeightedList mergeSpawnTables( - WeightedList vanillaSpawns, - WeightedList explicitSpawns) { - List> entries = new ArrayList<>( - vanillaSpawns.unwrap().size() + explicitSpawns.unwrap().size()); - Set> explicitTypes = new HashSet<>(); - for (Weighted entry : explicitSpawns.unwrap()) { - explicitTypes.add(entry.value().type()); - } - for (Weighted entry : vanillaSpawns.unwrap()) { - if (!explicitTypes.contains(entry.value().type())) { - entries.add(entry); - } - } - entries.addAll(explicitSpawns.unwrap()); - return WeightedList.of(entries); - } - - @Override - public int getSpawnHeight(LevelHeightAccessor levelheightaccessor) { - return delegate.getSpawnHeight(levelheightaccessor); - } - - @Override - public int getGenDepth() { - return runtimeHeight; - } - - @Override - public int getBaseHeight(int i, int j, Heightmap.Types heightmap_type, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - return platformGenerator.getAuthoringBaseHeight(); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - i, j, "bukkit_nms_base_height"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_base_height"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - NativeTerrainHeightCache.Query query = new NativeTerrainHeightCache.Query( - engine.getCacheID(), i, j, heightmap_type, - levelheightaccessor.getMinY(), levelheightaccessor.getHeight()); - OptionalInt resolved = terrainHeights.resolvedHeight(query, - () -> resolvedBaseHeight(i, j, heightmap_type, levelheightaccessor)); - if (resolved.isPresent()) { - return resolved.getAsInt(); - } - boolean ignoreFluid = !heightmap_type.isOpaque().test(Blocks.WATER.defaultBlockState()); - int height = engine.getDimensionStackContext() == null - ? engine.getHeight(i, j, ignoreFluid) - : Engine.hostHeight(engine, i, j, ignoreFluid); - return levelheightaccessor.getMinY() + height + 1; - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base height query could not acquire its engine runtime.", e); - } - } - - private OptionalInt resolvedBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor) { - Optional resolved = engine.getComplex().resolvedTerrainColumn(x, z); - return resolved.isPresent() - ? OptionalInt.of(NativeTransitionColumn.height(resolved.get(), type, heightAccessor)) - : OptionalInt.empty(); - } - - @Override - public NoiseColumn getBaseColumn(int i, int j, LevelHeightAccessor levelheightaccessor, RandomState randomstate) { - if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { - BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; - int floorIndex = platformGenerator.getAuthoringFloorY() - levelheightaccessor.getMinY(); - BlockState floor = platformGenerator.isJigsawStudioActive() && JigsawStudioGenerator.isLightFloor(i, j) - ? Blocks.SMOOTH_STONE.defaultBlockState() - : Blocks.POLISHED_DEEPSLATE.defaultBlockState(); - for (int index = 0; index < column.length; index++) { - column[index] = index == floorIndex - ? floor - : Blocks.AIR.defaultBlockState(); - } - return new NoiseColumn(levelheightaccessor.getMinY(), column); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope route = openHistoryCoordinateScope( - i, j, "bukkit_nms_base_column"); - GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_nms_base_column"); - IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - Optional resolved = engine.getComplex().resolvedTerrainColumn(i, j); - if (resolved.isPresent()) { - return NativeTransitionColumn.column(resolved.get(), levelheightaccessor); - } - boolean dimensionStack = engine.getDimensionStackContext() != null; - int block = dimensionStack - ? Engine.hostHeight(engine, i, j, true) - : engine.getHeight(i, j, true); - int water = dimensionStack - ? Engine.hostHeight(engine, i, j, false) - : engine.getHeight(i, j, false); - BlockState[] column = new BlockState[levelheightaccessor.getHeight()]; - for (int k = 0; k < column.length; k++) { - if (k <= block) { - column[k] = Blocks.STONE.defaultBlockState(); - } else if (k <= water) { - column[k] = Blocks.WATER.defaultBlockState(); - } else { - column[k] = Blocks.AIR.defaultBlockState(); - } - } - return new NoiseColumn(levelheightaccessor.getMinY(), column); - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base column query could not acquire its engine runtime.", e); - } - } - - private GenerationSessionLease requireGenerationLease(String operation) { - try { - return engine.acquireGenerationLease(operation); - } catch (GenerationSessionException exception) { - throw new IllegalStateException("Iris " + operation + " could not acquire its engine runtime.", exception); - } - } - - private GenerationHistoryRuntimeRouter.RuntimeRoute openHistoryRoute( - int chunkX, - int chunkZ, - String operation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); - if (router == null) { - if (platformGenerator != null && platformGenerator.getGenerationHistory() != null) { - throw new IllegalStateException("Iris " + operation - + " has no generation-history runtime router."); - } - return null; - } - try { - return router.openRoute(chunkX, chunkZ); - } catch (IOException failure) { - throw new IllegalStateException("Iris " + operation - + " could not route generation history for chunk " - + chunkX + "," + chunkZ + ".", failure); - } - } - - private GenerationHistoryRuntimeRouter.CoordinateScope openHistoryCoordinateScope( - int blockX, - int blockZ, - String operation - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(blockX, blockZ); - } catch (IOException failure) { - throw new IllegalStateException("Iris " + operation - + " could not route generation history at " - + blockX + "," + blockZ + ".", failure); - } - } - - private static GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope openHistoryRuntimeScope( - GenerationHistoryRuntimeRouter.RuntimeRoute route - ) { - return route == null ? null : route.openRuntimeScope(); - } - - private static void claimGeneratedSemantics( - GenerationHistoryRuntimeRouter.RuntimeRoute route, - ChunkAccess chunk, - int minimumY - ) { - if (route == null) { - return; - } - try { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - route.claimGeneratedSemantics((x, y, z) -> { - BlockState state = chunk.getBlockState(position.set(x, minimumY + y, z)); - return state.isAir() || state.liquid(); - }); - } catch (IOException failure) { - throw new IllegalStateException("Iris could not durably claim generated chunk semantics.", failure); - } - } - - private BukkitChunkGenerator.GenerationStagePermit requireGenerationStage(String operation) { - return platformGenerator == null - ? BukkitChunkGenerator.GenerationStagePermit.noop() - : platformGenerator.acquireGenerationStage(operation); - } - - @Override - public Optional getTypeNameForDataFixer() { - return delegate.getTypeNameForDataFixer(); - } - - @Override - public void validate() { - delegate.validate(); - } - - static { - List biomeSources = new ArrayList<>(1); - for (Field field : ChunkGenerator.class.getDeclaredFields()) { - if (!field.getType().equals(BiomeSource.class)) - continue; - biomeSources.add(field); - } - if (biomeSources.size() != 1) - throw new IllegalStateException("Expected exactly one BiomeSource field in ChunkGenerator, found " - + biomeSources.size() + " " + biomeSources.stream().map(Field::getName).toList()); - Field biomeSource = biomeSources.getFirst(); - - List setHeights = new ArrayList<>(1); - for (Method method : Heightmap.class.getDeclaredMethods()) { - Class[] types = method.getParameterTypes(); - if (!method.getName().equals("setHeight") - || !Arrays.equals(types, new Class[]{int.class, int.class, int.class}) - || !method.getReturnType().equals(void.class)) - continue; - setHeights.add(method); - } - if (setHeights.size() != 1) - throw new IllegalStateException("Expected exactly one Heightmap.setHeight(int,int,int) method, found " - + setHeights.size()); - Method setHeight = setHeights.getFirst(); - - BIOME_SOURCE = new WrappedField<>(ChunkGenerator.class, biomeSource.getName()); - SET_HEIGHT = new WrappedReturningMethod<>(Heightmap.class, setHeight.getName(), setHeight.getParameterTypes()); - } - - private static ChunkGenerator edit(ChunkGenerator generator, BiomeSource source) { - try { - BIOME_SOURCE.set(generator, source); - if (generator instanceof CustomChunkGenerator custom) - BIOME_SOURCE.set(custom.getDelegate(), source); - - return generator; - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private record SpawnTableKey(int runtimeId, Biome biome, Biome vanillaBiome, MobCategory category) { - } - - private record ReachableStructureCache(IrisDimension dimension, int runtimeId, Set keys) { - } - - private record StructureStepCache(int runtimeId, Registry registry, - List> structures) { - } - - record StudioStructureState( - ServerLevel level, - ChunkMap chunkMap, - ChunkGeneratorStructureState structureState - ) { - } - - @FunctionalInterface - interface StructureStatePublisher { - void publish() throws IllegalAccessException; - } - - private record NativePlacement(StructureStart start, IrisNativeStructureDecision decision) { - } - - private record NativePlacementGroup(String structureId, int featureIndex, int step, - List placements) { - } - - private record NativeLocateCandidate(Holder holder, String key) { - } - - private record IrisNativeLocateSearch(Holder holder, String structureId, - NativeStructureLocatePersistence.Search search) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBinding.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBinding.java deleted file mode 100644 index c44e3b841..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBinding.java +++ /dev/null @@ -1,1645 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; -import net.minecraft.util.ARGB; -import org.joml.Vector3fc; -import art.arcane.iris.world.history.SavedTerrainChunk; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; -import art.arcane.iris.platform.bukkit.nms.DatapackStructureScopeResult; -import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.platform.bukkit.nms.INMSBinding; -import art.arcane.iris.platform.bukkit.nms.MinecraftVersion; -import art.arcane.iris.platform.bukkit.nms.ServerShutdownBoundary; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.Pair; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.generation.cache.AtomicCache; -import art.arcane.iris.generation.chunk.TerrainChunk; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureVolumeIndex; -import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; -import art.arcane.iris.platform.generation.BukkitChunkGenerator; -import art.arcane.iris.platform.generation.PlatformChunkGenerator; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureFactory; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureGenerationException; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; -import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.hunk.Hunk; -import art.arcane.iris.generation.chunk.ChunkDataHunkHolder; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformGenerationRegistry; -import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.volmlib.util.json.JSONObject; -import art.arcane.volmlib.util.mantle.runtime.Mantle; -import art.arcane.volmlib.util.matter.Matter; -import art.arcane.volmlib.util.math.Vector3d; -import art.arcane.volmlib.util.scheduling.FoliaScheduler; -import art.arcane.volmlib.util.matter.MatterBiomeInject; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.world.storage.region.NBTWorld; -import art.arcane.volmlib.util.nbt.mca.palette.MCABiomeContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAChunkBiomeContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAGlobalPalette; -import art.arcane.volmlib.util.nbt.mca.palette.MCAIdMap; -import art.arcane.volmlib.util.nbt.mca.palette.MCAIdMapper; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPalette; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPaletteAccess; -import art.arcane.volmlib.util.nbt.mca.palette.MCAPalettedContainer; -import art.arcane.volmlib.util.nbt.mca.palette.MCAWrappedPalettedContainer; -import art.arcane.volmlib.util.nbt.tag.CompoundTag; -import art.arcane.iris.world.task.J; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.IdMapper; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.Registry; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.ByteTag; -import net.minecraft.nbt.CollectionTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.EndTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.ShortTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.commands.data.BlockDataAccessor; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.tags.TagKey; -import net.minecraft.world.attribute.EnvironmentAttributes; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.item.component.CustomData; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Biomes; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -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.level.block.state.properties.Property; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.WorldGenContext; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.structure.StructureCheck; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature; -import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.TreeFeature; -import net.minecraft.world.level.levelgen.flat.FlatLayerInfo; -import net.minecraft.world.level.levelgen.flat.FlatLevelGeneratorSettings; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Difficulty; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.CraftChunk; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.craftbukkit.block.CraftBlockState; -import org.bukkit.craftbukkit.block.CraftBlockStates; -import org.bukkit.craftbukkit.block.data.CraftBlockData; -import org.bukkit.craftbukkit.generator.CraftChunkData; -import org.bukkit.craftbukkit.inventory.CraftItemStack; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; -import org.bukkit.craftbukkit.util.CraftNamespacedKey; -import org.bukkit.entity.Entity; -import org.bukkit.event.entity.CreatureSpawnEvent; -import org.bukkit.generator.BiomeProvider; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; - -import java.awt.Color; -import java.io.File; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicInteger; - -public class NMSBinding implements INMSBinding { - private final KMap baseBiomeCache = new KMap<>(); - private volatile DataVersion dataVersion; - private final BlockData AIR = Material.AIR.createBlockData(); - private final AtomicCache> biomeMapCache = new AtomicCache<>(); - private final NmsWorldLifecycle worldLifecycle = new NmsWorldLifecycle(); - private final AtomicCache> registryCache = new AtomicCache<>(); - private final AtomicCache> globalCache = new AtomicCache<>(); - private final AtomicCache registryAccess = new AtomicCache<>(); - private final AtomicCache byIdRef = new AtomicCache<>(); - - @Override - public PlatformGenerationRegistry generationRegistry() { - return new NmsGenerationRegistry( - this::registry, - "bukkit-generation-registry-v1", - "bukkit-v26_3_R1-generated-registry-json-v1" - ); - } - - private static Object getFor(Class type, Object source) { - Object o = fieldFor(type, source); - - if (o != null) { - return o; - } - - o = invokeFor(type, source); - - if (o != null) { - return o; - } - - throw new IllegalStateException("Iris cannot resolve a " + type.getName() - + " from " + source.getClass().getName() + " on this server version"); - } - - private static Object invokeFor(Class returns, Object in) { - for (Method i : in.getClass().getMethods()) { - if (i.getReturnType().equals(returns)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); - return i.invoke(in); - } catch (ReflectiveOperationException | RuntimeException e) { - throw new IllegalStateException("Iris failed to invoke " + in.getClass().getName() + "." - + i.getName() + "() for " + returns.getName(), e); - } - } - } - - return null; - } - - private static Object fieldFor(Class returns, Object in) { - for (Class sourceType = in.getClass(); sourceType != null; sourceType = sourceType.getSuperclass()) { - Object o = fieldForClass(returns, sourceType, in); - - if (o != null) { - return o; - } - } - - return null; - } - - @SuppressWarnings("unchecked") - private static T fieldForClass(Class returnType, Class sourceType, Object in) { - for (Field i : sourceType.getDeclaredFields()) { - if (i.getType().equals(returnType)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); - return (T) i.get(in); - } catch (IllegalAccessException | RuntimeException e) { - throw new IllegalStateException("Iris failed to read " + sourceType.getName() + "." - + i.getName() + " for " + returnType.getName(), e); - } - } - } - return null; - } - - private static Class getClassType(Class type, int ordinal) { - return type.getDeclaredClasses()[ordinal]; - } - - @Override - public boolean hasTile(Material material) { - return !CraftBlockState.class.equals(CraftBlockStates.getBlockStateType(material)); - } - - @Override - public String getEntitySpawnCategory(String key) { - Identifier identifier = Identifier.parse(key); - if (!BuiltInRegistries.ENTITY_TYPE.containsKey(identifier)) { - throw new IllegalArgumentException("Unknown native entity type: " + key); - } - EntityType type = BuiltInRegistries.ENTITY_TYPE.getValue(identifier); - return type.getCategory().getSerializedName().toLowerCase(Locale.ROOT); - } - - @Override - public boolean hasTile(Location l) { - return ((CraftWorld) l.getWorld()).getHandle().getBlockEntity(new BlockPos(l.getBlockX(), l.getBlockY(), l.getBlockZ())) != null; - } - - @Override - @SuppressWarnings("unchecked") - public KMap serializeTile(Location location) { - BlockEntity e = ((CraftWorld) location.getWorld()).getHandle().getBlockEntity(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); - - if (e == null) { - return null; - } - - net.minecraft.nbt.CompoundTag tag = e.saveWithoutMetadata(registry()); - return (KMap) convertFromTag(tag, 0, 64); - } - - @Contract(value = "null, _, _ -> null", pure = true) - private Object convertFromTag(Tag tag, int depth, int maxDepth) { - if (tag == null || depth > maxDepth) return null; - return switch (tag) { - case CollectionTag collection -> { - KList list = new KList<>(); - - for (Object i : collection) { - if (i instanceof Tag t) - list.add(convertFromTag(t, depth + 1, maxDepth)); - else list.add(i); - } - yield list; - } - case net.minecraft.nbt.CompoundTag compound -> { - KMap map = new KMap<>(); - - for (String key : compound.keySet()) { - Tag child = compound.get(key); - if (child == null) continue; - Object value = convertFromTag(child, depth + 1, maxDepth); - if (value == null) continue; - map.put(key, value); - } - yield map; - } - case NumericTag numeric -> numeric.box(); - default -> tag.asString().orElse(null); - }; - } - - @Override - public void deserializeTile(KMap map, Location pos) { - if (map == null || pos == null || pos.getWorld() == null) { - return; - } - - Tag converted = convertToTag(map, 0, 64); - if (!(converted instanceof net.minecraft.nbt.CompoundTag tag)) { - return; - } - - ServerLevel level = ((CraftWorld) pos.getWorld()).getHandle(); - BlockPos blockPos = new BlockPos(pos.getBlockX(), pos.getBlockY(), pos.getBlockZ()); - int chunkX = pos.getBlockX() >> 4; - int chunkZ = pos.getBlockZ() >> 4; - if (J.isOwnedByCurrentRegion(pos.getWorld(), chunkX, chunkZ)) { - merge(level, blockPos, tag); - return; - } - if (!J.runAt(pos, () -> merge(level, blockPos, tag))) { - IrisLogging.warn("[NMS] Failed to schedule tile deserialize at " + blockPos + " in world " + pos.getWorld().getName()); - } - } - - private void merge(ServerLevel level, BlockPos blockPos, net.minecraft.nbt.CompoundTag tag) { - if (level == null || blockPos == null || tag == null) { - return; - } - - try { - BlockEntity blockEntity = level.getBlockEntity(blockPos); - if (blockEntity == null) { - IrisLogging.warn("[NMS] BlockEntity not found at " + blockPos); - BlockState state = level.getBlockState(blockPos); - if (!state.hasBlockEntity()) { - return; - } - - blockEntity = ((EntityBlock) state.getBlock()) - .newBlockEntity(blockPos, state); - } - - BlockDataAccessor accessor = new BlockDataAccessor(blockEntity, blockPos); - accessor.setData(accessor.getData().merge(tag)); - } catch (Throwable e) { - IrisLogging.warn("[NMS] Failed to merge tile data at " + blockPos + ": " + e.getMessage()); - IrisLogging.reportError(e); - } - } - - private Tag convertToTag(Object object, int depth, int maxDepth) { - if (object == null || depth > maxDepth) return EndTag.INSTANCE; - return switch (object) { - case Map map -> { - net.minecraft.nbt.CompoundTag tag = new net.minecraft.nbt.CompoundTag(); - for (Map.Entry i : map.entrySet()) { - tag.put(i.getKey().toString(), convertToTag(i.getValue(), depth + 1, maxDepth)); - } - yield tag; - } - case List list -> { - ListTag tag = new ListTag(); - for (Object i : list) { - tag.add(convertToTag(i, depth + 1, maxDepth)); - } - yield tag; - } - case Byte number -> ByteTag.valueOf(number); - case Short number -> ShortTag.valueOf(number); - case Integer number -> IntTag.valueOf(number); - case Long number -> LongTag.valueOf(number); - case Float number -> FloatTag.valueOf(number); - case Double number -> DoubleTag.valueOf(number); - case String string -> StringTag.valueOf(string); - default -> EndTag.INSTANCE; - }; - } - - @Override - public CompoundTag serializeEntity(Entity location) { - return null;// TODO: - } - - @Override - public Entity deserializeEntity(CompoundTag s, Location newPosition) { - return null;// TODO: - } - - @Override - public boolean supportsCustomHeight() { - return true; - } - - private RegistryAccess registry() { - RegistryAccess access = registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); - - if (access == null) { - throw new IllegalStateException("Iris cannot resolve the Minecraft registry access on this server version"); - } - - return access; - } - - private Registry getCustomBiomeRegistry() { - return registry().lookup(Registries.BIOME).orElseThrow(() -> new IllegalStateException( - "Iris cannot resolve the Minecraft biome registry on this server version")); - } - - private Registry getBlockRegistry() { - return registry().lookup(Registries.BLOCK).orElse(null); - } - - @Override - public Object getBiomeBaseFromId(int id) { - Object raw = getCustomBiomeRegistry().get(id); - if (raw instanceof java.util.Optional opt) { - raw = opt.orElse(null); - } - if (raw instanceof Holder holder) { - raw = holder.value(); - } - return raw; - } - - @Override - public int getMinHeight(World world) { - return world.getMinHeight(); - } - - @Override - public boolean supportsCustomBiomes() { - return true; - } - - @Override - public boolean supportsIrisWorldGeneration() { - return true; - } - - @Override - public int getTrueBiomeBaseId(Object biomeBase) { - return getCustomBiomeRegistry().getId(((Holder) biomeBase).value()); - } - - @Override - public Object getTrueBiomeBase(Location location) { - return ((CraftWorld) location.getWorld()).getHandle().getBiome(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ())); - } - - @Override - public String getTrueBiomeBaseKey(Location location) { - return getKeyForBiomeBase(getTrueBiomeBase(location)); - } - - @Override - public Object getCustomBiomeBaseFor(String mckey) { - return getCustomBiomeRegistry().getValue(net.minecraft.resources.Identifier.parse(mckey)); - } - - @Override - public Object getCustomBiomeBaseHolderFor(String mckey) { - return getCustomBiomeRegistry().get(getTrueBiomeBaseId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(mckey)))).orElse(null); - } - - public int getBiomeBaseIdForKey(String key) { - return getCustomBiomeRegistry().getId(getCustomBiomeRegistry().get(net.minecraft.resources.Identifier.parse(key)).map(Holder::value).orElse(null)); - } - - @Override - public String getKeyForBiomeBase(Object biomeBase) { - net.minecraft.world.level.biome.Biome biome; - if (biomeBase instanceof Holder holder) { - Object value = holder.value(); - if (!(value instanceof net.minecraft.world.level.biome.Biome held)) { - throw new IllegalArgumentException("Iris cannot read a biome key from holder value " - + (value == null ? "null" : value.getClass().getName())); - } - biome = held; - } else if (biomeBase instanceof net.minecraft.world.level.biome.Biome direct) { - biome = direct; - } else { - throw new IllegalArgumentException("Iris cannot read a biome key from " - + (biomeBase == null ? "null" : biomeBase.getClass().getName())); - } - - Identifier key = getCustomBiomeRegistry().getKey(biome); - if (key == null) { - throw new IllegalStateException("Iris found no registry key for biome " + biome); - } - return key.getPath(); // something, not something:something - } - - @Override - public Object getBiomeBase(World world, Biome biome) { - return biomeToBiomeBase(((CraftWorld) world).getHandle() - .registryAccess().lookup(Registries.BIOME).orElse(null), biome); - } - - @Override - public Object getBiomeBase(Object registry, Biome biome) { - Object v = baseBiomeCache.get(biome); - - if (v != null) { - return v; - } - //noinspection unchecked - v = biomeToBiomeBase((Registry) registry, biome); - if (v == null) { - // Ok so there is this new biome name called "CUSTOM" in Paper's new releases. - // But, this does NOT exist within CraftBukkit which makes it return an error. - // So, we will just return the ID that the plains biome returns instead. - //noinspection unchecked - return biomeToBiomeBase((Registry) registry, Biome.PLAINS); - } - baseBiomeCache.put(biome, v); - return v; - } - - @Override - public KList getBiomes() { - KList biomes = new KList<>(); - for (Biome biome : org.bukkit.Registry.BIOME) { - biomes.add(biome); - } - return biomes; - } - - @Override - public KList getStructureKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.STRUCTURE).keySet().forEach(k -> keys.add(k.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to read registered structure keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getJigsawStructureKeys() { - KList keys = new KList<>(); - try { - Registry structures = registry().lookupOrThrow(Registries.STRUCTURE); - for (Map.Entry, Structure> entry : structures.entrySet()) { - if (entry.getValue() instanceof JigsawStructure) { - keys.add(entry.getKey().identifier().toString()); - } - } - } catch (RuntimeException e) { - throw new IllegalStateException( - "Iris failed to read registered jigsaw structure keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getTemplatePoolKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.TEMPLATE_POOL).keySet() - .forEach(key -> keys.add(key.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException( - "Iris failed to read registered template pool keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public JigsawSourceMetadata getJigsawSourceMetadata(String structureKey) { - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Registry structures = registry().lookupOrThrow(Registries.STRUCTURE); - Structure structure = structures.getValue(identifier); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.sourceMetadata( - registry(), server.getStructureTemplateManager(), jigsaw); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve live jigsaw metadata for registered structure '" - + structureKey + "'", error); - } - } - - @Override - public int getTemplatePoolHorizontalSpan(String templatePoolKey) { - try { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.templatePoolHorizontalSpan( - registry(), server.getStructureTemplateManager(), templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the live horizontal span for registered " - + "template pool '" + templatePoolKey + "'", error); - } - } - - @Override - public int getJigsawStartPoolHorizontalSpan(String structureKey, String templatePoolKey) { - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Structure structure = registry().lookupOrThrow(Registries.STRUCTURE).getValue(identifier); - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getHandle().getServer(); - return NativeStructureFactory.jigsawStartPoolHorizontalSpan( - registry(), server.getStructureTemplateManager(), jigsaw, templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the effective start-pool span for registered " - + "jigsaw structure '" + structureKey + "' and pool '" + templatePoolKey + "'", error); - } - } - - @Override - public KList getStructureSetKeys() { - KList keys = new KList<>(); - try { - registry().lookupOrThrow(Registries.STRUCTURE_SET).keySet().forEach(k -> keys.add(k.toString())); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to read registered structure-set keys from the Minecraft registry", e); - } - return keys; - } - - @Override - public KList getReachableStructureKeys(World world) { - KList keys = new KList<>(); - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource(); - keys.addAll(VanillaStructureBiomes.reachableStructureKeys(level, source)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve reachable structures for Bukkit world '" - + (world == null ? "" : world.getName()) + "'", e); - } - return keys; - } - - @Override - public KList getStructureBiomeKeys(String structureKey) { - KList keys = new KList<>(); - try { - RegistryAccess access = registry(); - if (access == null) { - throw new IllegalStateException("Minecraft registry access is unavailable"); - } - keys.addAll(VanillaStructureBiomes.structureBiomeKeys(access, structureKey)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve biome keys for registered structure '" - + structureKey + "'", e); - } - return keys; - } - - @Override - public KList getPossibleBiomeKeys(World world) { - KList keys = new KList<>(); - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource(); - keys.addAll(VanillaStructureBiomes.possibleBiomeKeys(source)); - } catch (RuntimeException e) { - throw new IllegalStateException("Iris failed to resolve possible structure biome keys for Bukkit world '" - + (world == null ? "" : world.getName()) + "'", e); - } - return keys; - } - - @Override - public KList getObjectFeatureKeys() { - KList keys = new KList<>(); - try { - Registry reg = registry().lookupOrThrow(Registries.FEATURE); - for (Identifier id : reg.keySet()) { - Feature cf = reg.getValue(id); - if (cf == null) { - continue; - } - String group = classifyFeature(cf); - if (group == null) { - continue; - } - keys.add(group + "|" + id); - } - } catch (Throwable e) { - IrisLogging.reportError(e); - } - return keys; - } - - private static String classifyFeature(Feature feature) { - if (feature instanceof TreeFeature) { - return "trees"; - } - if (feature instanceof FallenTreeFeature) { - return "fallen_trees"; - } - if (feature instanceof AbstractHugeMushroomFeature) { - return "mushrooms"; - } - return null; - } - - @Override - public boolean placeFeature(World world, int x, int y, int z, String featureKey, long seed) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry reg = registry().lookupOrThrow(Registries.FEATURE); - Feature cf = reg.getValue(Identifier.parse(featureKey)); - if (cf == null) { - return false; - } - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - return cf.place(level, generator, random, new BlockPos(x, y, z)); - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public int[] placeStructure(World world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry reg = registry().lookupOrThrow(Registries.STRUCTURE); - net.minecraft.world.level.levelgen.structure.Structure structure = reg.getValue(Identifier.parse(structureKey)); - if (structure == null) { - return null; - } - net.minecraft.core.Holder holder = reg.wrapAsHolder(structure); - net.minecraft.world.level.levelgen.RandomState randomState = level.getChunkSource().randomState(); - net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager templateManager = level.getStructureTemplateManager(); - net.minecraft.world.level.StructureManager structureManager = level.structureManager(); - net.minecraft.world.level.biome.BiomeSource biomeSource = generator.getBiomeSource(); - net.minecraft.world.level.ChunkPos chunkPos = new net.minecraft.world.level.ChunkPos(chunkX, chunkZ); - - net.minecraft.world.level.levelgen.structure.StructureStart start = structure.generate( - holder, - level.dimension(), - level.registryAccess(), - generator, - biomeSource, - randomState.createClimateSampler(SamplerContext.EMPTY_UNCACHED), - randomState, - templateManager, - seed, - chunkPos, - 0, - level, - biome -> true); - - if (start == null || !start.isValid()) { - return null; - } - - BoundingBox box = start.getBoundingBox(); - int spanX = box.maxX() - box.minX() + 1; - int spanY = box.maxY() - box.minY() + 1; - int spanZ = box.maxZ() - box.minZ() + 1; - if (maxSpan > 0 && (spanX > maxSpan || spanZ > maxSpan || spanY > maxSpan)) { - return null; - } - - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - int minCX = box.minX() >> 4; - int maxCX = box.maxX() >> 4; - int minCZ = box.minZ() >> 4; - int maxCZ = box.maxZ() >> 4; - for (int cx = minCX; cx <= maxCX; cx++) { - for (int cz = minCZ; cz <= maxCZ; cz++) { - level.getChunk(cx, cz); - net.minecraft.world.level.ChunkPos cp = new net.minecraft.world.level.ChunkPos(cx, cz); - BoundingBox chunkBox = new BoundingBox( - cp.getMinBlockX(), box.minY(), cp.getMinBlockZ(), - cp.getMaxBlockX(), box.maxY(), cp.getMaxBlockZ()); - start.placeInChunk(level, structureManager, generator, random, chunkBox, cp); - } - } - return new int[]{box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()}; - } catch (RuntimeException e) { - throw NativeStructureGenerationException.failure( - "capture placement", structureKey, chunkX, chunkZ, e); - } - } - - @Override - public boolean supportsStructureCapture() { - return true; - } - - @Override - public KList nativeStructureVolumes(Engine engine, int minX, int minZ, int maxX, int maxZ) { - return NativeStructureVolumeIndex.volumes(engine, minX, minZ, maxX, maxZ); - } - - @Override - public void invalidateNativeStructureVolumeIndex(Engine engine) { - NativeStructureVolumeIndex.invalidate(engine); - } - - @Override - public int getBiomeId(Biome biome) { - for (World i : Bukkit.getWorlds()) { - if (i.getEnvironment().equals(World.Environment.NORMAL)) { - Registry registry = ((CraftWorld) i).getHandle().registryAccess().lookup(Registries.BIOME).orElse(null); - if (registry == null) { - continue; - } - Object baseRaw = getBiomeBase(registry, biome); - net.minecraft.world.level.biome.Biome base; - if (baseRaw instanceof Holder holder) { - Object value = holder.value(); - if (!(value instanceof net.minecraft.world.level.biome.Biome resolved)) { - continue; - } - base = resolved; - } else if (baseRaw instanceof net.minecraft.world.level.biome.Biome resolved) { - base = resolved; - } else { - continue; - } - return registry.getId(base); - } - } - - List biomes = new ArrayList<>(); - for (Biome entry : org.bukkit.Registry.BIOME) { - biomes.add(entry); - } - int index = biomes.indexOf(biome); - return Math.max(index, 0); - } - - private MCAIdMap getBiomeMapping() { - return biomeMapCache.aquire(() -> new MCAIdMap<>() { - @NotNull - @Override - public Iterator iterator() { - return getCustomBiomeRegistry().iterator(); - } - - @Override - public int getId(net.minecraft.world.level.biome.Biome paramT) { - return getCustomBiomeRegistry().getId(paramT); - } - - @Override - public net.minecraft.world.level.biome.Biome byId(int paramInt) { - return (net.minecraft.world.level.biome.Biome) getBiomeBaseFromId(paramInt); - } - }); - } - - @NotNull - private MCABiomeContainer getBiomeContainerInterface(MCAIdMap biomeMapping, MCAChunkBiomeContainer base) { - return new MCABiomeContainer() { - @Override - public int[] getData() { - return base.writeBiomes(); - } - - @Override - public void setBiome(int x, int y, int z, int id) { - base.setBiome(x, y, z, biomeMapping.byId(id)); - } - - @Override - public int getBiome(int x, int y, int z) { - return biomeMapping.getId(base.getBiome(x, y, z)); - } - }; - } - - @Override - public MCABiomeContainer newBiomeContainer(int min, int max) { - MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max); - return getBiomeContainerInterface(getBiomeMapping(), base); - } - - @Override - public MCABiomeContainer newBiomeContainer(int min, int max, int[] data) { - MCAChunkBiomeContainer base = new MCAChunkBiomeContainer<>(getBiomeMapping(), min, max, data); - return getBiomeContainerInterface(getBiomeMapping(), base); - } - - @Override - public int countCustomBiomes() { - AtomicInteger a = new AtomicInteger(0); - - getCustomBiomeRegistry().keySet().forEach((i) -> { - if (i.getNamespace().equals("minecraft")) { - return; - } - - a.incrementAndGet(); - IrisLogging.debug("Custom Biome: " + i); - }); - - return a.get(); - } - - public boolean supportsDataPacks() { - return true; - } - - public void setBiomes(int cx, int cz, World world, Hunk biomes) { - LevelChunk c = ((CraftWorld) world).getHandle().getChunk(cx, cz); - biomes.iterateSync((x, y, z, b) -> c.setNoiseBiome(x, y, z, (Holder) b)); - c.markUnsaved(); - } - - @Override - public MCAPaletteAccess createPalette() { - MCAIdMapper registry = registryCache.aquireNastyPrint(() -> { - Field cf = IdMapper.class.getDeclaredField("tToId"); - Field df = IdMapper.class.getDeclaredField("idToT"); - Field bf = IdMapper.class.getDeclaredField("nextId"); - cf.setAccessible(true); - df.setAccessible(true); - bf.setAccessible(true); - IdMapper blockData = Block.BLOCK_STATE_REGISTRY; - int b = bf.getInt(blockData); - Object2IntMap c = (Object2IntMap) cf.get(blockData); - List d = (List) df.get(blockData); - return new MCAIdMapper(c, d, b); - }); - if (registry == null) { - throw new IllegalStateException("Iris cannot mirror the Minecraft block state id map on this server version"); - } - MCAPalette global = globalCache.aquireNastyPrint(() -> new MCAGlobalPalette<>(registry, ((CraftBlockData) AIR).getState())); - if (global == null) { - throw new IllegalStateException("Iris cannot build the global block state palette on this server version"); - } - java.util.Map, BlockState> innerDecodeCache = new java.util.concurrent.ConcurrentHashMap<>(64); - java.util.Map outerDecodeCache = new java.util.concurrent.ConcurrentHashMap<>(64); - MCAPalettedContainer container = new MCAPalettedContainer<>(global, registry, - i -> innerDecodeCache.computeIfAbsent(i, t -> ((CraftBlockData) NBTWorld.getBlockData(t)).getState()), - i -> NBTWorld.getCompound(CraftBlockData.createData(i)), - ((CraftBlockData) AIR).getState()); - return new MCAWrappedPalettedContainer<>(container, - i -> NBTWorld.getCompound(CraftBlockData.createData(i)), - i -> outerDecodeCache.computeIfAbsent(i, t -> ((CraftBlockData) NBTWorld.getBlockData(t)).getState())); - } - - @Override - public void injectBiomesFromMantle(Chunk e, Mantle mantle) { - ChunkAccess chunk = ((CraftChunk) e).getHandle(ChunkStatus.FULL); - AtomicInteger c = new AtomicInteger(); - AtomicInteger r = new AtomicInteger(); - mantle.iterateChunk(e.getX(), e.getZ(), MatterBiomeInject.class, (x, y, z, b) -> { - if (b != null) { - if (b.isCustom()) { - chunk.setNoiseBiome(x, y, z, getCustomBiomeRegistry().get(b.getBiomeId()).get()); - c.getAndIncrement(); - } else { - chunk.setNoiseBiome(x, y, z, getCustomBiomeRegistry() - .get(net.minecraft.resources.Identifier.parse(b.getBiomeKey())) - .orElseGet(() -> getCustomBiomeRegistry() - .get(net.minecraft.resources.Identifier.parse("minecraft:plains")) - .orElseThrow())); - r.getAndIncrement(); - } - } - }); - } - - @Override - public boolean applyChunkBlocks(Chunk bukkitChunk, TerrainChunk data) { - if (!(data.getChunkData() instanceof CraftChunkData chunkData)) { - return false; - } - - try { - ServerLevel level = ((CraftWorld) bukkitChunk.getWorld()).getHandle(); - LevelChunk chunk = level.getChunk(bukkitChunk.getX(), bukkitChunk.getZ()); - ChunkAccess source = chunkData.getHandle(); - removeBlockEntities(chunk); - - int minY = level.getMinY(); - int baseX = chunk.getPos().getMinBlockX(); - int baseZ = chunk.getPos().getMinBlockZ(); - for (int i = 0; i < chunk.getSectionsCount(); i++) { - LevelChunkSection target = chunk.getSection(i); - LevelChunkSection from = source.getSection(i); - if (from.hasOnlyAir() && target.hasOnlyAir()) { - continue; - } - - int sectionBaseY = minY + (i << 4); - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - BlockState state = from.getBlockState(x, y, z); - target.setBlockState(x, y, z, state, false); - if (state.hasBlockEntity() && state.getBlock() instanceof EntityBlock entityBlock) { - BlockPos pos = new BlockPos(baseX + x, sectionBaseY + y, baseZ + z); - BlockEntity entity = entityBlock.newBlockEntity(pos, state); - if (entity != null) { - chunk.setBlockEntity(entity); - } - } - } - } - } - } - - finishChunkRewrite(level, chunk); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, Hunk data) { - if (!(chunkData instanceof CraftChunkData craftChunkData)) { - return false; - } - - try { - ChunkAccess access = craftChunkData.getHandle(); - int minY = craftChunkData.getMinHeight(); - int height = craftChunkData.getMaxHeight() - minY; - int accessMinY = access.getMinY(); - int accessMaxY = accessMinY + access.getHeight(); - int yStart = Math.max(0, accessMinY - minY); - int yEnd = Math.min(height, accessMaxY - minY); - int baseX = access.getPos().getMinBlockX(); - int baseZ = access.getPos().getMinBlockZ(); - ChunkDataHunkHolder holder = data instanceof ChunkDataHunkHolder chunkDataHolder ? chunkDataHolder : null; - for (int z = 0; z < 16; z++) { - for (int y = yStart; y < yEnd; y++) { - int blockY = y + minY; - int sectionIndex = (blockY - accessMinY) >> 4; - LevelChunkSection section = access.getSection(sectionIndex); - int sectionY = blockY & 15; - for (int x = 0; x < 16; x++) { - PlatformBlockState platformState = holder == null ? data.getRaw(x, y, z) : holder.getStoredRaw(x, y, z); - if (platformState == null) { - continue; - } - - BlockData blockData = (BlockData) platformState.nativeHandle(); - if (blockData instanceof IrisCustomData customData) { - blockData = customData.getBase(); - } - if (!(blockData instanceof CraftBlockData craftBlockData)) { - return false; - } - - BlockState state = craftBlockData.getState(); - BlockState oldState = section.setBlockState(x, sectionY, z, state, false); - if (state.hasBlockEntity()) { - BlockPos pos = new BlockPos(baseX + x, blockY, baseZ + z); - BlockEntity entity = ((EntityBlock) state.getBlock()).newBlockEntity(pos, state); - if (entity == null) { - access.removeBlockEntity(pos); - } else { - access.setBlockEntity(entity); - } - } else if (oldState != null && oldState.hasBlockEntity()) { - access.removeBlockEntity(new BlockPos(baseX + x, blockY, baseZ + z)); - } - } - } - } - - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean forceEvictChunk(World world, int chunkX, int chunkZ) { - try { - if (!world.isChunkLoaded(chunkX, chunkZ)) { - return true; - } - return world.unloadChunk(chunkX, chunkZ, true); - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public CompletableFuture flushSavedTerrainCapture(World world) { - return NmsSavedTerrainCapture.flush(world); - } - - @Override - public CompletableFuture captureSavedTerrainChunk(World world, int chunkX, int chunkZ, - int minimumY, int height) { - return NmsSavedTerrainCapture.capture(world, chunkX, chunkZ, minimumY, height); - } - - @Override - public boolean saveAndUnloadChunk(World world, int x, int z) { - try { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkHolderManager chm = level.moonrise$getChunkTaskScheduler().chunkHolderManager; - chm.processTicketUpdates(); - NewChunkHolder holder = chm.getChunkHolder(x, z); - if (holder != null) { - holder.save(false); - } - chm.processUnloads(); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - @Override - public boolean pollChunkTask(World world) { - if (FoliaScheduler.isRegionizedRuntime(Bukkit.getServer())) { - return false; - } - ServerLevel level = ((CraftWorld) world).getHandle(); - if (Thread.currentThread() != level.getServer().getRunningThread()) { - throw new IllegalStateException("Native chunk tasks require the server lifecycle thread"); - } - return level.getChunkSource().pollTask(); - } - - @Override - public void flushChunkIO(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - MoonriseRegionFileIO.flush(level); - } - - @Override - public void reconcileNativeStructurePois(Chunk chunk) { - NativeStructurePoiUpdates.reconcile((LevelChunk) ((CraftChunk) chunk).getHandle(ChunkStatus.FULL)); - } - - @Override - public boolean clearChunkBlocks(Chunk bukkitChunk) { - try { - ServerLevel level = ((CraftWorld) bukkitChunk.getWorld()).getHandle(); - LevelChunk chunk = level.getChunk(bukkitChunk.getX(), bukkitChunk.getZ()); - removeBlockEntities(chunk); - - BlockState air = ((CraftBlockData) AIR).getState(); - for (int i = 0; i < chunk.getSectionsCount(); i++) { - LevelChunkSection section = chunk.getSection(i); - if (section.hasOnlyAir()) { - continue; - } - - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - section.setBlockState(x, y, z, air, false); - } - } - } - } - - finishChunkRewrite(level, chunk); - return true; - } catch (Throwable e) { - IrisLogging.reportError(e); - return false; - } - } - - private void removeBlockEntities(LevelChunk chunk) { - for (BlockPos pos : new ArrayList<>(chunk.getBlockEntities().keySet())) { - chunk.removeBlockEntity(pos); - } - } - - private void finishChunkRewrite(ServerLevel level, LevelChunk chunk) { - Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); - chunk.markUnsaved(); - ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) level.getChunkSource().getLightEngine(); - lightEngine.starlight$serverRelightChunks(List.of(chunk.getPos()), p -> { - }, c -> { - }); - } - - public ItemStack applyCustomNbt(ItemStack itemStack, KMap customNbt) throws IllegalArgumentException { - if (customNbt != null && !customNbt.isEmpty()) { - net.minecraft.world.item.ItemStack s = CraftItemStack.asNMSCopy(itemStack); - - try { - net.minecraft.nbt.CompoundTag tag = TagParser.parseCompoundFully((new JSONObject(customNbt)).toString()); - tag.merge(s.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag()); - s.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); - } catch (CommandSyntaxException var5) { - throw new IllegalArgumentException(var5); - } - - return CraftItemStack.asBukkitCopy(s); - } else { - return itemStack; - } - } - - public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - validateDimensionContract(engine, world, level); - - ChunkMap chunkMap = level.getChunkSource().chunkMap; - Field worldGenContextField = getField(chunkMap.getClass(), WorldGenContext.class); - worldGenContextField.setAccessible(true); - WorldGenContext worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap); - - IrisChunkGenerator irisGenerator = new IrisChunkGenerator(worldGenContext.generator(), seed, engine, world); - WorldGenContext newContext = new WorldGenContext( - worldGenContext.level(), irisGenerator, - worldGenContext.structureManager(), worldGenContext.lightEngine(), worldGenContext.mainThreadExecutor(), worldGenContext.unsavedListener()); - - worldGenContextField.set(chunkMap, newContext); - net.minecraft.world.level.chunk.ChunkGenerator activeGenerator = level.getChunkSource().getGenerator(); - if (activeGenerator != irisGenerator) { - throw new IllegalStateException("Iris generator injection did not become the active Paper chunk generator; active=" - + activeGenerator.getClass().getName()); - } - retargetStructureCheck(level, irisGenerator); - } - - @Override - public DatapackStructureScopeResult scopeDatapackStructures( - World world, - DatapackStructureScopeIndex scopeIndex, - Set declaredSources, - IrisImportedStructureControl importedStructures - ) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkMap chunkMap = level.getChunkSource().chunkMap; - ChunkGeneratorStructureState currentState = level.getChunkSource().getGeneratorState(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - ChunkGeneratorStructureState scopedState = createStructureState(level, generator, currentState); - Registry structureSetRegistry = - level.registryAccess().lookupOrThrow(Registries.STRUCTURE_SET); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( - scopedState.possibleStructureSets(), - scopeIndex, - declaredSources, - importedStructures, - DatapackStructureStateFilter.keyIndex( - structureSetRegistry.listElements().toList())); - - Field possibleSetsField = getField(scopedState.getClass(), List.class); - possibleSetsField.setAccessible(true); - possibleSetsField.set(scopedState, selection.structureSets()); - - Field stateField = getField(chunkMap.getClass(), ChunkGeneratorStructureState.class); - stateField.setAccessible(true); - BukkitChunkGenerator platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator bukkitGenerator - ? bukkitGenerator - : null; - boolean studioBootstrap = platformGenerator != null - && platformGenerator.isStudioEntryBootstrapActive(); - boolean authoringStudio = platformGenerator != null - && platformGenerator.isAuthoringStudio(); - if (authoringStudio) { - requireIrisGenerator(generator); - if (currentState.possibleStructureSets().isEmpty()) { - currentState.ensureStructuresGenerated(); - } else { - ChunkGeneratorStructureState bootstrapState = createStructureState(level, generator, currentState); - Field bootstrapSetsField = getField(bootstrapState.getClass(), List.class); - bootstrapSetsField.setAccessible(true); - bootstrapSetsField.set(bootstrapState, List.of()); - bootstrapState.ensureStructuresGenerated(); - stateField.set(chunkMap, bootstrapState); - } - } else if (studioBootstrap) { - IrisChunkGenerator irisGenerator = requireIrisGenerator(generator); - irisGenerator.retainStudioStructureState(level, chunkMap, scopedState); - try { - stateField.set(chunkMap, scopedState); - } catch (IllegalAccessException | RuntimeException | Error failure) { - irisGenerator.abandonStudioStructureState(); - throw failure; - } - } else { - initializeAndPublishStructureState( - generator, - scopedState, - () -> stateField.set(chunkMap, scopedState)); - } - return new DatapackStructureScopeResult( - selection.retainedManagedSets(), - selection.excludedManagedSets()); - } - - @Override - public CompletableFuture completeStudioStructureBootstrap(World world) throws NoSuchFieldException, IllegalAccessException { - ServerLevel level = ((CraftWorld) world).getHandle(); - ChunkMap chunkMap = level.getChunkSource().chunkMap; - IrisChunkGenerator generator = requireIrisGenerator(level.getChunkSource().getGenerator()); - IrisChunkGenerator.StudioStructureState retained = - generator.retainedStudioStructureState(level, chunkMap); - if (retained == null) { - return CompletableFuture.completedFuture(null); - } - return generator.activateStudioStructureState(retained); - } - - @Override - public void abandonStudioStructureBootstrap(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - net.minecraft.world.level.chunk.ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (generator instanceof IrisChunkGenerator irisGenerator) { - irisGenerator.abandonStudioStructureState(); - } - } - - private ChunkGeneratorStructureState createStructureState( - ServerLevel level, - net.minecraft.world.level.chunk.ChunkGenerator generator, - ChunkGeneratorStructureState currentState - ) { - return generator.createState( - level.registryAccess().lookupOrThrow(Registries.STRUCTURE_SET), - currentState.randomState(), - currentState.getLevelSeed(), - level.spigotConfig); - } - - private void initializeAndPublishStructureState( - net.minecraft.world.level.chunk.ChunkGenerator generator, - ChunkGeneratorStructureState structureState, - IrisChunkGenerator.StructureStatePublisher publisher - ) throws IllegalAccessException { - if (generator instanceof IrisChunkGenerator irisGenerator) { - irisGenerator.initializeAndPublishStructureState(structureState, publisher); - return; - } - structureState.ensureStructuresGenerated(); - publisher.publish(); - } - - private IrisChunkGenerator requireIrisGenerator( - net.minecraft.world.level.chunk.ChunkGenerator generator - ) { - if (generator instanceof IrisChunkGenerator irisGenerator) { - return irisGenerator; - } - throw new IllegalStateException("Studio native structure state is not owned by the active Iris generator."); - } - - private void validateDimensionContract(Engine engine, World world, ServerLevel level) { - DimensionType actualType = level.dimensionType(); - String actualTypeKey = level.dimensionTypeRegistration().unwrapKey() - .map(key -> key.identifier().toString()) - .orElse(""); - IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(engine.getDimension(), "iris"); - IrisDimensionRuntimeContract actual = new IrisDimensionRuntimeContract( - actualTypeKey, - actualType.minY(), - actualType.height(), - actualType.logicalHeight()); - String runtimeName = "Bukkit world '" + world.getName() + "'"; - expected.requireExact(runtimeName, actual); - expected.requireHeight(runtimeName, level.getMinY(), level.getHeight()); - expected.requireHeight(runtimeName, world.getMinHeight(), world.getMaxHeight() - world.getMinHeight()); - IrisLogging.debug("Loaded world " + world.getName() + " with exact Iris dimension type " + actualTypeKey); - } - - private static void retargetStructureCheck(ServerLevel level, IrisChunkGenerator generator) throws NoSuchFieldException, IllegalAccessException { - Field structureCheckField = getField(level.getClass(), StructureCheck.class); - structureCheckField.setAccessible(true); - Object structureCheck = structureCheckField.get(level); - if (structureCheck == null) { - return; - } - Field generatorField = getField(structureCheck.getClass(), net.minecraft.world.level.chunk.ChunkGenerator.class); - generatorField.setAccessible(true); - generatorField.set(structureCheck, generator); - Field biomeSourceField = getField(structureCheck.getClass(), BiomeSource.class); - biomeSourceField.setAccessible(true); - biomeSourceField.set(structureCheck, generator.getBiomeSource()); - } - - public Vector3d getBoundingbox(org.bukkit.entity.EntityType entity) { - if (entity == null) { - return null; - } - - try { - // Registry lookup instead of an EntityType static-field scan: 26.2 moved the constants - // to a separate EntityTypes holder class that 26.1.2 does not have, while the registry - // resolves identically on both. ENTITY_TYPE is a DefaultedRegistry, so guard containsKey - // to avoid silently resolving unknown keys to the default entry. - Identifier key = Identifier.fromNamespaceAndPath(entity.getKey().getNamespace(), entity.getKey().getKey()); - if (!BuiltInRegistries.ENTITY_TYPE.containsKey(key)) { - return null; - } - EntityType entityType = BuiltInRegistries.ENTITY_TYPE.getValue(key); - if (entityType == null) { - return null; - } - return new Vector3d(entityType.getWidth(), entityType.getHeight(), entityType.getWidth()); - } catch (Throwable e) { - IrisLogging.error("Unable to get entity dimensions for " + entity + "!"); - IrisLogging.reportError(e); - return null; - } - } - - - @Override - public Entity spawnEntity(Location location, org.bukkit.entity.EntityType type, CreatureSpawnEvent.SpawnReason reason) { - if (location == null || location.getWorld() == null || type == null || type.getEntityClass() == null) { - return null; - } - CraftWorld world = (CraftWorld) location.getWorld(); - if (world.getDifficulty() == Difficulty.PEACEFUL) { - EntityType nativeType = BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse(type.getKey().toString())); - if (nativeType == null || !nativeType.isAllowedInPeaceful()) { - return null; - } - } - return world.spawn(location, type.getEntityClass(), null, reason); - } - - @Override - public Color getBiomeColor(Location location, BiomeColor type) { - ServerLevel reader = ((CraftWorld) location.getWorld()).getHandle(); - BlockPos pos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()); - Holder holder = reader.getBiome(pos); - net.minecraft.world.level.biome.Biome biome = holder.value(); - if (biome == null) throw new IllegalArgumentException("Invalid biome: " + holder.unwrapKey().orElse(null)); - - net.minecraft.world.attribute.EnvironmentAttributeSystem attributes = reader.environmentAttributes(); - int rgba = switch (type) { - case FOG -> environmentColor(attributes.getValue(EnvironmentAttributes.FOG_COLOR, pos)); - case WATER -> biome.getWaterColor(); - case WATER_FOG -> environmentColor(attributes.getValue(EnvironmentAttributes.WATER_FOG_COLOR, pos)); - case SKY -> environmentColor(attributes.getValue(EnvironmentAttributes.SKY_COLOR, pos)); - case FOLIAGE -> biome.getFoliageColor(); - case GRASS -> biome.getGrassColor(location.getBlockX(), location.getBlockZ()); - }; - if (rgba == 0) { - if (BiomeColor.FOLIAGE == type && biome.getSpecialEffects().foliageColorOverride().isEmpty()) - return null; - if (BiomeColor.GRASS == type && biome.getSpecialEffects().grassColorOverride().isEmpty()) - return null; - } - return new Color(rgba, true); - } - - private static Field getField(Class clazz, Class fieldType) throws NoSuchFieldException { - try { - for (Field f : clazz.getDeclaredFields()) { - if (f.getType().equals(fieldType)) - return f; - } - throw new NoSuchFieldException(fieldType.getName()); - } catch (NoSuchFieldException var4) { - Class superClass = clazz.getSuperclass(); - if (superClass == null) { - throw var4; - } else { - return getField(superClass, fieldType); - } - } - } - - public static Holder biomeToBiomeBase(Registry registry, Biome biome) { - if (registry == null || biome == null) { - return null; - } - - NamespacedKey biomeKey = resolveBiomeKey(biome); - if (biomeKey == null) { - return null; - } - - ResourceKey key = ResourceKey.create(Registries.BIOME, CraftNamespacedKey.toMinecraft(biomeKey)); - return registry.get(key).orElse(null); - } - - private static NamespacedKey resolveBiomeKey(Biome biome) { - Object keyOrNullValue = invokeNoThrow(biome, "getKeyOrNull", new Class[0]); - if (keyOrNullValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - Object keyOrThrowValue = invokeNoThrow(biome, "getKeyOrThrow", new Class[0]); - if (keyOrThrowValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - Object keyValue = invokeNoThrow(biome, "getKey", new Class[0]); - if (keyValue instanceof NamespacedKey namespacedKey) { - return namespacedKey; - } - - return null; - } - - private static Object invokeNoThrow(Object target, String methodName, Class[] parameterTypes, Object... args) { - if (target == null) { - return null; - } - - try { - Method method = target.getClass().getMethod(methodName, parameterTypes); - return method.invoke(target, args); - } catch (Throwable ignored) { - return null; - } - } - - private static int environmentColor(Vector3fc color) { - return ARGB.colorFromFloat(1F, color.x(), color.y(), color.z()); - } - - @Override - public DataVersion getDataVersion() { - return DataVersion.V26_3; - } - - @Override - public int getSpawnChunkCount(World world) { - return 0; - } - - @Override - public boolean missingDimensionTypes(String... keys) { - Registry type = registry().lookupOrThrow(Registries.DIMENSION_TYPE); - return !Arrays.stream(keys) - .map(key -> Identifier.fromNamespaceAndPath("iris", key)) - .allMatch(type::containsKey); - } - - @Override - public boolean injectBukkit() { - return worldLifecycle.injectBukkit(); - } - - @Override - public void ensureServerLevelInjection() { - worldLifecycle.ensureServerLevelInjection(); - } - - @Override - public void uninjectBukkit() { - worldLifecycle.uninjectBukkit(); - } - - @Override - public boolean isServerStopping() { - return worldLifecycle.isServerStopping(); - } - - @Override - public ServerShutdownBoundary createServerShutdownBoundary() { - return worldLifecycle.createServerShutdownBoundary(); - } - - @Override - public void deferPluginClassLoaderClose() { - worldLifecycle.deferPluginClassLoaderClose(); - } - - @Override - public void releasePluginClassLoaderClose() { - worldLifecycle.releasePluginClassLoaderClose(); - } - - @Override - public KMap> getBlockProperties() { - KMap> states = new KMap<>(); - - for (Block block : registry().lookupOrThrow(Registries.BLOCK)) { - BlockState state = block.defaultBlockState(); - if (state == null) state = block.getStateDefinition().any(); - BlockState finalState = state; - - states.put(CraftMagicNumbers.getMaterial(block), block.getStateDefinition() - .getProperties() - .stream() - .map(p -> createProperty(p, finalState)) - .toList()); - } - return states; - } - - private > BlockProperty createProperty(Property property, BlockState state) { - return new BlockProperty(property.getName(), property.getValueClass(), state.getValue(property), property.getPossibleValues(), property::getName); - } - - @Override - public Object createRuntimeLevelStem(Object registryAccess, ChunkGenerator raw) { - if (!(registryAccess instanceof RegistryAccess access)) { - throw new IllegalStateException("Runtime LevelStem creation requires a RegistryAccess instance."); - } - if (!(raw instanceof PlatformChunkGenerator generator)) { - throw new IllegalStateException("Generator is not platform chunk generator!"); - } - - Identifier dimensionKey = Identifier.fromNamespaceAndPath("iris", generator.getTarget().getDimension().getDimensionTypeKey()); - Holder.Reference dimensionType = access.lookupOrThrow(Registries.DIMENSION_TYPE) - .getOrThrow(ResourceKey.create(Registries.DIMENSION_TYPE, dimensionKey)); - return new LevelStem(dimensionType, chunkGenerator(access)); - } - - private net.minecraft.world.level.chunk.ChunkGenerator chunkGenerator(RegistryAccess access) { - FlatLevelGeneratorSettings settings = new FlatLevelGeneratorSettings(Optional.empty(), access.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.THE_VOID), List.of()); - settings.getLayersInfo().add(new FlatLayerInfo(1, Blocks.AIR)); - settings.updateLayers(); - return new FlatLevelSource(settings); - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdates.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdates.java deleted file mode 100644 index 254a4961b..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdates.java +++ /dev/null @@ -1,142 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import ca.spottedleaf.moonrise.common.util.TickThread; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.SectionPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.entity.ai.village.poi.PoiRecord; -import net.minecraft.world.entity.ai.village.poi.PoiSection; -import net.minecraft.world.entity.ai.village.poi.PoiType; -import net.minecraft.world.entity.ai.village.poi.PoiTypes; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.ProtoChunk; -import org.bukkit.NamespacedKey; -import org.bukkit.persistence.PersistentDataType; - -import java.util.Arrays; -import java.util.Optional; - -final class NativeStructurePoiUpdates { - private static final NamespacedKey DIRTY_SECTIONS = new NamespacedKey("iris", "native_poi_sections"); - - private final int skipPoiFlag; - - NativeStructurePoiUpdates(int skipPoiFlag) { - this.skipPoiFlag = skipPoiFlag; - } - - static NativeStructurePoiUpdates create() { - try { - return new NativeStructurePoiUpdates(Block.class.getField("UPDATE_SKIP_POI").getInt(null)); - } catch (NoSuchFieldException e) { - return null; - } catch (IllegalAccessException e) { - throw new ExceptionInInitializerError(e); - } - } - - boolean setBlock(WorldGenLevel world, BlockPos position, BlockState state, int flags, int limit) { - if ((flags & skipPoiFlag) != 0) { - return world.setBlock(position, state, flags, limit); - } - ChunkAccess chunk = world.getChunk(position); - if (!(chunk instanceof ProtoChunk)) { - return world.setBlock(position, state, flags, limit); - } - if (!(chunk instanceof ImposterProtoChunk) - && !PoiTypes.forState(chunk.getBlockState(position)).equals(PoiTypes.forState(state))) { - markSection(chunk, chunk.getSectionIndex(position.getY())); - } - return world.setBlock(position, state, flags | skipPoiFlag, limit); - } - - static void reconcile(LevelChunk chunk) { - long[] sections = chunk.persistentDataContainer.get(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY); - if (sections == null) { - return; - } - validateSections(chunk, sections); - ServerLevel world = (ServerLevel) chunk.getLevel(); - TickThread.ensureTickThread(world, chunk.getPos(), "Native structure POIs require the chunk owning thread"); - PoiManager manager = world.getPoiManager(); - for (int word = 0; word < sections.length; word++) { - long remaining = sections[word]; - while (remaining != 0) { - int index = (word << 6) + Long.numberOfTrailingZeros(remaining); - reconcileSection(chunk, manager, index); - remaining &= remaining - 1; - } - } - chunk.persistentDataContainer.remove(DIRTY_SECTIONS); - chunk.markUnsaved(); - } - - private static void markSection(ChunkAccess chunk, int index) { - long[] sections = chunk.persistentDataContainer.get(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY); - int word = index >>> 6; - long bit = 1L << (index & 63); - if (sections == null) { - sections = new long[word + 1]; - } else { - validateSections(chunk, sections); - if (word < sections.length && (sections[word] & bit) != 0) { - return; - } - if (word >= sections.length) { - sections = Arrays.copyOf(sections, word + 1); - } - } - sections[word] |= bit; - chunk.persistentDataContainer.set(DIRTY_SECTIONS, PersistentDataType.LONG_ARRAY, sections); - } - - private static void validateSections(ChunkAccess chunk, long[] sections) { - int count = chunk.getSectionsCount(); - int words = (count + 63) >>> 6; - if (sections.length > words - || sections.length == words && (count & 63) != 0 - && (sections[words - 1] >>> (count & 63)) != 0) { - throw new IllegalArgumentException("Native POI repair sections exceed chunk height at " + chunk.getPos()); - } - } - - private static void reconcileSection(LevelChunk chunk, PoiManager manager, int index) { - SectionPos sectionPosition = SectionPos.of(chunk.getPos(), chunk.getSectionYFromSectionIndex(index)); - Optional existing = manager.getOrLoad(sectionPosition.asLong()); - if (existing.isPresent()) { - for (PoiRecord record : existing.get().getRecords(type -> true, PoiManager.Occupancy.ANY).toList()) { - if (!PoiTypes.forState(chunk.getBlockState(record.getPos())).equals(Optional.of(record.getPoiType()))) { - manager.remove(record.getPos()); - } - } - } - LevelChunkSection section = chunk.getSection(index); - if (!section.maybeHas(PoiTypes::hasPoi)) { - return; - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int y = 0; y < 16; y++) { - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - Optional> type = PoiTypes.forState(section.getBlockState(x, y, z)); - if (type.isEmpty()) { - continue; - } - position.set(sectionPosition.minBlockX() + x, sectionPosition.minBlockY() + y, - sectionPosition.minBlockZ() + z); - if (!manager.getType(position).equals(type)) { - manager.add(position.immutable(), type.get()); - } - } - } - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccess.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccess.java deleted file mode 100644 index 6166e9210..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccess.java +++ /dev/null @@ -1,606 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.nativegen.v26_3_R1.WorldgenTerrainHeightmaps; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.QuartPos; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.attribute.EnvironmentAttributeReader; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.WorldGenLevel; -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.BlockEntityType; -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.EmptyLevelChunk; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.entity.EntityTypeTest; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.levelgen.Heightmap; -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.storage.LevelData; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.ticks.LevelTickAccess; -import net.minecraft.world.ticks.ScheduledTick; -import org.bukkit.event.entity.CreatureSpawnEvent; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; -import java.util.function.Supplier; - -final class NativeStructureWorldgenAccess implements WorldGenLevel { - private static final int WRITE_RADIUS = 1; - private static final NativeStructurePoiUpdates POI_UPDATES = NativeStructurePoiUpdates.create(); - - private final WorldGenLevel delegate; - private final ChunkPos generationCenter; - private final IntBinaryOperator surfaceFirstFreeY; - private final IntBinaryOperator floorFirstFreeY; - private final boolean virtualizeHeights; - private final Predicate protectedPosition; - private final Holder fallbackBiome; - private final BiomeManager biomeManager; - private final AABB generationBounds; - private final LevelTickAccess blockTicks; - private final LevelTickAccess fluidTicks; - private final Long2LongOpenHashMap terrainHeights; - private final Long2ObjectOpenHashMap outsideChunks; - - private NativeStructureWorldgenAccess(WorldGenLevel delegate, Boundary boundary) { - this.delegate = Objects.requireNonNull(delegate, "Native structure world access requires a delegate"); - this.generationCenter = Objects.requireNonNull( - boundary.generationCenter(), "Native structure world access requires a generation center"); - this.surfaceFirstFreeY = Objects.requireNonNull( - boundary.surfaceFirstFreeY(), "Native structure world access requires a surface resolver"); - this.floorFirstFreeY = Objects.requireNonNull( - boundary.floorFirstFreeY(), "Native structure world access requires an ocean-floor resolver"); - this.virtualizeHeights = boundary.virtualizeHeights(); - this.protectedPosition = Objects.requireNonNull( - boundary.protectedPosition(), "World access requires a protected-position predicate"); - BlockPos biomeSample = generationCenter.getMiddleBlockPosition(delegate.getSeaLevel()); - this.fallbackBiome = delegate.getBiome(biomeSample); - this.biomeManager = delegate.getBiomeManager().withDifferentSource(this); - this.generationBounds = new AABB( - generationCenter.getMinBlockX() - 16, - delegate.getMinY(), - generationCenter.getMinBlockZ() - 16, - generationCenter.getMaxBlockX() + 17, - delegate.getMinY() + delegate.getHeight(), - generationCenter.getMaxBlockZ() + 17); - this.blockTicks = new BoundedTickAccess<>(delegate.getBlockTicks(), this::isWritable); - this.fluidTicks = new BoundedTickAccess<>(delegate.getFluidTicks(), this::isWritable); - this.terrainHeights = new Long2LongOpenHashMap(); - this.terrainHeights.defaultReturnValue(Long.MIN_VALUE); - this.outsideChunks = new Long2ObjectOpenHashMap<>(); - } - - static NativeStructureWorldgenAccess create(WorldGenLevel delegate, ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - return new NativeStructureWorldgenAccess(delegate, new Boundary( - generationCenter, surfaceFirstFreeY, floorFirstFreeY, virtualizeHeights, protectedPosition)); - } - - @Override - public long getSeed() { - return delegate.getSeed(); - } - - @Override - public boolean ensureCanWrite(BlockPos position) { - return isWritable(position) && delegate.ensureCanWrite(position); - } - - @Override - public void setCurrentlyGenerating(Supplier description) { - delegate.setCurrentlyGenerating(description); - } - - @Override - public ServerLevel getLevel() { - return delegate.getLevel(); - } - - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos position) { - if (isReadable(position)) { - return delegate.getCurrentDifficultyAt(position); - } - return delegate.getCurrentDifficultyAt(generationCenter.getMiddleBlockPosition( - Mth.clamp(position.getY(), getMinY(), getMaxY() - 1))); - } - - @Override - public long nextSubTickCount() { - return delegate.nextSubTickCount(); - } - - @Override - public LevelTickAccess getBlockTicks() { - return blockTicks; - } - - @Override - public LevelTickAccess getFluidTicks() { - return fluidTicks; - } - - @Override - public LevelData getLevelData() { - return delegate.getLevelData(); - } - - @Override - public MinecraftServer getServer() { - return delegate.getServer(); - } - - @Override - public ChunkSource getChunkSource() { - return delegate.getChunkSource(); - } - - @Override - public RandomSource getRandom() { - return delegate.getRandom(); - } - - @Override - public void updateNeighborsAt(BlockPos position, Block block) { - if (isWritableNeighbourhood(position)) { - delegate.updateNeighborsAt(position, block); - } - } - - @Override - public void neighborShapeChanged(Direction direction, BlockPos position, - BlockPos neighbourPosition, BlockState neighbourState, - int updateFlags, int updateLimit) { - if (isWritable(position) && isWritable(neighbourPosition)) { - delegate.neighborShapeChanged( - direction, position, neighbourPosition, neighbourState, updateFlags, updateLimit); - } - } - - @Override - public void playSound(Entity source, BlockPos position, SoundEvent sound, - SoundSource soundSource, float volume, float pitch) { - if (isWritable(position)) { - delegate.playSound(source, position, sound, soundSource, volume, pitch); - } - } - - @Override - public void addParticle(ParticleOptions particle, double x, double y, double z, - double velocityX, double velocityY, double velocityZ) { - if (isWritable(BlockPos.containing(x, y, z))) { - delegate.addParticle(particle, x, y, z, velocityX, velocityY, velocityZ); - } - } - - @Override - public void levelEvent(Entity source, int eventId, BlockPos position, int data) { - if (isWritable(position)) { - delegate.levelEvent(source, eventId, position, data); - } - } - - @Override - public void gameEvent(Holder event, Vec3 position, GameEvent.Context context) { - if (isWritable(BlockPos.containing(position))) { - delegate.gameEvent(event, position, context); - } - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus status, boolean create) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ, status, create); - } - return create ? outsideChunk(chunkX, chunkZ) : null; - } - - @Override - public ChunkAccess getChunkIfLoadedImmediately(int chunkX, int chunkZ) { - if (!isInsideGenerationRegion(chunkX, chunkZ)) { - return null; - } - return delegate.getChunkIfLoadedImmediately(chunkX, chunkZ); - } - - @Override - public boolean hasChunk(int chunkX, int chunkZ) { - return isInsideGenerationRegion(chunkX, chunkZ) && delegate.hasChunk(chunkX, chunkZ); - } - - @Override - public int getHeight(Heightmap.Types type, int x, int z) { - if (!virtualizeHeights && isInsideGenerationRegion(x >> 4, z >> 4)) { - return delegate.getHeight(type, x, z); - } - return height(type, x, z); - } - - @Override - public int getSkyDarken() { - return delegate.getSkyDarken(); - } - - @Override - public BiomeManager getBiomeManager() { - return biomeManager; - } - - @Override - public Holder getNoiseBiome(int quartX, int quartY, int quartZ) { - return getUncachedNoiseBiome(quartX, quartY, quartZ); - } - - @Override - public Holder getUncachedNoiseBiome(int quartX, int quartY, int quartZ) { - if (isInsideGenerationRegion(QuartPos.toSection(quartX), QuartPos.toSection(quartZ))) { - return delegate.getUncachedNoiseBiome(quartX, quartY, quartZ); - } - return fallbackBiome; - } - - @Override - public boolean isClientSide() { - return delegate.isClientSide(); - } - - @Override - public int getSeaLevel() { - return delegate.getSeaLevel(); - } - - @Override - public DimensionType dimensionType() { - return delegate.dimensionType(); - } - - @Override - public int getMinY() { - return delegate.getMinY(); - } - - @Override - public int getHeight() { - return delegate.getHeight(); - } - - @Override - public RegistryAccess registryAccess() { - return delegate.registryAccess(); - } - - @Override - public FeatureFlagSet enabledFeatures() { - return delegate.enabledFeatures(); - } - - @Override - public EnvironmentAttributeReader environmentAttributes() { - return delegate.environmentAttributes(); - } - - @Override - public LevelLightEngine getLightEngine() { - return delegate.getLightEngine(); - } - - @Override - public int getBrightness(LightLayer layer, BlockPos position) { - if (isReadable(position)) { - return delegate.getBrightness(layer, position); - } - return layer == LightLayer.SKY && canSeeSky(position) ? 15 : 0; - } - - @Override - public int getRawBrightness(BlockPos position, int ambientDarkening) { - if (isReadable(position)) { - return delegate.getRawBrightness(position, ambientDarkening); - } - return Math.max(0, getBrightness(LightLayer.SKY, position) - ambientDarkening); - } - - @Override - public boolean canSeeSky(BlockPos position) { - if (isReadable(position)) { - return delegate.canSeeSky(position); - } - return position.getY() >= height(Heightmap.Types.WORLD_SURFACE_WG, position.getX(), position.getZ()); - } - - @Override - public WorldBorder getWorldBorder() { - return delegate.getWorldBorder(); - } - - @Override - public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunkForCollisions(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public BlockEntity getBlockEntity(BlockPos position) { - return isWritable(position) ? delegate.getBlockEntity(position) : null; - } - - @Override - public Optional getBlockEntity( - BlockPos position, BlockEntityType type) { - return isWritable(position) ? delegate.getBlockEntity(position, type) : Optional.empty(); - } - - @Override - public BlockState getBlockState(BlockPos position) { - return isReadable(position) ? delegate.getBlockState(position) : terrainState(position); - } - - @Override - public BlockState getBlockStateIfLoaded(BlockPos position) { - return isReadable(position) ? delegate.getBlockStateIfLoaded(position) : null; - } - - @Override - public FluidState getFluidState(BlockPos position) { - return isReadable(position) ? delegate.getFluidState(position) : terrainState(position).getFluidState(); - } - - @Override - public FluidState getFluidIfLoaded(BlockPos position) { - return isReadable(position) ? delegate.getFluidIfLoaded(position) : null; - } - - @Override - public List getEntities(Entity source, AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(source, boundedArea, predicate); - } - - @Override - public List getEntities( - EntityTypeTest type, - AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(type, boundedArea, predicate); - } - - @Override - public List players() { - return delegate.players(); - } - - @Override - public boolean isStateAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getBlockState(position)); - } - - @Override - public boolean isFluidAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getFluidState(position)); - } - - @Override - public BlockPos getHeightmapPos(Heightmap.Types type, BlockPos position) { - return position.atY(getHeight(type, position.getX(), position.getZ())); - } - - @Override - public boolean setBlock(BlockPos position, BlockState state, int updateFlags, int updateLimit) { - return isWritable(position) - && (POI_UPDATES != null - ? POI_UPDATES.setBlock(delegate, position, state, updateFlags, updateLimit) - : delegate.setBlock(position, state, updateFlags, updateLimit)); - } - - @Override - public boolean removeBlock(BlockPos position, boolean move) { - if (!isWritable(position)) { - return false; - } - return POI_UPDATES != null && delegate instanceof WorldGenRegion - ? setBlock(position, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL, Block.UPDATE_LIMIT) - : delegate.removeBlock(position, move); - } - - @Override - public boolean destroyBlock(BlockPos position, boolean drop, Entity source, int updateLimit) { - if (!isWritable(position)) { - return false; - } - return POI_UPDATES != null && delegate instanceof WorldGenRegion - ? !delegate.getBlockState(position).isAir() - && setBlock(position, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL, updateLimit) - : delegate.destroyBlock(position, drop, source, updateLimit); - } - - @Override - public boolean addFreshEntity(Entity entity) { - return isWritable(entity.blockPosition()) && delegate.addFreshEntity(entity); - } - - @Override - public boolean addFreshEntity(Entity entity, CreatureSpawnEvent.SpawnReason reason) { - return isWritable(entity.blockPosition()) && delegate.addFreshEntity(entity, reason); - } - - private boolean isReadable(BlockPos position) { - return isInsideGenerationRegion(position.getX() >> 4, position.getZ() >> 4) - && !isOutsideBuildHeight(position); - } - - private boolean isWritable(BlockPos position) { - return isReadable(position) && !protectedPosition.test(position); - } - - private boolean isWritableNeighbourhood(BlockPos position) { - return isWritable(position) - && isWritable(position.north()) - && isWritable(position.south()) - && isWritable(position.east()) - && isWritable(position.west()) - && isWritable(position.above()) - && isWritable(position.below()); - } - - private AABB boundedArea(AABB area) { - double minX = Math.max(area.minX, generationBounds.minX); - double minY = Math.max(area.minY, generationBounds.minY); - double minZ = Math.max(area.minZ, generationBounds.minZ); - double maxX = Math.min(area.maxX, generationBounds.maxX); - double maxY = Math.min(area.maxY, generationBounds.maxY); - double maxZ = Math.min(area.maxZ, generationBounds.maxZ); - if (minX >= maxX || minY >= maxY || minZ >= maxZ) { - return null; - } - if (minX == area.minX && minY == area.minY && minZ == area.minZ - && maxX == area.maxX && maxY == area.maxY && maxZ == area.maxZ) { - return area; - } - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - boolean isInsideGenerationRegion(int chunkX, int chunkZ) { - return Math.abs(chunkX - generationCenter.x()) <= WRITE_RADIUS - && Math.abs(chunkZ - generationCenter.z()) <= WRITE_RADIUS; - } - - @Override - public boolean isOutsideBuildHeight(BlockPos position) { - return position.getY() < getMinY() || position.getY() >= getMaxY(); - } - - private BlockState terrainState(BlockPos position) { - if (isOutsideBuildHeight(position)) { - return Blocks.VOID_AIR.defaultBlockState(); - } - long heights = heights(position.getX(), position.getZ()); - int surface = (int) (heights >> 32); - int floor = (int) heights; - if (position.getY() < floor) { - return Blocks.STONE.defaultBlockState(); - } - if (position.getY() < surface) { - return Blocks.WATER.defaultBlockState(); - } - return Blocks.AIR.defaultBlockState(); - } - - private int height(Heightmap.Types type, int x, int z) { - long heights = heights(x, z); - if (type == Heightmap.Types.OCEAN_FLOOR || type == Heightmap.Types.OCEAN_FLOOR_WG) { - return (int) heights; - } - return (int) (heights >> 32); - } - - private long heights(int x, int z) { - long key = ((long) x << 32) ^ (z & 0xffffffffL); - long cached = terrainHeights.get(key); - if (cached != Long.MIN_VALUE) { - return cached; - } - int floor = Mth.clamp(floorFirstFreeY.applyAsInt(x, z), getMinY(), getMaxY()); - int surface = Mth.clamp(surfaceFirstFreeY.applyAsInt(x, z), floor, getMaxY()); - long heights = ((long) surface << 32) | (floor & 0xffffffffL); - terrainHeights.put(key, heights); - return heights; - } - - private ChunkAccess outsideChunk(int chunkX, int chunkZ) { - long key = ChunkPos.pack(chunkX, chunkZ); - ChunkAccess cached = outsideChunks.get(key); - if (cached != null) { - return cached; - } - EmptyLevelChunk chunk = new EmptyLevelChunk(getLevel(), new ChunkPos(chunkX, chunkZ), fallbackBiome); - WorldgenTerrainHeightmaps.primeTerrain(chunk, surfaceFirstFreeY, floorFirstFreeY); - outsideChunks.put(key, chunk); - return chunk; - } - - private record Boundary(ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - } - - private static final class BoundedTickAccess implements LevelTickAccess { - private final LevelTickAccess delegate; - private final Predicate writable; - - private BoundedTickAccess(LevelTickAccess delegate, Predicate writable) { - this.delegate = delegate; - this.writable = writable; - } - - @Override - public void schedule(ScheduledTick tick) { - if (writable.test(tick.pos())) { - delegate.schedule(tick); - } - } - - @Override - public boolean hasScheduledTick(BlockPos position, T type) { - return writable.test(position) && delegate.hasScheduledTick(position, type); - } - - @Override - public int count() { - return delegate.count(); - } - - @Override - public boolean willTickThisTick(BlockPos position, T type) { - return writable.test(position) && delegate.willTickThisTick(position, type); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationHooks.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationHooks.java deleted file mode 100644 index c6bba6b20..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationHooks.java +++ /dev/null @@ -1,61 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.platform.agent.Agent; -import it.unimi.dsi.fastutil.shorts.ShortList; -import net.bytebuddy.ByteBuddy; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.matcher.ElementMatchers; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; -import java.util.function.LongPredicate; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.status.ChunkStatus; - -import java.util.List; - -final class NmsGenerationHooks { - private NmsGenerationHooks() { - } - - static void install() { - ByteBuddy buddy = new ByteBuddy(); - for (Class clazz : List.of(ChunkAccess.class, ProtoChunk.class)) { - buddy.redefine(clazz) - .visit(Advice.to(ChunkAccessAdvice.class).on(ElementMatchers.isMethod().and(ElementMatchers.takesArguments(ShortList.class, int.class)))) - .make() - .load(clazz.getClassLoader(), Agent.installed()); - } - buddy.redefine(WorldGenRegion.class) - .visit(Advice.to(WorldGenerationWriteAdvice.class).on(ElementMatchers.named("ensureCanWrite") - .and(ElementMatchers.takesArguments(BlockPos.class)))) - .make() - .load(WorldGenRegion.class.getClassLoader(), Agent.installed()); - } - - private static class WorldGenerationWriteAdvice { - @Advice.OnMethodExit - static void exit(@Advice.This WorldGenRegion region, @Advice.Argument(0) BlockPos position, - @Advice.Return(readOnly = false) boolean allowed) { - if (!allowed) { - return; - } - ChunkGenerator generator = region.getLevel().getChunkSource().getGenerator(); - if (generator instanceof LongPredicate predicate - && generator.getClass().getName().equals("art.arcane.iris.platform.bukkit.nms.v26_3_R1.IrisChunkGenerator") - && region.getChunk(position.getX() >> 4, position.getZ() >> 4) - .getPersistedStatus().isOrAfter(ChunkStatus.FULL)) { - allowed = predicate.test(ChunkPos.pack(position.getX() >> 4, position.getZ() >> 4)); - } - } - } - - private static class ChunkAccessAdvice { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) - static boolean enter(@Advice.This ChunkAccess access, @Advice.Argument(1) int index) { - return index >= access.getPostProcessing().length; - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationRegistry.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationRegistry.java deleted file mode 100644 index 4d5143de0..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsGenerationRegistry.java +++ /dev/null @@ -1,182 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.spi.PlatformGenerationRegistry; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.dimension.DimensionType; - -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.function.Supplier; - -final class NmsGenerationRegistry implements PlatformGenerationRegistry { - private static final String BIOME_REGISTRY = "minecraft:worldgen/biome"; - private static final String STRUCTURE_REGISTRY = "minecraft:worldgen/structure"; - private static final String DIMENSION_TYPE_REGISTRY = "minecraft:dimension_type"; - private static final String BLOCK_REGISTRY = "minecraft:block"; - private static final String ENTITY_TYPE_REGISTRY = "minecraft:entity_type"; - - private final Supplier registryAccess; - private final String runtimeIdentity; - private final String rendererIdentity; - - NmsGenerationRegistry( - Supplier registryAccess, - String runtimeIdentity, - String rendererIdentity - ) { - this.registryAccess = Objects.requireNonNull(registryAccess, "registryAccess"); - this.runtimeIdentity = requireText(runtimeIdentity, "runtimeIdentity"); - this.rendererIdentity = requireText(rendererIdentity, "rendererIdentity"); - } - - @Override - public String runtimeIdentity() { - return runtimeIdentity; - } - - @Override - public String generatedDefinitionRendererIdentity() { - return rendererIdentity; - } - - @Override - public String customBiomeResourceKey(String identitySha256) { - return PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey(identitySha256); - } - - @Override - public List legacyCustomBiomeResourceKeys( - String packName, - String dimensionKey, - String customBiomeId - ) { - return List.of(requireText(dimensionKey, "dimensionKey").toLowerCase(Locale.ROOT) - + ":" + requireText(customBiomeId, "customBiomeId").toLowerCase(Locale.ROOT)); - } - - @Override - public String dimensionTypeResourceKey(String packName, String dimensionKey, String dimensionTypeKey) { - return "iris:" + requireText(dimensionTypeKey, "dimensionTypeKey").toLowerCase(Locale.ROOT); - } - - @Override - public Definition definition(String registryKey, String resourceKey) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> presence(access, Registries.BIOME, registryKey, resourceKey); - case STRUCTURE_REGISTRY -> presence(access, Registries.STRUCTURE, registryKey, resourceKey); - case DIMENSION_TYPE_REGISTRY -> presence( - access, - Registries.DIMENSION_TYPE, - registryKey, - resourceKey - ); - case BLOCK_REGISTRY -> presence(access, Registries.BLOCK, registryKey, resourceKey); - case ENTITY_TYPE_REGISTRY -> presence(access, Registries.ENTITY_TYPE, registryKey, resourceKey); - default -> throw new IllegalArgumentException("Unsupported generation registry: " + registryKey); - }; - } - - @Override - public Definition canonicalDefinition(String registryKey, String resourceKey, String sourceJson) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> canonicalize(access, Biome.DIRECT_CODEC, sourceJson); - case DIMENSION_TYPE_REGISTRY -> canonicalize(access, DimensionType.DIRECT_CODEC, sourceJson); - default -> throw new IllegalArgumentException( - "Registry does not accept generated JSON definitions: " + registryKey - ); - }; - } - - @Override - public Definition generatedDefinition(String registryKey, String resourceKey) { - RegistryAccess access = Objects.requireNonNull(registryAccess.get(), "Minecraft registry access"); - return switch (registryKey) { - case BIOME_REGISTRY -> exactRegistered( - access, - Registries.BIOME, - Biome.DIRECT_CODEC, - resourceKey - ); - case DIMENSION_TYPE_REGISTRY -> exactRegistered( - access, - Registries.DIMENSION_TYPE, - DimensionType.DIRECT_CODEC, - resourceKey - ); - default -> throw new IllegalArgumentException( - "Registry does not contain Iris-generated definitions: " + registryKey - ); - }; - } - - private static Definition presence( - RegistryAccess access, - ResourceKey> registryKey, - String registryResourceKey, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing registry definition " + registryKey.identifier() - + " / " + resourceKey + "."); - } - return Definition.resourceIdentity(registryResourceKey, resourceKey); - } - - private static Definition canonicalize(RegistryAccess access, Codec codec, String sourceJson) { - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement source = JsonParser.parseString(Objects.requireNonNull(sourceJson, "sourceJson")); - T decoded = codec.parse(ops, source).getOrThrow(IllegalArgumentException::new); - JsonElement encoded = codec.encodeStart(ops, decoded).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Definition exactRegistered( - RegistryAccess access, - ResourceKey> registryKey, - Codec codec, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing generated registry definition " - + registryKey.identifier() + " / " + resourceKey + "."); - } - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement encoded = codec.encodeStart(ops, value).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Identifier requireIdentifier(String resourceKey) { - Identifier identifier = Identifier.tryParse(resourceKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registry resource key: " + resourceKey); - } - return identifier; - } - - private static String requireText(String value, String label) { - String required = Objects.requireNonNull(value, label).trim(); - if (required.isEmpty()) { - throw new IllegalArgumentException(label + " must not be blank."); - } - return required; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCapture.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCapture.java deleted file mode 100644 index 58efebfc4..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCapture.java +++ /dev/null @@ -1,266 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.world.history.NativeTerrainReceipt; -import art.arcane.iris.world.history.SavedTerrainChunk; -import art.arcane.iris.world.task.J; -import ca.spottedleaf.concurrentutil.completable.Completable; -import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.common.PlatformHooks; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; - -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.LockSupport; - -final class NmsSavedTerrainCapture { - private static final long WRITE_TIMEOUT_NANOS = TimeUnit.SECONDS.toNanos(30L); - private static final int SNAPSHOT_BATCH_SIZE = 64; - - private NmsSavedTerrainCapture() { - } - - static CompletableFuture capture(World world, int chunkX, int chunkZ, int minimumY, int height) { - ServerLevel level = ((CraftWorld) world).getHandle(); - CompletableFuture snapshot = new CompletableFuture<>(); - J.runRegionFuture(world, chunkX, chunkZ, () -> { - try { - NewChunkHolder holder = level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolder(chunkX, chunkZ); - ChunkAccess chunk = holder == null ? null : holder.getCurrentChunk(); - if (chunk == null) { - snapshot.complete(new CapturedChunk(new ChunkPos(chunkX, chunkZ), CompletableFuture.completedFuture(null))); - return; - } - String status = BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getPersistedStatus()).toString(); - if (!SavedTerrainChunk.hasTerrain(status)) { - throw new IOException("Native chunk " + chunkX + "," + chunkZ + " has no completed terrain at " + status); - } - snapshot.complete(scheduleSnapshot(level, chunk)); - } catch (Throwable failure) { - snapshot.completeExceptionally(failure); - } - }).whenComplete((ignored, failure) -> { - if (failure != null) { - snapshot.completeExceptionally(failure); - } - }); - return snapshot.thenApplyAsync(captured -> { - try { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - CompoundTag data = awaitSnapshot(level, captured, deadline); - Checkpoint checkpoint = checkpoint(chunkX, chunkZ, data); - awaitWrite(level, checkpoint.chunk(), deadline); - if (data == null) { - return SavedTerrainChunk.read(world.getWorldFolder().toPath(), chunkX, chunkZ, minimumY, height); - } - verifyCheckpoint(world.getWorldFolder().toPath(), checkpoint); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (DataOutputStream output = new DataOutputStream(bytes)) { - NbtIo.write(data, output); - } - return SavedTerrainChunk.readNbt(bytes.toByteArray(), chunkX, chunkZ, minimumY, height); - } catch (IOException failure) { - throw new CompletionException(failure); - } - }); - } - - static CompletableFuture flush(World world) { - ServerLevel level = ((CraftWorld) world).getHandle(); - List holders = level.moonrise$getChunkTaskScheduler().chunkHolderManager.getChunkHolders(); - return CompletableFuture.runAsync(() -> { - try { - flushSnapshots(world, level, holders); - } catch (IOException failure) { - throw new CompletionException(failure); - } - }); - } - - static void flushSnapshots(World world, ServerLevel level, List holders) throws IOException { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - List checkpoints = new ArrayList<>(holders.size()); - for (int offset = 0; offset < holders.size(); offset += SNAPSHOT_BATCH_SIZE) { - int end = Math.min(holders.size(), offset + SNAPSHOT_BATCH_SIZE); - List> snapshots = requestSnapshots(world, level, holders.subList(offset, end)); - awaitSnapshots(snapshots, deadline); - for (CompletableFuture future : snapshots) { - CapturedChunk captured = future.join(); - CompoundTag data = awaitSnapshot(level, captured, deadline); - checkpoints.add(checkpoint(captured.chunk().x(), captured.chunk().z(), data)); - } - for (int index = offset; index < end; index++) { - awaitWrite(level, checkpoints.get(index).chunk(), deadline); - } - } - finishCheckpoint(level, world.getWorldFolder().toPath(), checkpoints); - } - - private static List> requestSnapshots( - World world, ServerLevel level, List holders) { - List> snapshots = new ArrayList<>(holders.size()); - for (NewChunkHolder original : holders) { - ChunkPos position = new ChunkPos(original.chunkX, original.chunkZ); - CompletableFuture snapshot = new CompletableFuture<>(); - snapshots.add(snapshot); - J.runRegionFuture(world, position.x(), position.z(), () -> { - try { - NewChunkHolder holder = level.moonrise$getChunkTaskScheduler().chunkHolderManager - .getChunkHolder(position.x(), position.z()); - ChunkAccess chunk = holder == null ? null : holder.getCurrentChunk(); - snapshot.complete(chunk != null && chunk.isUnsaved() ? scheduleSnapshot(level, chunk) - : new CapturedChunk(position, CompletableFuture.completedFuture(null))); - } catch (Throwable failure) { - snapshot.completeExceptionally(failure); - } - }).whenComplete((ignored, failure) -> { - if (failure != null) { - snapshot.completeExceptionally(failure); - } - }); - } - return snapshots; - } - - private static void awaitSnapshots(List> snapshots, long deadlineNanos) - throws IOException { - try { - CompletableFuture.allOf(snapshots.toArray(CompletableFuture[]::new)) - .get(Math.max(0L, deadlineNanos - System.nanoTime()), TimeUnit.NANOSECONDS); - } catch (InterruptedException failure) { - Thread.currentThread().interrupt(); - throw new IOException("Interrupted while copying terrain checkpoint chunks.", failure); - } catch (ExecutionException | TimeoutException failure) { - throw new IOException("Unable to copy terrain checkpoint chunks.", failure); - } - } - - static void finishCheckpoint(ServerLevel level, Path worldFolder, List checkpoints) throws IOException { - long deadline = System.nanoTime() + WRITE_TIMEOUT_NANOS; - for (Checkpoint checkpoint : checkpoints) { - awaitWrite(level, checkpoint.chunk(), deadline); - } - MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA); - for (Checkpoint checkpoint : checkpoints) { - verifyCheckpoint(worldFolder, checkpoint); - } - } - - static Checkpoint checkpoint(int chunkX, int chunkZ, CompoundTag data) { - if (data == null) { - return new Checkpoint(new ChunkPos(chunkX, chunkZ), null); - } - byte[] receipt = data.getCompound("ChunkBukkitValues") - .flatMap(values -> values.getByteArray(NativeTerrainReceipt.NBT_KEY)).orElse(null); - long structureActivation = data.getCompound("ChunkBukkitValues").map(values -> values.getLongOr( - NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY, 0)).orElse(0L); - return new Checkpoint(new ChunkPos(chunkX, chunkZ), new Verification( - data.getStringOr("Status", ""), receipt, structureActivation)); - } - - private static void verifyCheckpoint(Path worldFolder, Checkpoint checkpoint) throws IOException { - Verification expected = checkpoint.verification(); - if (expected == null) { - return; - } - SavedTerrainChunk.verifyCheckpoint(worldFolder, checkpoint.chunk().x(), checkpoint.chunk().z(), - expected.status(), expected.receipt(), expected.structureActivation()); - } - - static void awaitWrite(ServerLevel level, ChunkPos chunk, long deadlineNanos) throws IOException { - ChunkTaskScheduler scheduler = level.moonrise$getChunkTaskScheduler(); - while (true) { - checkWriteWait(chunk, deadlineNanos); - if (MoonriseRegionFileIO.getPriority(level, chunk.x(), chunk.z(), - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA) == Priority.COMPLETING) { - return; - } - helpSaveTasks(scheduler); - } - } - - static CapturedChunk scheduleSnapshot(ServerLevel level, ChunkAccess chunk) { - SerializableChunkData snapshot = SerializableChunkData.copyOf(level, chunk); - PlatformHooks.get().chunkSyncSave(level, chunk, snapshot); - Completable nativeData = new Completable<>(); - CompletableFuture data = new CompletableFuture<>(); - PrioritisedExecutor.PrioritisedTask task = level.moonrise$getChunkTaskScheduler().saveExecutor - .createTask(() -> serializeSnapshot(snapshot, nativeData, data)); - task.queue(); - ChunkPos position = chunk.getPos(); - MoonriseRegionFileIO.scheduleSave(level, position.x(), position.z(), nativeData, task, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA, Priority.NORMAL); - return new CapturedChunk(position, data); - } - - static CompoundTag awaitSnapshot(ServerLevel level, CapturedChunk captured, long deadlineNanos) throws IOException { - ChunkTaskScheduler scheduler = level.moonrise$getChunkTaskScheduler(); - while (!captured.data().isDone()) { - checkWriteWait(captured.chunk(), deadlineNanos); - helpSaveTasks(scheduler); - } - try { - return captured.data().join(); - } catch (CompletionException failure) { - throw new IOException("Unable to serialize terrain checkpoint chunk " + captured.chunk(), failure.getCause()); - } - } - - private static void serializeSnapshot(SerializableChunkData snapshot, Completable nativeData, - CompletableFuture data) { - try { - CompoundTag serialized = snapshot.write(); - nativeData.complete(serialized); - data.complete(serialized); - } catch (Throwable failure) { - nativeData.completeExceptionally(failure); - data.completeExceptionally(failure); - } - } - - private static void checkWriteWait(ChunkPos chunk, long deadlineNanos) throws IOException { - if (Thread.currentThread().isInterrupted()) { - throw new IOException("Interrupted while saving terrain checkpoint chunk " + chunk); - } - if (System.nanoTime() - deadlineNanos >= 0L) { - throw new IOException("Timed out saving terrain checkpoint chunk " + chunk); - } - } - - private static void helpSaveTasks(ChunkTaskScheduler scheduler) { - boolean helped = scheduler.saveExecutor.executeTask(); - helped |= scheduler.compressionExecutor.executeTask(); - if (!helped) { - LockSupport.parkNanos(1_000_000L); - } - } - - record CapturedChunk(ChunkPos chunk, CompletableFuture data) { - } - - record Checkpoint(ChunkPos chunk, Verification verification) { - } - - record Verification(String status, byte[] receipt, long structureActivation) { - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java deleted file mode 100644 index 3a37aeb87..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java +++ /dev/null @@ -1,403 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.platform.bukkit.nms.ServerShutdownBoundary; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.localization.C; -import art.arcane.iris.platform.agent.Agent; -import net.bytebuddy.agent.builder.AgentBuilder; -import net.bytebuddy.agent.builder.ResettableClassFileTransformer; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.dynamic.DynamicType; -import net.bytebuddy.matcher.ElementMatchers; -import net.bytebuddy.utility.JavaModule; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.storage.LevelStorageSource; -import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.generator.ChunkGenerator; -import org.bukkit.plugin.Plugin; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicBoolean; - -final class NmsWorldLifecycle { - private final AtomicBoolean injected = new AtomicBoolean(); - private volatile ResettableClassFileTransformer levelStorageAccessTransformer; - private volatile ResettableClassFileTransformer serverLevelTransformer; - private volatile ResettableClassFileTransformer pluginClassLoaderTransformer; - private boolean pluginClassLoaderCloseDeferred; - - public boolean injectBukkit() { - synchronized (injected) { - if (injected.get()) { - return true; - } - try { - IrisLogging.info("Injecting Bukkit"); - Agent.requireClassLoaderCloseDeferral(); - Class loaderCloseType = getClass().getClassLoader().getClass() - .getMethod("close").getDeclaringClass(); - PluginClassLoaderInjectionListener loaderListener = new PluginClassLoaderInjectionListener(); - pluginClassLoaderTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .with(AgentBuilder.RedefinitionStrategy.Listener.ErrorEscalating.FAIL_FAST) - .with(loaderListener) - .type(ElementMatchers.is(loaderCloseType)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(PluginClassLoaderCloseAdvice.class) - .on(ElementMatchers.named("close") - .and(ElementMatchers.takesArguments(0)) - .and(ElementMatchers.returns(void.class))))) - .installOn(Agent.getInstrumentation()); - loaderListener.requireInstalled(); - levelStorageAccessTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .type(ElementMatchers.is(LevelStorageSource.LevelStorageAccess.class)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(LevelStorageAccessAdvice.class).on(ElementMatchers.isConstructor() - .and(ElementMatchers.takesArguments(4)) - .and(ElementMatchers.takesArgument(0, LevelStorageSource.class)) - .and(ElementMatchers.takesArgument(1, String.class)) - .and(ElementMatchers.takesArgument(2, Path.class)) - .and(ElementMatchers.takesArgument(3, ResourceKey.class))))) - .installOn(Agent.getInstrumentation()); - serverLevelTransformer = new AgentBuilder.Default() - .disableClassFormatChanges() - .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) - .type(ElementMatchers.is(ServerLevel.class)) - .transform((builder, typeDescription, classLoader, module, protectionDomain) -> - builder.visit(Advice.to(ServerLevelAdvice.class).on(ElementMatchers.isConstructor() - .and(ElementMatchers.takesArgument(0, MinecraftServer.class)) - .and(ElementMatchers.takesArgument(5, LevelStem.class))))) - .installOn(Agent.getInstrumentation()); - NmsGenerationHooks.install(); - - injected.set(true); - return true; - } catch (Throwable e) { - IrisLogging.reportError(C.RED + "Failed to inject Bukkit", e); - ResettableClassFileTransformer partialServerLevel = serverLevelTransformer; - ResettableClassFileTransformer partialStorageAccess = levelStorageAccessTransformer; - ResettableClassFileTransformer partialPluginClassLoader = pluginClassLoaderTransformer; - serverLevelTransformer = null; - levelStorageAccessTransformer = null; - pluginClassLoaderTransformer = null; - for (ResettableClassFileTransformer partial : new ResettableClassFileTransformer[]{ - partialServerLevel, - partialStorageAccess, - partialPluginClassLoader - }) { - if (partial == null) { - continue; - } - try { - partial.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION); - } catch (Throwable cleanupFailure) { - IrisLogging.reportError("Failed to remove partial Bukkit lifecycle injection", cleanupFailure); - } - } - return false; - } - } - } - - public void ensureServerLevelInjection() { - if (!injected.get()) { - throw new IllegalStateException("Iris world lifecycle injection is unavailable. Fix the Java Agent or Code Injection startup failure and restart before creating worlds."); - } - try { - Agent.getInstrumentation().retransformClasses( - LevelStorageSource.LevelStorageAccess.class, - ServerLevel.class - ); - } catch (Throwable e) { - throw new IllegalStateException("Failed to re-apply Bukkit world lifecycle injection. World creation was stopped before loading the world.", e); - } - } - - public void uninjectBukkit() { - synchronized (injected) { - ResettableClassFileTransformer activeServerLevel = serverLevelTransformer; - ResettableClassFileTransformer activeStorageAccess = levelStorageAccessTransformer; - serverLevelTransformer = null; - levelStorageAccessTransformer = null; - injected.set(false); - for (ResettableClassFileTransformer transformer : new ResettableClassFileTransformer[]{ - activeServerLevel, - activeStorageAccess - }) { - if (transformer == null) { - continue; - } - try { - transformer.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION); - } catch (Throwable e) { - IrisLogging.reportError(C.RED + "Failed to remove Bukkit world lifecycle injection", e); - } - } - } - } - - public boolean isServerStopping() { - return ((CraftServer) Bukkit.getServer()).getServer().hasStopped(); - } - - public ServerShutdownBoundary createServerShutdownBoundary() { - MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); - return new ServerShutdownBoundary( - () -> server.hasFullyShutdown, - server.getRunningThread() - ); - } - - public void deferPluginClassLoaderClose() { - synchronized (injected) { - if (pluginClassLoaderTransformer == null || pluginClassLoaderCloseDeferred) { - return; - } - Agent.retainClassLoader(getClass().getClassLoader()); - pluginClassLoaderCloseDeferred = true; - } - } - - public void releasePluginClassLoaderClose() { - ResettableClassFileTransformer transformer; - boolean releaseLoader; - synchronized (injected) { - transformer = pluginClassLoaderTransformer; - pluginClassLoaderTransformer = null; - releaseLoader = pluginClassLoaderCloseDeferred; - pluginClassLoaderCloseDeferred = false; - } - try { - if (transformer != null - && !transformer.reset(Agent.getInstrumentation(), AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)) { - throw new IllegalStateException("Failed to remove Iris plugin class loader lifecycle injection"); - } - } finally { - if (releaseLoader) { - Agent.releaseClassLoader(getClass().getClassLoader()); - } - } - } - - private static final class PluginClassLoaderInjectionListener extends AgentBuilder.Listener.Adapter { - private volatile boolean transformed; - private volatile Throwable failure; - - @Override - public void onTransformation(TypeDescription type, ClassLoader loader, JavaModule module, - boolean loaded, DynamicType dynamicType) { - transformed = true; - } - - @Override - public void onError(String typeName, ClassLoader loader, JavaModule module, - boolean loaded, Throwable throwable) { - failure = throwable; - } - - private void requireInstalled() { - if (!transformed || failure != null) { - throw new IllegalStateException("Iris plugin class loader lifecycle injection failed", failure); - } - } - } - - private static class PluginClassLoaderCloseAdvice { - @Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class) - static boolean enter(@Advice.This ClassLoader loader) throws ReflectiveOperationException { - Class installer = Class.forName( - "art.arcane.iris.platform.agent.Installer", true, ClassLoader.getSystemClassLoader()); - return Boolean.TRUE.equals(installer.getMethod("deferClassLoaderClose", ClassLoader.class) - .invoke(null, loader)); - } - } - - private static class LevelStorageAccessAdvice { - @Advice.OnMethodEnter - static void enter( - @Advice.Argument(1) String levelId, - @Advice.Argument(value = 3, readOnly = false) ResourceKey dimensionType - ) { - if (levelId == null || levelId.isBlank()) { - return; - } - - ClassLoader pluginClassLoader; - Class generatorType; - Class stagingType; - try { - Plugin irisPlugin = Bukkit.getPluginManager().getPlugin("Iris"); - if (irisPlugin == null) { - return; - } - pluginClassLoader = irisPlugin.getClass().getClassLoader(); - generatorType = Class.forName("art.arcane.iris.platform.generation.PlatformChunkGenerator", true, pluginClassLoader); - stagingType = Class.forName("art.arcane.iris.world.lifecycle.WorldLifecycleStaging", true, pluginClassLoader); - } catch (Throwable ignored) { - return; - } - - Object generator; - try { - generator = stagingType - .getDeclaredMethod("peekStemGenerator", String.class) - .invoke(null, levelId); - } catch (Throwable e) { - throw new RuntimeException("Iris failed to inspect the staged world generator", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - if (generator == null || !generatorType.isInstance(generator)) { - return; - } - - try { - Object target = generatorType.getMethod("getTarget").invoke(generator); - if (target == null) { - throw new IllegalStateException("Iris generator has no engine target."); - } - Object world = target.getClass().getMethod("getWorld").invoke(target); - if (world == null) { - throw new IllegalStateException("Iris generator target has no world identity."); - } - Object rawIdentity = world.getClass().getMethod("identity").invoke(world); - String worldIdentity = rawIdentity == null ? "" : rawIdentity.toString().trim(); - Identifier worldIdentifier = Identifier.parse(worldIdentity); - if (!"iris".equals(worldIdentifier.getNamespace()) - && !"minecraft".equals(worldIdentifier.getNamespace())) { - throw new IllegalStateException( - "Iris generator has an unmanaged world identity: " + worldIdentity); - } - dimensionType = ResourceKey.create(Registries.LEVEL_STEM, worldIdentifier); - } catch (Throwable e) { - throw new RuntimeException("Iris failed to bind the staged world storage identity", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - } - } - - private static class ServerLevelAdvice { - @Advice.OnMethodEnter - static void enter( - @Advice.Argument(0) MinecraftServer server, - @Advice.Argument(value = 4, readOnly = false) ResourceKey dimensionKey, - @Advice.Argument(value = 5, readOnly = false) LevelStem levelStem, - @Advice.AllArguments Object[] constructorArguments - ) { - if (dimensionKey == null) - return; - - // This advice is inlined into every ServerLevel construction on the server. Until a - // world is proven Iris-owned, every failure must fail OPEN (keep the vanilla stem): - // Iris being unloaded or half-loaded must never break other plugins' world creation. - String levelId; - ClassLoader pluginClassLoader; - Class generatorType; - Class stagingType; - try { - levelId = dimensionKey.identifier().getPath(); - if (levelId == null || levelId.isBlank()) { - return; - } - - Plugin irisPlugin = Bukkit.getPluginManager().getPlugin("Iris"); - if (irisPlugin == null) { - return; - } - pluginClassLoader = irisPlugin.getClass().getClassLoader(); - generatorType = Class.forName("art.arcane.iris.platform.generation.PlatformChunkGenerator", true, pluginClassLoader); - stagingType = Class.forName("art.arcane.iris.world.lifecycle.WorldLifecycleStaging", true, pluginClassLoader); - } catch (Throwable ignored) { - // Iris absent or half-loaded: fail OPEN for a world we cannot prove ours. - return; - } - - // From here Iris is present and its classes resolve; a failure resolving the - // staged generator must fail LOUD — silently handing a possibly-staged Iris world - // the vanilla stem corrupts generation. - ChunkGenerator gen = null; - try { - ChunkGenerator constructorGenerator = null; - for (Object argument : constructorArguments) { - if (argument instanceof ChunkGenerator candidate) { - constructorGenerator = candidate; - break; - } - } - Object generator = generatorType.isInstance(constructorGenerator) ? constructorGenerator : null; - if (generator == null) { - generator = stagingType - .getDeclaredMethod("consumeStemGenerator", String.class) - .invoke(null, levelId); - } - if (generator instanceof ChunkGenerator owned && owned.getClass().getPackageName().startsWith("art.arcane.iris")) { - gen = owned; - } - } catch (Throwable e) { - throw new RuntimeException("Iris failed to resolve the staged world generator", - e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - if (gen == null) { - return; - } - - // Past the ownership gate the world is Iris-owned; silently handing back the - // vanilla stem would corrupt generation, so failures from here rethrow. - try { - Object bindings = Class.forName("art.arcane.iris.platform.bukkit.nms.INMS", true, pluginClassLoader) - .getDeclaredMethod("get") - .invoke(null); - if (bindings == null) { - throw new IllegalStateException("Iris failed to resolve an INMSBinding instance."); - } - - Method stemMethod = null; - for (Method candidate : bindings.getClass().getMethods()) { - if (candidate.getName().equals("createRuntimeLevelStem") && candidate.getParameterCount() == 2) { - stemMethod = candidate; - break; - } - } - if (stemMethod == null) { - throw new IllegalStateException("Iris binding is missing createRuntimeLevelStem."); - } - Object target = generatorType.getMethod("getTarget").invoke(gen); - if (target == null) { - throw new IllegalStateException("Iris generator has no engine target."); - } - Object world = target.getClass().getMethod("getWorld").invoke(target); - if (world == null) { - throw new IllegalStateException("Iris generator target has no world identity."); - } - Object rawIdentity = world.getClass().getMethod("identity").invoke(world); - String worldIdentity = rawIdentity == null ? "" : rawIdentity.toString().trim(); - Identifier worldIdentifier = Identifier.parse(worldIdentity); - if (!"iris".equals(worldIdentifier.getNamespace()) - && !"minecraft".equals(worldIdentifier.getNamespace())) { - throw new IllegalStateException( - "Iris generator has an unmanaged world identity: " + worldIdentity); - } - Object resolvedStem = stemMethod.invoke(bindings, server.registryAccess(), gen); - if (!(resolvedStem instanceof LevelStem runtimeStem)) { - throw new IllegalStateException("Iris runtime LevelStem binding returned " + (resolvedStem == null ? "null" : resolvedStem.getClass().getName()) + "."); - } - dimensionKey = ResourceKey.create(Registries.DIMENSION, worldIdentifier); - levelStem = runtimeStem; - } catch (Throwable e) { - throw new RuntimeException("Iris failed to replace the levelStem", e instanceof InvocationTargetException ex ? ex.getCause() : e); - } - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/VanillaStructureBiomes.java b/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/VanillaStructureBiomes.java deleted file mode 100644 index c281bc0af..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/VanillaStructureBiomes.java +++ /dev/null @@ -1,80 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureGenerationKeys; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.levelgen.structure.Structure; - -import java.util.LinkedHashSet; -import java.util.Optional; -import java.util.Set; - -final class VanillaStructureBiomes { - private VanillaStructureBiomes() { - } - - static Set possibleBiomeKeys(BiomeSource source) { - if (source == null) { - throw new IllegalStateException("Minecraft chunk generator has no biome source"); - } - Set keys = new LinkedHashSet<>(); - Set> possibleBiomes = source instanceof CustomBiomeSource customBiomeSource - ? customBiomeSource.possibleStructureBiomes() - : source.possibleBiomes(); - for (Holder holder : possibleBiomes) { - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - if (keys.isEmpty()) { - throw new IllegalStateException("Minecraft biome source exposes no registered possible biomes"); - } - return keys; - } - - static Set structureBiomeKeys(RegistryAccess access, String structureKey) { - if (access == null) { - throw new IllegalStateException("Minecraft registry access is unavailable"); - } - if (structureKey == null || structureKey.isBlank()) { - throw new IllegalArgumentException("Registered structure key must not be blank"); - } - Set keys = new LinkedHashSet<>(); - Registry registry = access.lookupOrThrow(Registries.STRUCTURE); - Structure structure = registry.getValue(Identifier.parse(structureKey)); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - boolean hasFilterEntries = false; - for (Holder holder : structure.biomes()) { - hasFilterEntries = true; - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - // An empty biome filter is legal datapack content (opt-in structures whose tags only - // reference absent modded biomes); the structure is unreachable, not an error state. - if (keys.isEmpty() && hasFilterEntries) { - throw new IllegalStateException("Registered structure '" + structureKey - + "' has biome filter entries but none resolve to registered biome keys"); - } - return keys; - } - - static Set reachableStructureKeys(ServerLevel level, BiomeSource source) { - if (level == null) { - throw new IllegalStateException("Minecraft server level is unavailable"); - } - Set possible = possibleBiomeKeys(source); - return NativeStructureGenerationKeys.reachable(level, possible); - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java index 9730421ae..09a9198e3 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/IrisChunkGeneratorHeightCacheTest.java @@ -1,6 +1,9 @@ package art.arcane.iris.nativegen.v26_3_R1; -import art.arcane.iris.platform.bukkit.nms.v26_3_R1.IrisChunkGenerator; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeChunkGenerator; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainColumns; +import art.arcane.iris.platform.bukkit.nms.BukkitTerrainColumnPolicy; + import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.GenerationSessionLease; @@ -101,14 +104,13 @@ public final class IrisChunkGeneratorHeightCacheTest { when(engine.getCacheID()).thenReturn(1); when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop()); when(complex.resolvedTerrainColumn(anyInt(), anyInt())).thenReturn(Optional.of(signature())); - IrisChunkGenerator generator = mock(IrisChunkGenerator.class, CALLS_REAL_METHODS); - set(generator, "engine", engine); - set(generator, "terrainHeights", new NativeTerrainHeightCache()); + NativeChunkGenerator generator = mock(NativeChunkGenerator.class, CALLS_REAL_METHODS); + set(generator, "terrainColumns", new NativeTerrainColumns(new BukkitTerrainColumnPolicy(engine, null))); return new Fixture(generator, engine, complex); } - private static void set(IrisChunkGenerator generator, String name, Object value) throws Exception { - Field field = IrisChunkGenerator.class.getDeclaredField(name); + private static void set(NativeChunkGenerator generator, String name, Object value) throws Exception { + Field field = NativeChunkGenerator.class.getDeclaredField(name); field.setAccessible(true); field.set(generator, value); } @@ -126,6 +128,6 @@ public final class IrisChunkGeneratorHeightCacheTest { new TerrainBoundarySignature.BiomeEncoding(List.of("minecraft:plains"), new short[]{0})), geometry); } - private record Fixture(IrisChunkGenerator generator, Engine engine, IrisComplex complex) { + private record Fixture(NativeChunkGenerator generator, Engine engine, IrisComplex complex) { } } diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java index 597c28eb8..8f6a12e0d 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeGenerationWriteGuardTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.iris.structure.nativegen.GenerationWritePolicy; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeGenerationWriteGuard; + import art.arcane.iris.generation.runtime.DimensionStackContext; import art.arcane.iris.generation.runtime.DimensionStackLayout; import art.arcane.iris.generation.runtime.IrisComplex; @@ -45,11 +49,11 @@ public final class NativeGenerationWriteGuardTest { DimensionStackContext stack = mock(DimensionStackContext.class); when(objects.contains(-3, 74, -7)).thenReturn(true); - Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(objects, stack, -64); + Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, stack, -64)); assertTrue(protectedPositions.test(new BlockPos(-3, 10, -7))); verifyNoInteractions(stack); - assertFalse(NativeGenerationWriteGuard.protectedPositions(objects, null, -64) + assertFalse(NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, null, -64)) .test(new BlockPos(-3, 11, -7))); } @@ -65,7 +69,7 @@ public final class NativeGenerationWriteGuardTest { when(negative.isHostFeatureProtectedY(64)).thenReturn(true); when(positive.isHostFeatureProtectedY(65)).thenReturn(true); - Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(objects, stack, -64); + Predicate protectedPositions = NativeGenerationWriteGuard.protectedPositions(GenerationWritePolicy.protectedPositions(objects, stack, -64)); assertTrue(protectedPositions.test(new BlockPos(-1, 0, -1))); assertFalse(protectedPositions.test(new BlockPos(-1, 1, -1))); @@ -85,10 +89,10 @@ public final class NativeGenerationWriteGuardTest { when(router.history()).thenReturn(history); when(history.activeActivation()).thenReturn(active); when(active.activationId()).thenReturn(4L); - assertTrue(NativeGenerationWriteGuard.isHistoricalStructure(engine, 3)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 0)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 4)); - assertFalse(NativeGenerationWriteGuard.isHistoricalStructure(engine, 5)); + assertTrue(GenerationWritePolicy.isHistoricalStructure(engine, 3)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 0)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 4)); + assertFalse(GenerationWritePolicy.isHistoricalStructure(engine, 5)); } @Test @@ -100,11 +104,11 @@ public final class NativeGenerationWriteGuardTest { ChunkAccess partial = mock(ChunkAccess.class); when(partial.getPersistedStatus()).thenReturn(ChunkStatus.TERRAIN); when(region.getChunk(anyInt(), anyInt())).thenReturn(partial); - assertTrue(NativeGenerationWriteGuard.allowsDecoration(engine, region, new ChunkPos(0, 0))); + assertTrue(NativeGenerationWriteGuard.allowsDecoration(engine.getComplex()::allowsMantleChunkWrite, region, new ChunkPos(0, 0))); ChunkAccess complete = mock(ChunkAccess.class); when(complete.getPersistedStatus()).thenReturn(ChunkStatus.FULL); when(region.getChunk(1, -1)).thenReturn(complete); - assertFalse(NativeGenerationWriteGuard.allowsDecoration(engine, region, new ChunkPos(0, 0))); + assertFalse(NativeGenerationWriteGuard.allowsDecoration(engine.getComplex()::allowsMantleChunkWrite, region, new ChunkPos(0, 0))); } @Test @@ -115,11 +119,11 @@ public final class NativeGenerationWriteGuardTest { ChunkAccess chunk = mock(ChunkAccess.class); when(chunk.getPos()).thenReturn(new ChunkPos(-2, 3)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.TERRAIN); - assertTrue(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.FEATURES)); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.TERRAIN)); + assertTrue(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.FEATURES)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.TERRAIN)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.FEATURES); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.FEATURES)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.FEATURES)); when(chunk.getPersistedStatus()).thenReturn(ChunkStatus.FULL); - assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine, chunk, ChunkStatus.SPAWN)); + assertFalse(NativeGenerationWriteGuard.allowsPendingStage(engine.getComplex()::allowsMantleChunkWrite, chunk, ChunkStatus.SPAWN)); } } diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java index 99aadec7f..c1d8a8e30 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureFactoryTest.java @@ -1,5 +1,19 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureStartInjector; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureFactory; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeJigsawMetadata; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.ForcedStructureChunkGenerator; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTemplatePoolBounds; + import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; import java.util.Set; import art.arcane.iris.structure.jigsaw.IrisJigsawConfiguration; @@ -110,10 +124,10 @@ public class NativeStructureFactoryTest { distance.putInt("vertical", 64); axisSpecific.put("max_distance_from_center", distance); - assertEquals(80, NativeStructureFactory.sourceDistance(scalar, "horizontal")); - assertEquals(80, NativeStructureFactory.sourceDistance(scalar, "vertical")); - assertEquals(96, NativeStructureFactory.sourceDistance(axisSpecific, "horizontal")); - assertEquals(64, NativeStructureFactory.sourceDistance(axisSpecific, "vertical")); + assertEquals(80, NativeJigsawMetadata.sourceDistance(scalar, "horizontal")); + assertEquals(80, NativeJigsawMetadata.sourceDistance(scalar, "vertical")); + assertEquals(96, NativeJigsawMetadata.sourceDistance(axisSpecific, "horizontal")); + assertEquals(64, NativeJigsawMetadata.sourceDistance(axisSpecific, "vertical")); } @Test @@ -123,8 +137,8 @@ public class NativeStructureFactoryTest { .terrainAdapation(TerrainAdjustment.BEARD_BOX) .build()); - assertEquals(0, NativeStructureFactory.horizontalReferenceExpansion(none)); - assertEquals(12, NativeStructureFactory.horizontalReferenceExpansion(beardBox)); + assertEquals(0, NativeJigsawMetadata.horizontalReferenceExpansion(none)); + assertEquals(12, NativeJigsawMetadata.horizontalReferenceExpansion(beardBox)); } @Test diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java index e4bc15a75..6820648de 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureGenerationKeysContractTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureGenerationKeys; + import net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement; import net.minecraft.SharedConstants; import net.minecraft.core.Vec3i; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java index 5e1444a83..2a1cc2282 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureLocatePersistenceTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureLocatePersistence; + import net.minecraft.SharedConstants; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.Bootstrap; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java index ae2be95af..d47ea0bd0 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprintTest.java @@ -1,11 +1,19 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceRepair; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureOwnershipFingerprint; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; import art.arcane.iris.structure.nativegen.IrisNativeStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -65,7 +73,7 @@ public class NativeStructureOwnershipFingerprintTest { generated, 0, 80, -64, 320); int alignedLocatorY = NativeStructureOwnershipFingerprint.locatorY(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( generated, @@ -85,11 +93,9 @@ public class NativeStructureOwnershipFingerprintTest { StructureStart reloaded = new StructureStart(structure, origin, 0, regeneratedPieces); String afterReload = NativeStructureOwnershipFingerprint.fingerprint( "minecraft:monument", reloaded); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - wrapped, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY()), - NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", wrapped, NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY())); assertEquals(beforeReload, afterReload); assertTrue(NativeStructureOwnershipFingerprint.matches(ownership, reloaded)); @@ -110,11 +116,9 @@ public class NativeStructureOwnershipFingerprintTest { for (StructurePiece piece : moved.getPieces()) { piece.move(1, 0, 0); } - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - expected, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(expected).minY()), - expected.getBoundingBox()); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", expected, expected.getBoundingBox()), plan(origin, NativeStructureReferenceEnvelope.contentBounds(expected).minY())); assertFalse(NativeStructureOwnershipFingerprint.matches(ownership, moved)); } @@ -129,18 +133,16 @@ public class NativeStructureOwnershipFingerprintTest { NativeStructureVerticalPlacer.alignOceanMonumentToSeaLevel( generated, 0, 80, -64, 320); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( generated, structure, 0, terrain); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - wrapped, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY()), - NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", wrapped, NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY())); PiecesContainer regeneratedPieces = OceanMonumentStructure.regeneratePiecesAfterLoad( origin, seed, new PiecesContainer(wrapped.getPieces())); StructureStart reloaded = new StructureStart(structure, origin, 0, regeneratedPieces); @@ -263,13 +265,10 @@ public class NativeStructureOwnershipFingerprintTest { private static NativeStructureOwnershipRecord ownership( ChunkPos origin, StructureStart start, OceanMonumentStructure structure) { - return NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - start, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY()), - NativeStructureReferenceEnvelope.referenceBounds( - start, structure, new IrisStructureTerrain()) - ); + return NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", start, NativeStructureReferenceEnvelope.referenceBounds( + start, structure, new IrisStructureTerrain())), plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY())); } private static StructureStart poolStart( diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java index b692ef480..6bac40f6b 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecoveryTest.java @@ -1,12 +1,22 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.terrain.structure.StructureOwnershipRecordView; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureOwnershipRecovery; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureOwnershipFingerprint; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; import art.arcane.iris.structure.placement.StructurePlacementGrid; import art.arcane.iris.structure.nativegen.IrisNativeStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; import net.minecraft.core.Direction; @@ -63,7 +73,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, reloaded, - plan, expected, false); + plan, expected, false, NativeStructureOwnershipRecord::capture); assertNotNull(recovered); assertEquals(StructurePlacementGrid.placementIdentity(plan.placement()), @@ -71,7 +81,7 @@ public class NativeStructureOwnershipRecoveryTest { assertEquals(expectedLocatorY, recovered.locatorY()); assertNotEquals(NativeStructureOwnershipFingerprint.locatorY(reloaded), recovered.locatorY()); - assertEquals(IrisStructureTerrainMode.FORCE_CARVE, + assertEquals(StructureTerrainMode.FORCE_CARVE, recovered.restoredDecision().terrain().resolvedMode()); assertEquals(24, recovered.restoredDecision().terrain().getHorizontalPadding()); assertTrue(recovered.referenceMinChunkX() @@ -89,7 +99,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, natural, - plan, natural, true); + plan, natural, true, NativeStructureOwnershipRecord::capture); assertNull(recovered); } @@ -114,10 +124,10 @@ public class NativeStructureOwnershipRecoveryTest { StructurePlacementGrid.placementIdentity(changedIdentity.placement())); assertNull(NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, expected, - changedIdentity, expected, false)); + changedIdentity, expected, false, NativeStructureOwnershipRecord::capture)); assertNull(NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, changedGeometry, - plan(origin, "geometry-check", 0), expected, false)); + plan(origin, "geometry-check", 0), expected, false, NativeStructureOwnershipRecord::capture)); } @Test @@ -129,7 +139,7 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureOwnershipRecord recovered = NativeStructureOwnershipRecovery.proveCandidate( "minecraft:monument", structure, expected, - plan(origin, "fingerprint-check", 16), expected, false); + plan(origin, "fingerprint-check", 16), expected, false, NativeStructureOwnershipRecord::capture); assertNotNull(recovered); StructureStart moved = monumentStart(structure, origin, seed); @@ -148,10 +158,11 @@ public class NativeStructureOwnershipRecoveryTest { OceanMonumentStructure structure = structure(); StructureStart start = monumentStart(structure, origin, seed); NativeStructureStartPlan plan = plan( - origin, "vacuum-envelope-refresh", IrisStructureTerrainMode.VACUUM, 0); + origin, "vacuum-envelope-refresh", StructureTerrainMode.VACUUM, 0); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(start); - NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, content); + NativeStructureOwnershipRecord stale = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, content), plan); NativeStructureOwnershipRecord refreshed = NativeStructureOwnershipRecovery.refreshReferenceEnvelope( @@ -166,7 +177,7 @@ public class NativeStructureOwnershipRecoveryTest { assertEquals(stale.locatorY(), refreshed.locatorY()); assertEquals(stale.contentFingerprint(), refreshed.contentFingerprint()); assertEquals(stale.decision(), refreshed.decision()); - assertEquals(IrisStructureTerrainMode.VACUUM, + assertEquals(StructureTerrainMode.VACUUM, refreshed.restoredDecision().terrain().resolvedMode()); BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); @@ -187,10 +198,10 @@ public class NativeStructureOwnershipRecoveryTest { OceanMonumentStructure structure = structure(); StructureStart expected = monumentStart(structure, origin, seed); NativeStructureStartPlan plan = plan( - origin, "vacuum-content-check", IrisStructureTerrainMode.VACUUM, 0); - NativeStructureOwnershipRecord stale = NativeStructureOwnershipFingerprint.capture( - structureKey, expected, plan, - NativeStructureReferenceEnvelope.contentBounds(expected)); + origin, "vacuum-content-check", StructureTerrainMode.VACUUM, 0); + NativeStructureOwnershipRecord stale = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, expected, NativeStructureReferenceEnvelope.contentBounds(expected)), plan); StructureStart moved = monumentStart(structure, origin, seed); for (StructurePiece piece : moved.getPieces()) { piece.move(1, 0, 0); @@ -210,8 +221,9 @@ public class NativeStructureOwnershipRecoveryTest { NativeStructureStartPlan plan = plan(origin, "current-force-carve", 24); BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, envelope); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, envelope), plan); assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope( structureKey, structure, start, ownership)); @@ -226,19 +238,20 @@ public class NativeStructureOwnershipRecoveryTest { StructureStart start = monumentStart(structure, origin, seed); BoundingBox initial = NativeStructureReferenceEnvelope.contentBounds(start); int maximumReferenceBlockX = ((origin.x() - + NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15; + + StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15; int shiftX = maximumReferenceBlockX - initial.maxX(); for (StructurePiece piece : start.getPieces()) { piece.move(shiftX, 0, 0); } NativeStructureStartPlan plan = plan( - origin, "clipped-vacuum", IrisStructureTerrainMode.VACUUM, 0); + origin, "clipped-vacuum", StructureTerrainMode.VACUUM, 0); BoundingBox envelope = NativeStructureReferenceEnvelope.referenceBounds( start, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - structureKey, start, plan, envelope); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, envelope), plan); - assertEquals(origin.x() + NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS, + assertEquals(origin.x() + StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS, ownership.referenceMaxChunkX()); assertSame(ownership, NativeStructureOwnershipRecovery.refreshReferenceEnvelope( structureKey, structure, start, ownership)); @@ -270,12 +283,12 @@ public class NativeStructureOwnershipRecoveryTest { String placementId, int horizontalPadding) { return plan(origin, placementId, - IrisStructureTerrainMode.FORCE_CARVE, horizontalPadding); + StructureTerrainMode.FORCE_CARVE, horizontalPadding); } private static NativeStructureStartPlan plan(ChunkPos origin, String placementId, - IrisStructureTerrainMode terrainMode, + StructureTerrainMode terrainMode, int horizontalPadding) { IrisNativeStructure source = new IrisNativeStructure() .setStructure("minecraft:monument") diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java index 61059a5f1..de6f5806a 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorEncaseTest.java @@ -1,8 +1,18 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePalette; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureTerrainSettings; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTerrainIntegrator; + import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.placement.IrisStructureYBand; import art.arcane.volmlib.util.math.RNG; import net.minecraft.SharedConstants; @@ -76,7 +86,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:stronghold", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(1) .setCeilingPadding(1) .setFloorPadding(1), @@ -100,7 +110,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:stronghold", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.ENCASE), + new IrisStructureTerrain().setMode(StructureTerrainMode.ENCASE), null); assertEquals(-1, bounds.minY()); @@ -181,7 +191,7 @@ public class NativeStructurePostProcessorEncaseTest { world(new HashMap<>()), area, List.of(new NativeStructureTerrainIntegrator.TerrainTarget( "test:bounded-snapshot", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)))); assertNotNull(sourceTerrain); assertEquals(23 * 16 * 16, sourceTerrain.sampledCells()); @@ -210,7 +220,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:stronghold", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setEncasePalette(palette), NativeStructurePostProcessorEncaseTest::tuffBlock); @@ -221,10 +231,10 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void omittedTerrainConfigurationRetainsSourceSemantics() { for (TerrainAdjustment adjustment : TerrainAdjustment.values()) { - IrisStructureTerrain resolved = NativeStructureTerrainIntegrator.resolveNativeTerrain( + StructureTerrainSettings resolved = NativeStructureTerrainIntegrator.resolveNativeTerrain( start(adjustment, 64), null); - assertEquals(IrisStructureTerrainMode.SOURCE, resolved.resolvedMode()); + assertEquals(StructureTerrainMode.SOURCE, resolved.resolvedMode()); } } @@ -240,7 +250,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "test:bury", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX() - 5, groundY, bounds.minZ())); @@ -267,7 +277,7 @@ public class NativeStructurePostProcessorEncaseTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "test:encapsulate", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX() - 11, bounds.minY(), bounds.minZ())); @@ -304,7 +314,7 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void explicitTerrainConfigurationWinsOverAutoEncase() { IrisStructureTerrain configured = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.PRESERVE); + .setMode(StructureTerrainMode.PRESERVE); assertSame(configured, NativeStructureTerrainIntegrator.resolveNativeTerrain( start(TerrainAdjustment.BURY, 64), configured)); @@ -313,11 +323,11 @@ public class NativeStructurePostProcessorEncaseTest { @Test public void legacyTemplateAirIsClearedAfterEveryTerrainFillPath() { IrisStructureTerrain source = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.SOURCE); + .setMode(StructureTerrainMode.SOURCE); IrisStructureTerrain preserve = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.PRESERVE); + .setMode(StructureTerrainMode.PRESERVE); IrisStructureTerrain encase = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE); + .setMode(StructureTerrainMode.ENCASE); assertTrue(NativeStructureTerrainIntegrator.clearsLegacyTemplateAir( start(TerrainAdjustment.BURY, 64), source)); @@ -337,7 +347,7 @@ public class NativeStructurePostProcessorEncaseTest { BoundingBox sourceBounds = generated.getBoundingBox(); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(4); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -373,7 +383,7 @@ public class NativeStructurePostProcessorEncaseTest { StructureStart generated = start(TerrainAdjustment.BURY, 64); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(112); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -446,7 +456,7 @@ public class NativeStructurePostProcessorEncaseTest { assertEquals(minY, start.getBoundingBox().minY()); } - private static BlockState tuffBlock(IrisMaterialPalette palette, RNG rng, int x, int y, int z) { + private static BlockState tuffBlock(StructurePalette palette, RNG rng, int x, int y, int z) { assertNotNull(palette); assertNotNull(rng); return Blocks.TUFF.defaultBlockState(); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java index 51aef687f..e8865d11b 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorMonumentTest.java @@ -1,7 +1,11 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import net.minecraft.SharedConstants; import net.minecraft.core.Direction; import net.minecraft.core.HolderSet; @@ -95,7 +99,7 @@ public class NativeStructurePostProcessorMonumentTest { start.getStructure(), 0, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(4)); int offset = NativeStructureVerticalPlacer.applyVerticalPlacement( diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java index 9e941f0a5..96063aabc 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorScatteredFeatureTest.java @@ -1,5 +1,11 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReflection; + import net.minecraft.SharedConstants; import net.minecraft.core.HolderSet; import net.minecraft.server.Bootstrap; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java index a32dd1b4c..38660ffc1 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorSurfaceTerrainTest.java @@ -1,9 +1,29 @@ package art.arcane.iris.nativegen.v26_3_R1; -import art.arcane.iris.generation.mantle.StructureCarvingFootprint; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReferenceEnvelope; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTerrainIntegrator; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureFoundationBuilder; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureSurfaceSupportBuilder; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVegetationClearer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTemplateOccupancy; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReflection; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.WorldgenTerrainHeightmaps; + +import art.arcane.volmlib.util.structure.StructureCarvingFootprint; +import art.arcane.volmlib.util.structure.StructureCarveShape; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; @@ -214,10 +234,10 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, x, sourceY, 0, Blocks.GRASS_BLOCK.defaultBlockState()); } assertTrue(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(start, IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(start, StructureTerrainMode.FLATTEN))); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> x < 2 ? groundY - 32 : groundY + 32); for (int x = 0; x <= 3; x++) { @@ -240,7 +260,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, 0, groundY - 19, 0, Blocks.DIRT.defaultBlockState()); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> groundY); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> groundY); for (int y = groundY - 19; y <= groundY; y++) { assertEquals(Blocks.DIRT.defaultBlockState(), state(blocks, 0, y, 0)); @@ -260,9 +280,9 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Map originalSubmerged = new HashMap<>(submerged); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(buried), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> 100); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> 100); NativeStructureSurfaceFitter.prepareSurfaceStructures(world(submerged), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), (x, z) -> groundY - 8); + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> groundY - 8); assertEquals(originalBuried, buried); assertEquals(originalSubmerged, submerged); @@ -280,12 +300,12 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart start = new StructureStart(structure, new ChunkPos(0, 0), 0, new PiecesContainer(desertStart().getPieces())); assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(start, IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(start, StructureTerrainMode.FLATTEN))); } assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(desertStart(TerrainAdjustment.BURY), IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(desertStart(TerrainAdjustment.BURY), StructureTerrainMode.FLATTEN))); assertFalse(NativeStructureSurfaceFitter.requiresFlattenTerrain( - surfaceTarget(desertStart(TerrainAdjustment.ENCAPSULATE), IrisStructureTerrainMode.FLATTEN))); + surfaceTarget(desertStart(TerrainAdjustment.ENCAPSULATE), StructureTerrainMode.FLATTEN))); } @Test @@ -294,8 +314,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))), new BoundingBox(0, 128, 0, 0, 136, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setFlattenRange(32); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setFlattenRange(32); BoundingBox area = new BoundingBox(0, 0, 0, 0, 255, 0); Map blocks = new HashMap<>(); put(blocks, 0, 94, 0, Blocks.GOLD_BLOCK.defaultBlockState()); @@ -323,7 +343,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { block(0, 0, 0, Blocks.COBBLESTONE.defaultBlockState())))), new BoundingBox(0, 64, 0, 0, 70, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece)); - IrisStructureTerrain terrain = new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN) + IrisStructureTerrain terrain = new IrisStructureTerrain().setMode(StructureTerrainMode.FLATTEN) .setHorizontalPadding(128).setFlattenRange(128); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds(start, start.getStructure(), terrain); @@ -351,7 +371,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Map blocks = supportTerrain(0, 0); Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written); @@ -372,7 +392,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { put(blocks, 0, 80, 0, Blocks.STONE.defaultBlockState()); Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertTrue(written.isEmpty()); @@ -399,7 +419,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { } NativeStructureSurfaceFitter.prepareSurfaceStructures(world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.FLATTEN)), + List.of(surfaceTarget(start, StructureTerrainMode.FLATTEN)), (x, z) -> x == footprint.minX() ? 80 : 112); assertEquals(81, piece.getBoundingBox().minY()); @@ -421,8 +441,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(4, 65, 0, 4, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24); BoundingBox area = new BoundingBox(0, 0, 0, 8, 128, 0); Map blocks = flatTerrain(area, 96); for (int x : List.of(0, 8)) { @@ -472,8 +492,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(0, 65, 0, 8, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 0); WorldGenLevel world = world(chunk); @@ -532,8 +552,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(), StructureTemplatePool.Projection.TERRAIN_MATCHING), new BoundingBox(4, 65, 0, 6, 75, 0), 0, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(left, path, right)); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24).setFlattenRange(32); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24).setFlattenRange(32); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 0); WorldGenLevel world = world(chunk); @@ -579,8 +599,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { } } StructureStart start = rigidSurfaceStart(pieces); - NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, IrisStructureTerrainMode.FLATTEN); - target.terrain().setHorizontalPadding(24).setFlattenRange(64); + NativeStructureTerrainIntegrator.TerrainTarget target = surfaceTarget(start, StructureTerrainMode.FLATTEN); + ((IrisStructureTerrain) target.terrain()).setHorizontalPadding(24).setFlattenRange(64); BoundingBox area = new BoundingBox(0, 0, 0, 8, 127, 8); Map blocks = new HashMap<>(); for (int x = 0; x <= 8; x++) { @@ -667,11 +687,11 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain( new NativeStructureTerrainIntegrator.TerrainTarget( "test:beard_box", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)))); assertFalse(NativeStructureSurfaceFitter.requiresSurfaceTerrain( new NativeStructureTerrainIntegrator.TerrainTarget( "test:beard_box", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.PRESERVE)))); + new IrisStructureTerrain().setMode(StructureTerrainMode.PRESERVE)))); } @Test @@ -681,15 +701,15 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.TerrainTarget sourceNone = new NativeStructureTerrainIntegrator.TerrainTarget( "test:none", none, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE)); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE)); NativeStructureTerrainIntegrator.TerrainTarget vacuumNone = new NativeStructureTerrainIntegrator.TerrainTarget( "test:none", none, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); NativeStructureTerrainIntegrator.TerrainTarget vacuumBox = new NativeStructureTerrainIntegrator.TerrainTarget( "test:box", box, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); assertFalse(NativeStructureSurfaceFitter.requiresSurfaceTerrain(sourceNone)); assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(vacuumNone)); @@ -778,7 +798,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List targets = List.of( new NativeStructureTerrainIntegrator.TerrainTarget( "nova_structures:tavern_oak", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))); + new IrisStructureTerrain().setMode(StructureTerrainMode.SOURCE))); NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, targets, (x, z) -> 65); @@ -886,7 +906,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, 0, 64, 0)); @@ -911,7 +931,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 65, 0)); @@ -932,7 +952,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 80); for (int y = 61; y <= 74; y++) { @@ -957,7 +977,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 79); for (int y = 61; y <= 74; y++) { @@ -989,7 +1009,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.SurfaceTerrainPlan plan = NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); for (int y = 61; y <= 64; y++) { put(blocks, 0, y, 0, Blocks.CAVE_AIR.defaultBlockState()); @@ -1029,7 +1049,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.SurfaceTerrainPlan plan = NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); put(blocks, 0, 65, 0, Blocks.COBBLESTONE.defaultBlockState()); @@ -1051,7 +1071,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { new BoundingBox(15, 65, 0, 16, 70, 0), 1, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece), TerrainAdjustment.NONE); NativeStructureTerrainIntegrator.TerrainTarget target = - surfaceTarget(start, IrisStructureTerrainMode.VACUUM); + surfaceTarget(start, StructureTerrainMode.VACUUM); BoundingBox wideArea = new BoundingBox(0, 54, 0, 31, 72, 15); BoundingBox westArea = new BoundingBox(0, 54, 0, 15, 72, 15); BoundingBox eastArea = new BoundingBox(16, 54, 0, 31, 72, 15); @@ -1100,7 +1120,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1121,7 +1141,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1145,7 +1165,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 1, 66, 0)); @@ -1170,7 +1190,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1190,7 +1210,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 0, 65, 0)); @@ -1215,7 +1235,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(originalBlocks, blocks); @@ -1236,7 +1256,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); assertEquals(Blocks.GRASS_BLOCK.defaultBlockState(), state(blocks, 4, 61, 4)); @@ -1254,7 +1274,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 50); assertEquals(originalBlocks, blocks); @@ -1273,7 +1293,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60, 1); assertEquals(originalBlocks, blocks); @@ -1297,7 +1317,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureSurfaceFitter.prepareSurfaceStructures( world(blocks), area, - List.of(surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(start, StructureTerrainMode.VACUUM)), (x, z) -> 60); for (int y = 61; y <= 65; y++) { @@ -1317,7 +1337,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { new BoundingBox(15, 66, 0, 16, 72, 0), 1, Rotation.NONE); StructureStart start = rigidSurfaceStart(List.of(piece), TerrainAdjustment.NONE); NativeStructureTerrainIntegrator.TerrainTarget target = - surfaceTarget(start, IrisStructureTerrainMode.VACUUM); + surfaceTarget(start, StructureTerrainMode.VACUUM); BoundingBox wideArea = new BoundingBox(0, 54, 0, 31, 74, 15); BoundingBox westArea = new BoundingBox(0, 54, 0, 15, 74, 15); BoundingBox eastArea = new BoundingBox(16, 54, 0, 31, 74, 15); @@ -1573,13 +1593,13 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Set vacuumWritten = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(vacuumBlocks), area, - List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(vacuumStart, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), vacuumWritten); assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(supportTerrain(0, 0)), area, - List.of(surfaceTarget(vacuumStart, IrisStructureTerrainMode.SOURCE)), + List.of(surfaceTarget(vacuumStart, StructureTerrainMode.SOURCE)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty()); PoolElementStructurePiece distantUpper = rigidTemplatePiece( @@ -1589,7 +1609,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { List.of(lower, distantUpper), TerrainAdjustment.NONE); assertTrue(NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(supportTerrain(0, 0)), area, - List.of(surfaceTarget(distantStart, IrisStructureTerrainMode.VACUUM)), + List.of(surfaceTarget(distantStart, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager).isEmpty()); } @@ -1676,8 +1696,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { Set written = NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( world(blocks), area, List.of( - surfaceTarget(start, IrisStructureTerrainMode.PRESERVE), - surfaceTarget(start, IrisStructureTerrainMode.VACUUM)), + surfaceTarget(start, StructureTerrainMode.PRESERVE), + surfaceTarget(start, StructureTerrainMode.VACUUM)), NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager); assertEquals(Set.of(BlockPos.asLong(0, 63, 0)), written); @@ -2033,7 +2053,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), bounds, "minecraft:desert_pyramid", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM), null); assertEquals(Blocks.STONE.defaultBlockState(), state(blocks, bounds.minX(), bounds.minY(), bounds.minZ())); @@ -2055,7 +2075,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:desert_pyramid", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(1) .setCeilingPadding(1), null); @@ -2089,7 +2109,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:ancient_city", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FORCE_CARVE), null); + new IrisStructureTerrain().setMode(StructureTerrainMode.FORCE_CARVE), null); assertEquals(Blocks.AIR.defaultBlockState(), state(blocks, firstBounds.minX(), y, z)); @@ -2338,8 +2358,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.integrateTerrain( world(blocks), area, "minecraft:ancient_city", start, new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(6) .setCeilingPadding(8) .setFloorPadding(0) @@ -2378,7 +2398,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart generated = desertStart(); BoundingBox content = generated.getBoundingBox(); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( @@ -2403,7 +2423,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { StructureStart generated = desertStart(TerrainAdjustment.NONE); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.VACUUM) + .setMode(StructureTerrainMode.VACUUM) .setHorizontalPadding(64); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds( @@ -2425,7 +2445,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { ChunkPos origin = generated.getChunkPos(); BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(generated); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(124); BoundingBox references = NativeStructureReferenceEnvelope.referenceBounds( @@ -2575,11 +2595,11 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget( StructureStart start) { - return surfaceTarget(start, IrisStructureTerrainMode.SOURCE); + return surfaceTarget(start, StructureTerrainMode.SOURCE); } private static NativeStructureTerrainIntegrator.TerrainTarget surfaceTarget( - StructureStart start, IrisStructureTerrainMode mode) { + StructureStart start, StructureTerrainMode mode) { return new NativeStructureTerrainIntegrator.TerrainTarget( "nova_structures:tavern_oak", start, new IrisStructureTerrain().setMode(mode)); @@ -2619,8 +2639,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { private static NativeStructureTerrainIntegrator.OrganicCarve organicCarve( StructureStart start, int horizontalPadding, double lobeStrength) { IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(horizontalPadding) .setCeilingPadding(8) .setFloorPadding(0) @@ -2630,7 +2650,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { return NativeStructureTerrainIntegrator.organicCarve( NativeStructureTerrainIntegrator.carveFootprint(start, horizontalPadding, NativeStructurePostProcessorSurfaceTerrainTest::forbiddenTemplateManager), - terrain, IrisStructureCarveShape.ERODED, TEST_SEED); + terrain, StructureCarveShape.ERODED, TEST_SEED); } /** @@ -2648,8 +2668,8 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { return true; }, SLAB_PADDING, 1_000_000); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ERODED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ERODED) .setHorizontalPadding(SLAB_PADDING) .setCeilingPadding(8) .setFloorPadding(0) @@ -2664,7 +2684,7 @@ public class NativeStructurePostProcessorSurfaceTerrainTest { NativeStructureTerrainIntegrator.carveOrganicColumns(world(blocks), area, NativeStructureTerrainIntegrator.organicCarve( - footprint, terrain, IrisStructureCarveShape.ERODED, TEST_SEED)); + footprint, terrain, StructureCarveShape.ERODED, TEST_SEED)); int[] depths = new int[SLAB_DEPTH]; for (int z = 0; z < SLAB_DEPTH; z++) { diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java index 6f04a83fc..f47dbe3fb 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructurePostProcessorVegetationTest.java @@ -1,5 +1,11 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureSurfaceFitter; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVegetationClearer; + import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java index 1b0e9e0a9..220f4861e 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancyTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTemplateOccupancy; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureReflection; + import com.mojang.datafixers.util.Either; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java index 8eeef2d6c..31a000f01 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTerrainHeightCacheTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainHeightCache; + import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraft.world.level.levelgen.Heightmap; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java index f19117842..e4317a803 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeTransitionColumnTest.java @@ -1,10 +1,14 @@ package art.arcane.iris.nativegen.v26_3_R1; +import art.arcane.iris.world.history.TerrainNativeBlockKeys; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTransitionColumn; + import art.arcane.iris.world.history.BoundaryColumnGeometry; import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import net.minecraft.SharedConstants; @@ -45,8 +49,8 @@ public final class NativeTransitionColumnTest { @Test public void customKeysUseCarrierForNativeColumnsAndHeightmaps() { String key = "itemsadder:rocks/ruby_ore"; - PlatformBlockState custom = mock(PlatformBlockState.class); - PlatformBlockState carrier = mock(PlatformBlockState.class); + NativeBlockState custom = mock(NativeBlockState.class); + NativeBlockState carrier = mock(NativeBlockState.class); when(custom.placementBaseState()).thenReturn(carrier); when(carrier.key()).thenReturn("minecraft:oak_log[axis=z]"); IrisPlatform platform = mock(IrisPlatform.class); @@ -59,17 +63,17 @@ public final class NativeTransitionColumnTest { try (MockedStatic platforms = mockStatic(IrisPlatforms.class)) { platforms.when(IrisPlatforms::isBound).thenReturn(true); platforms.when(IrisPlatforms::get).thenReturn(platform); - NoiseColumn column = NativeTransitionColumn.column(signature, height); + NoiseColumn column = NativeTransitionColumn.column(signature.geometry(), height, TerrainNativeBlockKeys::placementKey); assertEquals(Direction.Axis.Z, column.getBlock(-15).getValue(RotatedPillarBlock.AXIS)); - assertEquals(-14, NativeTransitionColumn.height(signature, Heightmap.Types.OCEAN_FLOOR_WG, height)); + assertEquals(-14, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.OCEAN_FLOOR_WG, height, TerrainNativeBlockKeys::placementKey)); } } @Test public void preservesNativePropertiesCavesAndFluids() { - NoiseColumn column = NativeTransitionColumn.column(signature("minecraft:oak_log[axis=x]"), - LevelHeightAccessor.create(-16, 32)); + NoiseColumn column = NativeTransitionColumn.column(signature("minecraft:oak_log[axis=x]").geometry(), + LevelHeightAccessor.create(-16, 32), TerrainNativeBlockKeys::placementKey); assertEquals(Direction.Axis.X, column.getBlock(-15).getValue(RotatedPillarBlock.AXIS)); assertTrue(column.getBlock(-14).is(Blocks.CAVE_AIR)); assertEquals(Integer.valueOf(3), column.getBlock(-13).getValue(LiquidBlock.LEVEL)); @@ -80,14 +84,13 @@ public final class NativeTransitionColumnTest { public void usesNativeHeightmapPredicatesForWaterAndOpenSpace() { TerrainBoundarySignature signature = signature("minecraft:oak_log[axis=x]"); LevelHeightAccessor height = LevelHeightAccessor.create(-16, 32); - assertEquals(-12, NativeTransitionColumn.height(signature, Heightmap.Types.WORLD_SURFACE_WG, height)); - assertEquals(-14, NativeTransitionColumn.height(signature, Heightmap.Types.OCEAN_FLOOR_WG, height)); + assertEquals(-12, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.WORLD_SURFACE_WG, height, TerrainNativeBlockKeys::placementKey)); + assertEquals(-14, NativeTransitionColumn.height(signature.geometry(), Heightmap.Types.OCEAN_FLOOR_WG, height, TerrainNativeBlockKeys::placementKey)); } @Test public void rejectsUnknownSavedStatesInsteadOfReplacingTerrain() { - assertThrows(IllegalArgumentException.class, () -> NativeTransitionColumn.column( - signature("missing:removed_block"), LevelHeightAccessor.create(-16, 32))); + assertThrows(IllegalArgumentException.class, () -> NativeTransitionColumn.column(signature("missing:removed_block").geometry(), LevelHeightAccessor.create(-16, 32), TerrainNativeBlockKeys::placementKey)); } private static TerrainBoundarySignature signature(String solidState) { diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java index 5ee25b4ce..a30e593d8 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawn; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawnType; -import art.arcane.iris.platform.bukkit.nms.datapack.v263.DataFixerV263; +import art.arcane.iris.pack.datapack.v263.DataFixerV263; import art.arcane.volmlib.util.collection.KList; import com.google.gson.JsonParser; import com.mojang.serialization.JsonOps; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSourceStructureContractTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSourceStructureContractTest.java index 41ac5d5a7..ffc57a5c1 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSourceStructureContractTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeSourceStructureContractTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.iris.platform.bukkit.nms.BukkitBiomePolicy; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.terrain.IrisDimension; import net.minecraft.SharedConstants; @@ -28,10 +30,10 @@ public class CustomBiomeSourceStructureContractTest { IrisDimension upper = new IrisDimension(); upper.setLoadKey("Layers/Upper"); - assertEquals("layers:upper/aurora", CustomBiomeSource.customBiomeKey(upper, "Aurora")); + assertEquals("layers:upper/aurora", BukkitBiomePolicy.customBiomeKey(upper, "Aurora")); assertNotEquals( - CustomBiomeSource.customBiomeKey(host, "Aurora"), - CustomBiomeSource.customBiomeKey(upper, "Aurora") + BukkitBiomePolicy.customBiomeKey(host, "Aurora"), + BukkitBiomePolicy.customBiomeKey(upper, "Aurora") ); } @@ -39,8 +41,8 @@ public class CustomBiomeSourceStructureContractTest { public void temporaryNaturalBiomeAnswersAreNotMemoized() { Engine temporary = engineAnsweringNaturalFallback(true, 12, -8); Engine stable = engineAnsweringNaturalFallback(false, 12, -8); - assertFalse(CustomBiomeSource.isBiomeCacheable(temporary, 12, -8)); - assertTrue(CustomBiomeSource.isBiomeCacheable(stable, 12, -8)); + assertFalse(BukkitBiomePolicy.isBiomeCacheable(temporary, 12, -8)); + assertTrue(BukkitBiomePolicy.isBiomeCacheable(stable, 12, -8)); } private static Engine engineAnsweringNaturalFallback( diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorMonumentLocateContractTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorMonumentLocateContractTest.java index 50200394d..686fa18f4 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorMonumentLocateContractTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorMonumentLocateContractTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureLocateResults; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureLocateResults; import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorSpawnAdmissionTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorSpawnAdmissionTest.java index c114acef6..3a9b10b36 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorSpawnAdmissionTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/IrisChunkGeneratorSpawnAdmissionTest.java @@ -1,5 +1,10 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.iris.platform.bukkit.nms.BukkitGeneratorContext; +import org.bukkit.World; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeChunkGenerator; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeBiomeSourceImpl; + import net.minecraft.server.level.ServerLevel; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.GenerationSessionManager; @@ -90,8 +95,8 @@ public class IrisChunkGeneratorSpawnAdmissionTest { } } - private static void setField(IrisChunkGenerator generator, String name, Object value) throws Exception { - Field field = IrisChunkGenerator.class.getDeclaredField(name); + private static void setField(NativeChunkGenerator generator, String name, Object value) throws Exception { + Field field = NativeChunkGenerator.class.getDeclaredField(name); field.setAccessible(true); field.set(generator, value); } @@ -99,7 +104,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { private static final class Fixture { private final GenerationSessionManager sessions = new GenerationSessionManager(true); private final IrisEngine engine = mock(IrisEngine.class); - private final IrisChunkGenerator generator = mock(IrisChunkGenerator.class, CALLS_REAL_METHODS); + private final NativeChunkGenerator generator = mock(NativeChunkGenerator.class, CALLS_REAL_METHODS); private final Holder biome = Holder.direct(mock(Biome.class)); private final StructureManager structures = mock(StructureManager.class); private final WeightedList expected = mock(WeightedList.class); @@ -112,7 +117,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { GenerationHistoryRuntimeRouter router = mock(GenerationHistoryRuntimeRouter.class); SavedBiomeRuntime saved = mock(SavedBiomeRuntime.class); ChunkGenerator delegate = mock(ChunkGenerator.class); - CustomBiomeSource source = mock(CustomBiomeSource.class); + NativeBiomeSourceImpl source = mock(NativeBiomeSourceImpl.class); when(level.getBiome(new BlockPos(0, 64, 0))).thenReturn(biome); setField(generator, "runtimeLevel", level); when(engine.getGenerationSessions()).thenReturn(sessions); @@ -130,7 +135,7 @@ public class IrisChunkGeneratorSpawnAdmissionTest { }); when(delegate.getMobsAt(level, structures, MobCategory.MONSTER, new BlockPos(0, 64, 0))) .thenReturn(expected); - setField(generator, "engine", engine); + setField(generator, "context", new BukkitGeneratorContext(new BukkitGeneratorContext.Options(1L, engine, mock(World.class)))); setField(generator, "delegate", delegate); setField(generator, "customBiomeSource", source); } diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBindingDatapackStructureScopeTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBindingDatapackStructureScopeTest.java index ab0c0fe21..77127ba6f 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBindingDatapackStructureScopeTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBindingDatapackStructureScopeTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureSetFilter; + import net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement; import art.arcane.iris.pack.datapack.DatapackIngestService; import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; @@ -60,9 +62,9 @@ public class NMSBindingDatapackStructureScopeTest { List.of(), List.of("managed:illager_barracks")); - DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection vanilla = NativeStructureSetFilter.filter( List.of(managedSet), index, Set.of()); - DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection declaring = NativeStructureSetFilter.filter( List.of(managedSet), index, index.declaredSources(List.of(SOURCE))); assertEquals(0, vanilla.structureSets().size()); @@ -81,9 +83,9 @@ public class NMSBindingDatapackStructureScopeTest { List.of("managed:tavern"), List.of()); - DatapackStructureStateFilter.Selection vanilla = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection vanilla = NativeStructureSetFilter.filter( List.of(vanillaSet), index, Set.of()); - DatapackStructureStateFilter.Selection declaring = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection declaring = NativeStructureSetFilter.filter( List.of(vanillaSet), index, index.declaredSources(List.of(SOURCE))); assertEquals(1, vanilla.structureSets().size()); @@ -101,7 +103,7 @@ public class NMSBindingDatapackStructureScopeTest { "minecraft:custom", structureHolder("managed:only")); DatapackStructureScopeIndex index = index(List.of("managed:only"), List.of()); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(unmanagedSet), index, Set.of()); assertEquals(0, selection.structureSets().size()); @@ -125,18 +127,18 @@ public class NMSBindingDatapackStructureScopeTest { 34, 8, RandomSpreadType.LINEAR, 14357620))); DatapackStructureScopeIndex scopeIndex = index( List.of(), List.of("managed:illager_barracks")); - DatapackStructureStateFilter.StructureSetKeyIndex keyIndex = - DatapackStructureStateFilter.keyIndex(List.of(registered)); + NativeStructureSetFilter.StructureSetKeyIndex keyIndex = + NativeStructureSetFilter.keyIndex(List.of(registered)); - DatapackStructureStateFilter.Selection excluded = - DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection excluded = + NativeStructureSetFilter.filter( List.of(spigotDirect), scopeIndex, Set.of(), new IrisImportedStructureControl(), keyIndex); - DatapackStructureStateFilter.Selection retained = - DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection retained = + NativeStructureSetFilter.filter( List.of(spigotDirect), scopeIndex, scopeIndex.declaredSources(List.of(SOURCE)), @@ -151,8 +153,8 @@ public class NMSBindingDatapackStructureScopeTest { @Test public void structureScopeDoesNotLinkPaperOnlyPlacementClasses() throws IOException { - InputStream classResource = NMSBindingDatapackStructureScopeTest.class - .getResourceAsStream("DatapackStructureStateFilter.class"); + InputStream classResource = NativeStructureSetFilter.class + .getResourceAsStream("NativeStructureSetFilter.class"); assertNotNull(classResource); try (InputStream input = classResource) { String classFile = new String(input.readAllBytes(), StandardCharsets.ISO_8859_1); @@ -177,7 +179,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(complexes, fossils), index(List.of(), List.of()), Set.of(), control); RandomSpreadStructurePlacement scaledComplexes = @@ -207,7 +209,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(custom), index(List.of(), List.of()), Set.of(), control); assertSame(custom, selection.structureSets().getFirst()); @@ -239,14 +241,14 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(dependent, target), index(List.of(), List.of()), Set.of(), control); Holder scaledDependent = selection.structureSets().get(0); Holder scaledTarget = selection.structureSets().get(1); assertNotSame(dependent, scaledDependent); assertNotSame(target, scaledTarget); - assertSame(scaledTarget, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledTarget, NativeStructureSetFilter.exclusionZone( scaledDependent.value().placement()).orElseThrow().otherSet()); assertEquals(26, ((RandomSpreadStructurePlacement) scaledTarget.value().placement()).spacing()); @@ -285,14 +287,14 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(first, second), index(List.of(), List.of()), Set.of(), control); Holder scaledFirst = selection.structureSets().get(0); Holder scaledSecond = selection.structureSets().get(1); - assertSame(scaledSecond, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledSecond, NativeStructureSetFilter.exclusionZone( scaledFirst.value().placement()).orElseThrow().otherSet()); - assertSame(scaledFirst, DatapackStructureStateFilter.exclusionZone( + assertSame(scaledFirst, NativeStructureSetFilter.exclusionZone( scaledSecond.value().placement()).orElseThrow().otherSet()); assertEquals(26, ((RandomSpreadStructurePlacement) scaledSecond.value().placement()).spacing()); @@ -311,7 +313,7 @@ public class NMSBindingDatapackStructureScopeTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(custom), index(List.of(), List.of()), Set.of(), control); StructurePlacement placement = selection.structureSets().getFirst().value().placement(); @@ -341,12 +343,12 @@ public class NMSBindingDatapackStructureScopeTest { List.of("managed:blocked"), List.of("managed:blocked")); - DatapackStructureStateFilter.Selection selection = DatapackStructureStateFilter.filter( + NativeStructureSetFilter.Selection selection = NativeStructureSetFilter.filter( List.of(vanillaSet, managedSet), index, Set.of()); assertEquals(1, selection.structureSets().size()); StructurePlacement scopedPlacement = selection.structureSets().getFirst().value().placement(); - assertEquals(0, DatapackStructureStateFilter.exclusionZone(scopedPlacement).stream().count()); + assertEquals(0, NativeStructureSetFilter.exclusionZone(scopedPlacement).stream().count()); } private static DatapackStructureScopeIndex index( diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdatesTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdatesTest.java index 0232d6b7a..9c6e30a3e 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdatesTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructurePoiUpdatesTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructurePoiUpdates; + import ca.spottedleaf.moonrise.common.util.TickThread; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccessTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccessTest.java index f6d20f7ce..2d303788e 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccessTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NativeStructureWorldgenAccessTest.java @@ -1,9 +1,11 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureWorldgenAccess; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.v26_3_R1.NativeStructureTerrainIntegrator; +import art.arcane.volmlib.util.structure.StructureTerrainMode; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructurePostProcessor; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureTerrainIntegrator; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -84,7 +86,7 @@ public class NativeStructureWorldgenAccessTest { StructureStart start = new StructureStart(structure, generationCenter(), 0, new PiecesContainer(List.of(piece))); List targets = List.of( new NativeStructureTerrainIntegrator.TerrainTarget("minecraft:village_plains", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN))); + new IrisStructureTerrain().setMode(StructureTerrainMode.FLATTEN))); BoundingBox area = new BoundingBox(generationCenter().getMinBlockX(), -64, generationCenter().getMinBlockZ(), generationCenter().getMaxBlockX(), 319, generationCenter().getMaxBlockZ()); RecordingDelegate unbounded = new RecordingDelegate(); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkIOFlushTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkIOFlushTest.java index 5b967837c..50d0323d3 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkIOFlushTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkIOFlushTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainAccessImpl; import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO.RegionDataController; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; @@ -83,7 +84,7 @@ public class NmsChunkIOFlushTest { } private static final class Fixture { - private final NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + private final NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); private final CraftWorld world = mock(CraftWorld.class); private final ServerLevel level = mock(ServerLevel.class); private final RegionDataController chunks = mock(RegionDataController.class); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkTaskPollingTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkTaskPollingTest.java index 8e4475314..846ebe2b6 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkTaskPollingTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsChunkTaskPollingTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainAccessImpl; import art.arcane.volmlib.util.scheduling.FoliaScheduler; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; @@ -41,7 +42,7 @@ public class NmsChunkTaskPollingTest { ServerLevel level = mock(ServerLevel.class); MinecraftServer server = mock(MinecraftServer.class); ServerChunkCache chunks = mock(ServerChunkCache.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); CompletableFuture completed = new CompletableFuture<>(); when(world.getHandle()).thenReturn(level); when(level.getServer()).thenReturn(server); @@ -66,7 +67,7 @@ public class NmsChunkTaskPollingTest { CraftWorld world = mock(CraftWorld.class); ServerLevel level = mock(ServerLevel.class); MinecraftServer server = mock(MinecraftServer.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); when(world.getHandle()).thenReturn(level); when(level.getServer()).thenReturn(server); when(server.getRunningThread()).thenReturn(new Thread("Other server owner")); @@ -83,7 +84,7 @@ public class NmsChunkTaskPollingTest { public void foliaNeverUsesTheWorldWideTaskPump() { Server bukkitServer = mock(Server.class); CraftWorld world = mock(CraftWorld.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); try (MockedStatic bukkit = mockStatic(Bukkit.class); MockedStatic folia = mockStatic(FoliaScheduler.class)) { bukkit.when(Bukkit::getServer).thenReturn(bukkitServer); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsEntitySpawnDifficultyTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsEntitySpawnDifficultyTest.java index 4a4ec9255..9d507b60a 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsEntitySpawnDifficultyTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsEntitySpawnDifficultyTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainAccessImpl; import net.minecraft.SharedConstants; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; @@ -41,7 +42,7 @@ public class NmsEntitySpawnDifficultyTest { public void peacefulRejectsEveryNativeForbiddenTypeBeforeBukkitCreatesIt() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); int rejected = 0; @@ -64,7 +65,7 @@ public class NmsEntitySpawnDifficultyTest { public void peacefulPreservesPassiveAndNativeMonsterCategoryExceptions() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); Entity created = mock(Entity.class); doReturn(created).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); @@ -77,7 +78,7 @@ public class NmsEntitySpawnDifficultyTest { @Test public void nonPeacefulDifficultiesPreserveHostileSpawns() { CraftWorld world = mock(CraftWorld.class); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); Entity created = mock(Entity.class); doReturn(created).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); @@ -92,7 +93,7 @@ public class NmsEntitySpawnDifficultyTest { public void unrelatedBukkitSpawnFailuresStillPropagate() { CraftWorld world = mock(CraftWorld.class); when(world.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - NMSBinding binding = mock(NMSBinding.class, CALLS_REAL_METHODS); + NativeTerrainAccessImpl binding = mock(NativeTerrainAccessImpl.class, CALLS_REAL_METHODS); Location location = new Location(world, 0.5, 80, 0.5); IllegalStateException failure = new IllegalStateException("Spawn rejected by platform"); doThrow(failure).when(world).spawn(eq(location), any(Class.class), isNull(), eq(CreatureSpawnEvent.SpawnReason.NATURAL)); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCheckpointTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCheckpointTest.java deleted file mode 100644 index 2a670dc58..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainCheckpointTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.world.history.SavedTerrainChunk; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import net.minecraft.SharedConstants; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; - -public class NmsSavedTerrainCheckpointTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void checkpointWaitsForDirtyAndUnloadedChunkWritesBeforeFlushAndVerification() throws Exception { - ServerLevel level = mock(ServerLevel.class); - Path root = Path.of("checkpoint-world"); - byte[] receipt = new byte[]{1, 2, 3}; - NmsSavedTerrainCapture.Checkpoint saved = new NmsSavedTerrainCapture.Checkpoint(new ChunkPos(4, -5), - new NmsSavedTerrainCapture.Verification("minecraft:full", receipt, 7L)); - NmsSavedTerrainCapture.Checkpoint unloaded = NmsSavedTerrainCapture.checkpoint(-8, 9, null); - Set completed = new HashSet<>(); - AtomicBoolean flushed = new AtomicBoolean(); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, 4, -5, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - completed.add(saved.chunk()); - return Priority.COMPLETING; - }); - io.when(() -> MoonriseRegionFileIO.getPriority(level, -8, 9, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - completed.add(unloaded.chunk()); - return Priority.COMPLETING; - }); - io.when(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> { - assertEquals(Set.of(saved.chunk(), unloaded.chunk()), completed); - flushed.set(true); - return null; - }); - terrain.when(() -> SavedTerrainChunk.verifyCheckpoint(root, 4, -5, "minecraft:full", receipt, 7L)) - .thenAnswer(invocation -> { - assertTrue(flushed.get()); - return null; - }); - - NmsSavedTerrainCapture.finishCheckpoint(level, root, List.of(saved, unloaded)); - - terrain.verify(() -> SavedTerrainChunk.verifyCheckpoint(root, 4, -5, "minecraft:full", receipt, 7L)); - io.verify(() -> MoonriseRegionFileIO.flush(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), never()); - io.verify(() -> MoonriseRegionFileIO.getPriority(level, 100, 100, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), never()); - terrain.verifyNoMoreInteractions(); - } - } - - @Test - public void storageFlushFailureCannotCompleteTheCheckpoint() throws Exception { - ServerLevel level = mock(ServerLevel.class); - IOException failure = new IOException("storage flush failed"); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenThrow(failure); - - assertSame(failure, assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.finishCheckpoint(level, Path.of("checkpoint-world"), List.of()))); - terrain.verifyNoInteractions(); - } - } - - @Test - public void persistedReceiptMismatchCannotCompleteTheCheckpoint() throws Exception { - ServerLevel level = mock(ServerLevel.class); - Path root = Path.of("checkpoint-world"); - byte[] receipt = new byte[]{4, 5}; - IOException failure = new IOException("saved terrain receipt mismatch"); - NmsSavedTerrainCapture.Checkpoint saved = new NmsSavedTerrainCapture.Checkpoint(new ChunkPos(1, 2), - new NmsSavedTerrainCapture.Verification("minecraft:noise", receipt, 3L)); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class); - MockedStatic terrain = mockStatic(SavedTerrainChunk.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, 1, 2, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenReturn(Priority.COMPLETING); - terrain.when(() -> SavedTerrainChunk.verifyCheckpoint(root, 1, 2, "minecraft:noise", receipt, 3L)) - .thenThrow(failure); - - assertSame(failure, assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.finishCheckpoint(level, root, List.of(saved)))); - io.verify(() -> MoonriseRegionFileIO.flushRegionStorages(level, MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainSnapshotTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainSnapshotTest.java deleted file mode 100644 index 815575ad0..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainSnapshotTest.java +++ /dev/null @@ -1,223 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import art.arcane.iris.world.task.J; -import ca.spottedleaf.concurrentutil.completable.Completable; -import ca.spottedleaf.concurrentutil.executor.PrioritisedExecutor; -import ca.spottedleaf.concurrentutil.executor.thread.BalancedPrioritisedThreadPool; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.common.PlatformHooks; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkHolderManager; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.NewChunkHolder; -import net.minecraft.SharedConstants; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; -import org.bukkit.World; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.File; -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class NmsSavedTerrainSnapshotTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void ownerReservesSaveOrderBeforeOffOwnerSerializationAndLaterAutosave() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor(); - MockedStatic snapshots = mockStatic(SerializableChunkData.class); - MockedStatic hooks = mockStatic(PlatformHooks.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - Thread owner = Thread.currentThread(); - ChunkAccess chunk = mock(ChunkAccess.class); - SerializableChunkData snapshot = mock(SerializableChunkData.class); - PlatformHooks platform = mock(PlatformHooks.class); - CompoundTag capturedData = new CompoundTag(); - CompoundTag autosave = new CompoundTag(); - List submissions = new ArrayList<>(); - when(chunk.getPos()).thenReturn(new ChunkPos(4, -9)); - snapshots.when(() -> SerializableChunkData.copyOf(fixture.level, chunk)).thenAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - return snapshot; - }); - hooks.when(PlatformHooks::get).thenReturn(platform); - doAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - return null; - }).when(platform).chunkSyncSave(fixture.level, chunk, snapshot); - when(snapshot.write()).thenAnswer(invocation -> { - assertNotSame(owner, Thread.currentThread()); - assertEquals(List.of("checkpoint", "autosave"), submissions); - return capturedData; - }); - io.when(() -> MoonriseRegionFileIO.scheduleSave(eq(fixture.level), eq(4), eq(-9), - any(Completable.class), any(PrioritisedExecutor.PrioritisedTask.class), - eq(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), eq(Priority.NORMAL))) - .thenAnswer(invocation -> { - assertSame(owner, Thread.currentThread()); - submissions.add("checkpoint"); - return null; - }); - io.when(() -> MoonriseRegionFileIO.scheduleSave(fixture.level, 4, -9, autosave, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)).thenAnswer(invocation -> { - submissions.add("autosave"); - return null; - }); - - NmsSavedTerrainCapture.CapturedChunk captured = NmsSavedTerrainCapture.scheduleSnapshot(fixture.level, chunk); - - assertEquals(List.of("checkpoint"), submissions); - assertFalse(captured.data().isDone()); - verify(snapshot, never()).write(); - verify(platform).chunkSyncSave(fixture.level, chunk, snapshot); - MoonriseRegionFileIO.scheduleSave(fixture.level, 4, -9, autosave, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA); - Future result = checkpoint.submit(() -> NmsSavedTerrainCapture.awaitSnapshot( - fixture.level, captured, System.nanoTime() + TimeUnit.SECONDS.toNanos(3))); - - assertSame(capturedData, result.get(5, TimeUnit.SECONDS)); - verify(snapshot).write(); - } - } - - @Test - public void failedOffOwnerSerializationCannotSucceedAsACheckpoint() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor(); - MockedStatic snapshots = mockStatic(SerializableChunkData.class); - MockedStatic hooks = mockStatic(PlatformHooks.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - ChunkAccess chunk = mock(ChunkAccess.class); - SerializableChunkData snapshot = mock(SerializableChunkData.class); - IllegalStateException failure = new IllegalStateException("serialization failed"); - when(chunk.getPos()).thenReturn(new ChunkPos(4, -9)); - snapshots.when(() -> SerializableChunkData.copyOf(fixture.level, chunk)).thenReturn(snapshot); - hooks.when(PlatformHooks::get).thenReturn(mock(PlatformHooks.class)); - when(snapshot.write()).thenThrow(failure); - NmsSavedTerrainCapture.CapturedChunk captured = NmsSavedTerrainCapture.scheduleSnapshot(fixture.level, chunk); - - Future result = checkpoint.submit(() -> assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitSnapshot(fixture.level, captured, - System.nanoTime() + TimeUnit.SECONDS.toNanos(3)))); - - assertSame(failure, result.get(5, TimeUnit.SECONDS).getCause()); - assertTrue(captured.data().isCompletedExceptionally()); - } - } - - @Test - public void checkpointBoundsOwnerRequestsUntilPriorNativeWritesFinish() throws Exception { - try (SaveFixture fixture = new SaveFixture(); - ExecutorService owner = Executors.newSingleThreadExecutor(); - ExecutorService checkpoint = Executors.newSingleThreadExecutor()) { - World world = mock(World.class); - when(world.getWorldFolder()).thenReturn(new File("checkpoint-world")); - List holders = new ArrayList<>(129); - for (int index = 0; index < 129; index++) { - NewChunkHolder holder = mock(NewChunkHolder.class); - setField(holder, "chunkX", index); - holders.add(holder); - } - AtomicInteger requested = new AtomicInteger(); - AtomicBoolean writesBlocked = new AtomicBoolean(true); - CountDownLatch firstBatchWaiting = new CountDownLatch(1); - Future result = checkpoint.submit(() -> { - try (MockedStatic scheduling = mockStatic(J.class); - MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - scheduling.when(() -> J.runRegionFuture(eq(world), anyInt(), anyInt(), any(Runnable.class))) - .thenAnswer(invocation -> { - requested.incrementAndGet(); - Runnable action = invocation.getArgument(3); - return CompletableFuture.runAsync(action, owner); - }); - io.when(() -> MoonriseRegionFileIO.getPriority(eq(fixture.level), anyInt(), anyInt(), - eq(MoonriseRegionFileIO.RegionFileType.CHUNK_DATA))).thenAnswer(invocation -> { - firstBatchWaiting.countDown(); - return writesBlocked.get() ? Priority.NORMAL : Priority.COMPLETING; - }); - NmsSavedTerrainCapture.flushSnapshots(world, fixture.level, holders); - io.verify(() -> MoonriseRegionFileIO.getPriority(fixture.level, 128, 0, - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA), times(2)); - return null; - } - }); - try { - assertTrue(firstBatchWaiting.await(5, TimeUnit.SECONDS)); - assertEquals(64, requested.get()); - assertTrue(owner.submit(() -> true).get(5, TimeUnit.SECONDS)); - assertFalse(result.isDone()); - writesBlocked.set(false); - result.get(5, TimeUnit.SECONDS); - assertEquals(129, requested.get()); - } finally { - writesBlocked.set(false); - } - } - } - - private static void setField(Object instance, String name, Object value) throws Exception { - Field field = instance.getClass().getField(name); - field.setAccessible(true); - field.set(instance, value); - } - - private static final class SaveFixture implements AutoCloseable { - private final BalancedPrioritisedThreadPool pool = new BalancedPrioritisedThreadPool( - TimeUnit.MILLISECONDS.toNanos(1), Executors.defaultThreadFactory()); - private final ServerLevel level = mock(ServerLevel.class); - - private SaveFixture() throws Exception { - BalancedPrioritisedThreadPool.OrderedStreamGroup group = pool.createOrderedStreamGroup(); - ChunkTaskScheduler scheduler = mock(ChunkTaskScheduler.class); - setField(scheduler, "saveExecutor", group.createExecutor()); - setField(scheduler, "compressionExecutor", group.createExecutor()); - setField(scheduler, "chunkHolderManager", mock(ChunkHolderManager.class)); - when(level.moonrise$getChunkTaskScheduler()).thenReturn(scheduler); - } - - @Override - public void close() throws Exception { - pool.halt(true); - assertTrue(pool.joinInterruptable(5_000)); - } - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainWriteBarrierTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainWriteBarrierTest.java deleted file mode 100644 index d391d328a..000000000 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsSavedTerrainWriteBarrierTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.v26_3_R1; - -import ca.spottedleaf.concurrentutil.executor.thread.BalancedPrioritisedThreadPool; -import ca.spottedleaf.concurrentutil.util.Priority; -import ca.spottedleaf.moonrise.patches.chunk_system.io.MoonriseRegionFileIO; -import ca.spottedleaf.moonrise.patches.chunk_system.scheduling.ChunkTaskScheduler; -import net.minecraft.SharedConstants; -import net.minecraft.server.Bootstrap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import org.junit.BeforeClass; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -public class NmsSavedTerrainWriteBarrierTest { - @BeforeClass - public static void bootstrapMinecraftRegistries() { - SharedConstants.tryDetectVersion(); - Bootstrap.bootStrap(); - } - - @Test - public void checkpointHelpsSaveAndCompressionWhileNativeGenerationOccupiesTheWorker() throws Exception { - BalancedPrioritisedThreadPool pool = new BalancedPrioritisedThreadPool( - TimeUnit.MILLISECONDS.toNanos(1), Executors.defaultThreadFactory()); - BalancedPrioritisedThreadPool.OrderedStreamGroup group = pool.createOrderedStreamGroup(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue generation = group.createExecutor(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue saves = group.createExecutor(); - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue compression = group.createExecutor(); - ServerLevel level = level(saves, compression); - ChunkPos chunk = new ChunkPos(4, -9); - CountDownLatch generationEntered = new CountDownLatch(1); - CountDownLatch releaseGeneration = new CountDownLatch(1); - AtomicBoolean pendingWrite = new AtomicBoolean(true); - AtomicInteger serialized = new AtomicInteger(); - AtomicInteger compressed = new AtomicInteger(); - AtomicReference saveThread = new AtomicReference<>(); - AtomicReference compressionThread = new AtomicReference<>(); - try (MockedStatic io = mockStatic(MoonriseRegionFileIO.class)) { - io.when(() -> MoonriseRegionFileIO.getPriority(level, chunk.x(), chunk.z(), - MoonriseRegionFileIO.RegionFileType.CHUNK_DATA)) - .thenAnswer(invocation -> pendingWrite.get() ? Priority.NORMAL : Priority.COMPLETING); - generation.queueTask(() -> { - generationEntered.countDown(); - try { - assertTrue(releaseGeneration.await(5, TimeUnit.SECONDS)); - } catch (InterruptedException interruption) { - Thread.currentThread().interrupt(); - throw new AssertionError(interruption); - } - }); - pool.adjustThreadCount(1); - assertTrue(generationEntered.await(5, TimeUnit.SECONDS)); - saves.queueTask(() -> { - saveThread.set(Thread.currentThread()); - serialized.incrementAndGet(); - compression.queueTask(() -> { - compressionThread.set(Thread.currentThread()); - compressed.incrementAndGet(); - pendingWrite.set(false); - }); - }); - - NmsSavedTerrainCapture.awaitWrite(level, chunk, System.nanoTime() + TimeUnit.SECONDS.toNanos(3)); - - assertEquals(1L, releaseGeneration.getCount()); - assertEquals(1, serialized.get()); - assertEquals(1, compressed.get()); - assertFalse(pendingWrite.get()); - assertSame(Thread.currentThread(), saveThread.get()); - assertSame(Thread.currentThread(), compressionThread.get()); - } finally { - releaseGeneration.countDown(); - pool.halt(true); - assertTrue(pool.joinInterruptable(5_000)); - } - } - - @Test - public void waitingForNativeWriteHonorsTheSharedDeadline() throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkPos chunk = new ChunkPos(2, 3); - IOException failure = assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitWrite(level, chunk, System.nanoTime() - 1L)); - assertTrue(failure.getMessage().contains("Timed out")); - } - - @Test - public void interruptedNativeWriteWaitPreservesInterruption() throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkPos chunk = new ChunkPos(2, 3); - Thread.currentThread().interrupt(); - try { - IOException failure = assertThrows(IOException.class, - () -> NmsSavedTerrainCapture.awaitWrite(level, chunk, - System.nanoTime() + TimeUnit.SECONDS.toNanos(3))); - assertTrue(failure.getMessage().contains("Interrupted")); - assertTrue(Thread.currentThread().isInterrupted()); - } finally { - Thread.interrupted(); - } - } - - private static ServerLevel level(BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue saves, - BalancedPrioritisedThreadPool.OrderedStreamGroup.Queue compression) throws Exception { - ServerLevel level = mock(ServerLevel.class); - ChunkTaskScheduler scheduler = mock(ChunkTaskScheduler.class); - Field savesField = ChunkTaskScheduler.class.getField("saveExecutor"); - Field compressionField = ChunkTaskScheduler.class.getField("compressionExecutor"); - savesField.setAccessible(true); - compressionField.setAccessible(true); - savesField.set(scheduler, saves); - compressionField.set(scheduler, compression); - when(level.moonrise$getChunkTaskScheduler()).thenReturn(scheduler); - return level; - } -} diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/WorldgenTerrainHeightmapsTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/WorldgenTerrainHeightmapsTest.java index ef3b1dfc8..50bcaad1e 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/WorldgenTerrainHeightmapsTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/WorldgenTerrainHeightmapsTest.java @@ -1,7 +1,9 @@ package art.arcane.iris.platform.bukkit.nms.v26_3_R1; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeTerrainPipeline; + import art.arcane.iris.nativegen.v26_3_R1.NativeBlockTags; -import art.arcane.iris.nativegen.v26_3_R1.WorldgenTerrainHeightmaps; +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.WorldgenTerrainHeightmaps; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -70,7 +72,7 @@ public class WorldgenTerrainHeightmapsTest { public void authoringHeightmapsReadPlacedObjectsAndFluidsWithoutTerrainResolvers() { ProtoChunk chunk = terrainChunk(new ChunkPos(0, 0)); - IrisChunkGenerator.primeAuthoringHeightmaps(chunk); + NativeTerrainPipeline.primeAuthoringHeightmaps(chunk); assertEquals(LAND_CANOPY_TOP, chunk.getHeight(Heightmap.Types.WORLD_SURFACE_WG, LAND_X, LAND_Z)); assertEquals(LAND_CANOPY_TOP, chunk.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, LAND_X, LAND_Z)); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java index 4e4f08681..62eff8f25 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementRuntimeContractTest.java @@ -18,6 +18,7 @@ package art.arcane.iris.structure; +import art.arcane.volmlib.nativelib.common.structure.StructureReaderFixture; import art.arcane.iris.structure.authoring.StructureCapability; import art.arcane.iris.structure.authoring.StructureLoss; import net.minecraft.SharedConstants; @@ -33,7 +34,6 @@ import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; public class VillageImporterListPoolElementRuntimeContractTest { @@ -54,7 +54,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructureTemplatePool.Projection.RIGID ); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(towers); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(towers)); Map entry = VillageImporter.piecePoolEntry( "qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower", 1 @@ -85,7 +85,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of(Map.of("name", "minecraft:bottom"))); - assertSame(tower, resolution.physicalElement()); + assertEquals(tower.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("minecraft:pillager_outpost/watchtower", resolution.templateLocation()); assertEquals(1, resolution.omittedElements()); assertEquals("qa/pillager_outpost/piece/minecraft/pillager_outpost/watchtower", entry.get("piece")); @@ -103,7 +103,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement waystone = StructurePoolElement .legacy("dungeons_and_taverns:waystones/waystone_scaffold") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(waystone); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(waystone)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "dungeons_and_taverns:waystones", "qa/pool/dungeons_and_taverns/waystones", @@ -116,7 +116,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(waystone, resolution.physicalElement()); + assertEquals(waystone.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("dungeons_and_taverns:waystones/waystone_scaffold", resolution.templateLocation()); assertEquals(VillageImporter.PoolMemberDisposition.EMPTY, normalization.disposition()); assertEquals(true, normalization.poolEntry().get("empty")); @@ -130,7 +130,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement orphan = StructurePoolElement .legacy("test:orphan_house") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(orphan); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(orphan)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:orphan_pool", "qa/pool/test/orphan_pool", @@ -143,7 +143,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 37, List.of()); - assertSame(orphan, resolution.physicalElement()); + assertEquals(orphan.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals("test:orphan_house", resolution.templateLocation()); assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition()); assertTrue(normalization.poolEntry().isEmpty()); @@ -160,7 +160,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement start = StructurePoolElement .legacy("test:air_start") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(start); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(start)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:start", "qa/pool/test/start", @@ -173,7 +173,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(start, resolution.physicalElement()); + assertEquals(start.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, normalization.disposition()); assertEquals("qa/piece/test/air_start", normalization.poolEntry().get("piece")); assertTrue(normalization.losses().isEmpty()); @@ -184,7 +184,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement inert = StructurePoolElement .legacy("test:mixed_air_inert") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(inert); + VillageImporter.PoolElementResolution resolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(inert)); VillageImporter.PoolMemberNormalization normalization = VillageImporter.normalizePoolMember( "test:mixed", "qa/pool/test/mixed", @@ -197,7 +197,7 @@ public class VillageImporterListPoolElementRuntimeContractTest { 0, List.of()); - assertSame(inert, resolution.physicalElement()); + assertEquals(inert.getClass().getSimpleName(), resolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.OMITTED, normalization.disposition()); assertTrue(normalization.poolEntry().isEmpty()); assertEquals("connectorless_all_air_mixed_member_omitted", @@ -214,8 +214,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement nonAirElement = StructurePoolElement .legacy("test:mixed_non_air_primary") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement); - VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement); + VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(allAirElement)); + VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(nonAirElement)); VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember( "test:mixed", "qa/pool/test/mixed", @@ -239,8 +239,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { 41, List.of()); - assertSame(allAirElement, allAirResolution.physicalElement()); - assertSame(nonAirElement, nonAirResolution.physicalElement()); + assertEquals(allAirElement.getClass().getSimpleName(), allAirResolution.physicalElement().typeName()); + assertEquals(nonAirElement.getClass().getSimpleName(), nonAirResolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition()); assertEquals("qa/piece/test/mixed_all_air_primary", allAir.poolEntry().get("piece")); @@ -257,8 +257,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { StructurePoolElement nonAirElement = StructurePoolElement .legacy("test:non_air_primary") .apply(StructureTemplatePool.Projection.RIGID); - VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(allAirElement); - VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(nonAirElement); + VillageImporter.PoolElementResolution allAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(allAirElement)); + VillageImporter.PoolElementResolution nonAirResolution = VillageImporter.resolvePoolElement(StructureReaderFixture.element(nonAirElement)); VillageImporter.PoolMemberNormalization allAir = VillageImporter.normalizePoolMember( "test:primary", "qa/pool/test/primary", @@ -282,8 +282,8 @@ public class VillageImporterListPoolElementRuntimeContractTest { 29, List.of()); - assertSame(allAirElement, allAirResolution.physicalElement()); - assertSame(nonAirElement, nonAirResolution.physicalElement()); + assertEquals(allAirElement.getClass().getSimpleName(), allAirResolution.physicalElement().typeName()); + assertEquals(nonAirElement.getClass().getSimpleName(), nonAirResolution.physicalElement().typeName()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, allAir.disposition()); assertEquals(VillageImporter.PoolMemberDisposition.PHYSICAL, nonAir.disposition()); assertEquals("qa/piece/test/all_air_primary", allAir.poolEntry().get("piece")); diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java similarity index 96% rename from adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java rename to adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java index 23c933745..d7b2bac8e 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/nativegen/NativeStructureVolumeIndexTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndexTest.java @@ -1,8 +1,14 @@ -package art.arcane.iris.nativegen.v26_3_R1; +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.structure.nativegen.IrisStructureVolumePolicy; + +import art.arcane.volmlib.nativelib.v26_3_R1.terrain.NativeStructureVolumeSource; + +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; import art.arcane.volmlib.util.collection.KList; import net.minecraft.SharedConstants; @@ -63,7 +69,7 @@ public class NativeStructureVolumeIndexTest { StructureStart start = swampHut(0, 0, 4, 6); BoundingBox bounds = start.getPieces().getFirst().getBoundingBox(); - KList volumes = NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, start); + KList volumes = NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, start); assertEquals(1, volumes.size()); NativeStructureVolume volume = volumes.getFirst(); @@ -78,8 +84,8 @@ public class NativeStructureVolumeIndexTest { @Test public void invalidStartsContributeNoVolumes() { - assertNull(NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, StructureStart.INVALID_START)); - assertNull(NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, null)); + assertNull(NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, StructureStart.INVALID_START)); + assertNull(NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, null)); } @Test @@ -174,9 +180,10 @@ public class NativeStructureVolumeIndexTest { listeners.remove(invocation.getArgument(0)); return null; }).when(engine).removeGenerationRuntimeRetirementListener(any()); - NativeStructureVolumeIndex.Context context = new NativeStructureVolumeIndex.Context( + NativeStructureVolumeSource.Context context = new NativeStructureVolumeSource.Context( null, null, null, null, () -> null, () -> null, () -> null); - NativeStructureVolumeIndex.install(engine, context); + NativeStructureVolumeIndex.install(engine, + new NativeStructureVolumeSource<>(context, IrisStructureVolumePolicy.INSTANCE)::volumesAt); try { assertEquals(1, listeners.size()); IntConsumer original = listeners.iterator().next(); @@ -501,7 +508,7 @@ public class NativeStructureVolumeIndexTest { if (chunkX != originChunkX || chunkZ != originChunkZ) { return NativeStructureVolume.NONE; } - return NativeStructureVolumeIndex.appendPieces(null, STRUCTURE_KEY, swampHut(chunkX, chunkZ, 4, 6)); + return NativeStructureVolumeSource.appendPieces(null, STRUCTURE_KEY, swampHut(chunkX, chunkZ, 4, 6)); } } diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java new file mode 100644 index 000000000..2a816a9aa --- /dev/null +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/volmlib/nativelib/common/structure/StructureReaderFixture.java @@ -0,0 +1,12 @@ +package art.arcane.volmlib.nativelib.common.structure; + +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; + +public final class StructureReaderFixture { + private StructureReaderFixture() { + } + + public static NativeStructureReader.Element element(Object handle) { + return new ReflectiveStructureReader.ElementView(handle, null, ReflectiveStructureReader::readDefaultConnectors); + } +} diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java index f3fc8ab3f..aefe52ff1 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java @@ -56,7 +56,7 @@ import art.arcane.volmlib.util.director.help.DirectorMiniMenu; import art.arcane.volmlib.util.localization.BukkitLanguageSwitcher; import art.arcane.iris.integration.MultiverseCoreLink; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.ServerShutdownBoundary; +import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary; import art.arcane.iris.studio.view.BukkitGuiHost; import art.arcane.iris.studio.view.PregeneratorJob; import art.arcane.iris.platform.bukkit.BoardSVC; @@ -782,7 +782,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { reconcileStartupWorlds(); } IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); - // The mantle stores block values as PlatformBlockState, so a BlockData retention can never + // The mantle stores block values as NativeBlockState, so a BlockData retention can never // match a slice type; the block-state slice is deliberately never retainable (regenerable, huge). IrisToolbelt.retainMantleDataForSlice(TreeBlockMaterial.class.getCanonicalName()); }); @@ -1270,6 +1270,7 @@ public class Iris extends VolmitPlugin implements Listener, ReloadAware { } else { try { INMS.get().releasePluginClassLoaderClose(); + SlimJar.closeNativeProviders(); } catch (Throwable failure) { SHUTDOWN_ERRORS.println("[Iris] Failed to release the plugin class loader after runtime cleanup."); failure.printStackTrace(SHUTDOWN_ERRORS); diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisBootstrap.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisBootstrap.java index 72581c5d3..1734ce4aa 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisBootstrap.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisBootstrap.java @@ -9,7 +9,7 @@ import art.arcane.iris.world.lifecycle.WorldReplacementBootstrap; import art.arcane.iris.world.lifecycle.WorldReplacementBootstrapMarker; import art.arcane.iris.pack.DefaultPackBootstrapProvisioner; import art.arcane.iris.pack.DefaultPackBootstrapProvisioner.BootstrapRequest; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import io.papermc.paper.ServerBuildInfo; import art.arcane.iris.pack.DefaultPackBootstrapProvisioner.ProvisionResult; import art.arcane.iris.platform.bootstrap.SlimJar; @@ -46,6 +46,7 @@ public final class IrisBootstrap implements PluginBootstrap { private static void loadRuntimeLibraries(BootstrapContext context) { SlimJar.loadBootstrap( context.getDataDirectory().resolve("cache").resolve("libraries"), + ServerBuildInfo.buildInfo().minecraftVersionId(), new SlimJar.BootstrapLogger() { @Override public void info(String message) { diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisPluginLoader.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisPluginLoader.java index 86b3d95ae..5f3b93e39 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisPluginLoader.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/IrisPluginLoader.java @@ -1,6 +1,7 @@ package art.arcane.iris; import art.arcane.iris.platform.bootstrap.SlimJar; +import io.papermc.paper.ServerBuildInfo; import io.papermc.paper.plugin.bootstrap.PluginProviderContext; import io.papermc.paper.plugin.loader.PluginClasspathBuilder; import io.papermc.paper.plugin.loader.PluginLoader; @@ -18,7 +19,7 @@ public final class IrisPluginLoader implements PluginLoader { public void classloader(PluginClasspathBuilder classpathBuilder) { PluginProviderContext context = classpathBuilder.getContext(); Path libraryRoot = context.getDataDirectory().resolve("cache").resolve("libraries"); - SlimJar.loadBootstrap(libraryRoot, new SlimJar.BootstrapLogger() { + SlimJar.loadBootstrap(libraryRoot, ServerBuildInfo.buildInfo().minecraftVersionId(), new SlimJar.BootstrapLogger() { @Override public void info(String message) { context.getLogger().info(message); @@ -35,7 +36,9 @@ public final class IrisPluginLoader implements PluginLoader { } }); for (Path library : relocatedLibraries(libraryRoot)) { - classpathBuilder.addLibrary(new JarLibrary(library)); + if (!library.startsWith(libraryRoot.resolve("com/github/VolmitSoftware/VolmLib"))) { + classpathBuilder.addLibrary(new JarLibrary(library)); + } } } diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java index 997dc4c12..9e6ac7ec9 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java @@ -21,7 +21,7 @@ package art.arcane.iris.command; import com.google.gson.JsonObject; import art.arcane.iris.Iris; import art.arcane.iris.pack.datapack.ServerConfigurator; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.world.runtime.ChunkClearer; import art.arcane.iris.generation.validation.GoldenHashScanner; import art.arcane.iris.world.runtime.InPlaceChunkRegenerator; diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandObject.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandObject.java index 5bc470cb3..16ed13bf0 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandObject.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandObject.java @@ -44,7 +44,7 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.data.Cuboid; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.iris.command.specialhandlers.NullableDimensionHandler; @@ -218,7 +218,7 @@ public class CommandObject implements DirectorExecutor { } @Override - public void set(int x, int y, int z, PlatformBlockState s) { + public void set(int x, int y, int z, NativeBlockState s) { BlockData d = (BlockData) s.nativeHandle(); Block block = world.getBlockAt(x, y, z); @@ -242,7 +242,7 @@ public class CommandObject implements DirectorExecutor { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return BukkitBlockState.of(world.getBlockAt(x, y, z).getBlockData()); } diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandSVC.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandSVC.java index 1be4cae3e..b8910be9b 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandSVC.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandSVC.java @@ -20,7 +20,7 @@ package art.arcane.iris.command; import art.arcane.iris.Iris; import art.arcane.iris.configuration.IrisSettings; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.localization.C; import art.arcane.iris.platform.bukkit.plugin.IrisService; import art.arcane.iris.platform.bukkit.plugin.VolmitSender; diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandWhat.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandWhat.java index 1c1b194a1..e9a3c2e2e 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandWhat.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandWhat.java @@ -111,7 +111,7 @@ public class CommandWhat implements DirectorExecutor { if (key == null || key.getKey().equals("custom")) { try { - commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_DATA_PACK_BIOME_ID, MessageArgument.untrusted("value", INMS.get().getTrueBiomeBaseKey(location)), MessageArgument.untrusted("value2", INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(location))))); + commandSender.sendMessage(IrisLanguage.text(BukkitCommandMessagesExtended.COMMAND_WHAT_DATA_PACK_BIOME_ID, MessageArgument.untrusted("value", INMS.get().getTrueBiomeBaseKey(location)), MessageArgument.untrusted("value2", INMS.get().getBiomeId(location)))); } catch (Throwable ee) { Iris.reportError(ee); } diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/view/BukkitVisionOverlay.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/view/BukkitVisionOverlay.java index 4ea0a13fb..3bcc4956c 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/view/BukkitVisionOverlay.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/view/BukkitVisionOverlay.java @@ -18,6 +18,8 @@ package art.arcane.iris.studio.view; +import art.arcane.volmlib.nativelib.view.WorldMarker; + import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.studio.render.RenderType; @@ -45,7 +47,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; -import static art.arcane.iris.platform.bukkit.registry.Attributes.MAX_HEALTH; +import static art.arcane.iris.platform.registry.Attributes.MAX_HEALTH; public final class BukkitVisionOverlay implements GuiOverlay { private final Engine engine; @@ -54,7 +56,7 @@ public final class BukkitVisionOverlay implements GuiOverlay { private final AtomicBoolean playerRefreshQueued = new AtomicBoolean(); private final AtomicLong teleportSequence = new AtomicLong(); private final AtomicReference latestTeleport = new AtomicReference<>(); - private volatile List playerMarkers = List.of(); + private volatile List playerMarkers = List.of(); public BukkitVisionOverlay(Engine engine, UUID openerId) { this.engine = engine; @@ -66,7 +68,7 @@ public final class BukkitVisionOverlay implements GuiOverlay { * built by a server thread. */ @Override - public List players() { + public List players() { queuePlayerRefresh(); return playerMarkers; } @@ -78,10 +80,10 @@ public final class BukkitVisionOverlay implements GuiOverlay { boolean scheduled = J.runGlobal(() -> { try { - List markers = new ArrayList<>(); + List markers = new ArrayList<>(); for (Player player : BukkitWorldBinding.players(engine.getWorld())) { Location at = player.getLocation(); - markers.add(GuiMarker.player(player.getName(), at.getX(), at.getZ())); + markers.add(WorldMarker.player(player.getName(), at.getX(), at.getZ())); } playerMarkers = List.copyOf(markers); } finally { @@ -95,7 +97,7 @@ public final class BukkitVisionOverlay implements GuiOverlay { } @Override - public void requestEntities(Consumer> sink) { + public void requestEntities(Consumer> sink) { J.runGlobal(() -> { IrisWorld target = engine.getWorld(); World world = BukkitWorldBinding.world(target); @@ -116,7 +118,7 @@ public final class BukkitVisionOverlay implements GuiOverlay { return; } - List collected = Collections.synchronizedList(new ArrayList<>(living.size())); + List collected = Collections.synchronizedList(new ArrayList<>(living.size())); AtomicInteger pending = new AtomicInteger(living.size()); Runnable complete = () -> { if (pending.decrementAndGet() == 0) { @@ -142,14 +144,14 @@ public final class BukkitVisionOverlay implements GuiOverlay { }); } - private GuiMarker marker(LivingEntity entity, Location at) { + private WorldMarker marker(LivingEntity entity, Location at) { String label = Form.capitalizeWords(entity.getType().name().toLowerCase(Locale.ROOT).replaceAll("\\Q_\\E", " ")); double maxHealth = 0; try { maxHealth = entity.getAttribute(MAX_HEALTH).getValue(); } catch (Throwable ignored) { } - return GuiMarker.entity(label, at.getX(), at.getY(), at.getZ(), entity.getHealth(), maxHealth); + return WorldMarker.entity(label, at.getX(), at.getY(), at.getZ(), entity.getHealth(), maxHealth); } @Override diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSVC.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSVC.java index 0dac77d05..8e530d127 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSVC.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSVC.java @@ -67,8 +67,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; -import static art.arcane.iris.platform.bukkit.registry.Particles.CRIT_MAGIC; -import static art.arcane.iris.platform.bukkit.registry.Particles.REDSTONE; +import static art.arcane.iris.platform.registry.Particles.CRIT_MAGIC; +import static art.arcane.iris.platform.registry.Particles.REDSTONE; public class WandSVC implements IrisService { private static final int MS_PER_TICK = Integer.parseInt(System.getProperty("iris.ms_per_tick", "30")); diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSelection.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSelection.java index d523bd321..bfa90c333 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSelection.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/studio/wand/WandSelection.java @@ -26,7 +26,7 @@ import org.bukkit.entity.Player; import java.awt.Color; -import static art.arcane.iris.platform.bukkit.registry.Particles.REDSTONE; +import static art.arcane.iris.platform.registry.Particles.REDSTONE; public class WandSelection { private static final double STEP = 0.10; diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/BukkitWorldReconciler.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/BukkitWorldReconciler.java index ec96babae..735f382ac 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/BukkitWorldReconciler.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/BukkitWorldReconciler.java @@ -18,6 +18,8 @@ package art.arcane.iris.world; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.server.NativeServerDiagnostics; import art.arcane.iris.pack.datapack.ServerConfigurator; import art.arcane.iris.Iris; @@ -413,19 +415,8 @@ public final class BukkitWorldReconciler { } private static boolean containsCreateWorldUnsupportedOperation(Throwable throwable) { - Throwable cursor = throwable; - while (cursor != null) { - if (cursor instanceof UnsupportedOperationException || cursor instanceof IllegalStateException) { - for (StackTraceElement element : cursor.getStackTrace()) { - if ("org.bukkit.craftbukkit.CraftServer".equals(element.getClassName()) - && "createWorld".equals(element.getMethodName())) { - return true; - } - } - } - cursor = cursor.getCause(); - } - return false; + return NativeAdapters.find(NativeServerDiagnostics.class) + .map(diagnostics -> diagnostics.isRejectedWorldCreation(throwable)).orElse(false); } interface Backend { diff --git a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/runtime/BukkitEnginePlatformHooks.java b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/runtime/BukkitEnginePlatformHooks.java index 28b058d78..c1559165a 100644 --- a/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/runtime/BukkitEnginePlatformHooks.java +++ b/adapters/bukkit/plugin/src/main/java/art/arcane/iris/world/runtime/BukkitEnginePlatformHooks.java @@ -36,7 +36,7 @@ import art.arcane.iris.world.history.SavedTerrainChunk; import java.util.concurrent.CompletableFuture; import art.arcane.iris.generation.runtime.EngineMode; import art.arcane.iris.generation.runtime.EnginePlatformHooks; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; import art.arcane.iris.world.IrisWorld; diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/build.gradle b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/build.gradle new file mode 100644 index 000000000..763e61a20 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'java' +} + +version = '1.0.0' + +repositories { + mavenCentral() + maven { url = 'https://repo.papermc.io/repository/maven-public/' } +} + +dependencies { + compileOnly files(providers.gradleProperty('irisJar').get()) + compileOnly 'io.papermc.paper:paper-api:26.1.2.build.74-stable' +} + +java { + toolchain.languageVersion = JavaLanguageVersion.of(25) +} + +sourceSets { + main { + java.srcDirs = ['src'] + resources.srcDirs = ['resources'] + } +} + +tasks.withType(JavaCompile).configureEach { + options.compilerArgs += '-parameters' +} + +tasks.named('jar') { + archiveBaseName = 'IrisNativeLifecycleFixture' +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/resources/plugin.yml b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/resources/plugin.yml new file mode 100644 index 000000000..965cbc3fd --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/resources/plugin.yml @@ -0,0 +1,11 @@ +name: IrisNativeLifecycleFixture +version: 1.0.0 +main: art.arcane.iris.fixture.NativeLifecycleFixture +api-version: '26.1.2' +depend: [Iris] +commands: + irislifecycle: + permission: irislifecycle.run +permissions: + irislifecycle.run: + default: op diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/settings.gradle b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/settings.gradle new file mode 100644 index 000000000..facdaf589 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'iris-native-lifecycle-fixture' diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/src/art/arcane/iris/fixture/NativeLifecycleFixture.java b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/src/art/arcane/iris/fixture/NativeLifecycleFixture.java new file mode 100644 index 000000000..3851d2289 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle-fixture/src/art/arcane/iris/fixture/NativeLifecycleFixture.java @@ -0,0 +1,252 @@ +package art.arcane.iris.fixture; + +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.iris.world.runtime.RuntimeInjection; +import art.arcane.iris.world.IrisCreator; +import art.arcane.iris.world.IrisToolbelt; +import org.bukkit.Material; +import org.bukkit.block.Biome; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainAccess; +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; +import art.arcane.volmlib.nativelib.terrain.NativeWorldRuntime; +import art.arcane.volmlib.nativelib.terrain.WorldRuntimeExecution; +import art.arcane.volmlib.nativelib.terrain.WorldRuntimeOptions; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.concurrent.CompletableFuture; +import java.util.List; +import java.util.logging.Level; + +public final class NativeLifecycleFixture extends JavaPlugin implements WorldRuntimeExecution { + private static final NamespacedKey WORLD_KEY = NamespacedKey.minecraft("native_lifecycle_probe"); + private static final NamespacedKey TERRAIN_KEY = new NamespacedKey("iris", "native_terrain_probe"); + private static final long WORLD_SEED = 78264193L; + private NativeWorldRuntime runtime; + + @Override + public void onEnable() { + runtime = NativeAdapters.require(NativeWorldRuntime.class); + NativeTerrainAccess terrain = NativeAdapters.require(NativeTerrainAccess.class); + getLogger().info("Native lifecycle ready: " + runtime.description() + + "; terrain provider=" + terrain.getClass().getName()); + } + + @Override + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + return new ChunkGenerator() {}; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] arguments) { + if (arguments.length != 1) { + sender.sendMessage("Use /irislifecycle create|unload|status|structures|terrain-create|terrain-check|terrain-unload"); + return true; + } + try { + switch (arguments[0]) { + case "create" -> create(sender); + case "unload" -> unload(sender); + case "structures" -> structures(sender); + case "terrain-create" -> createTerrain(sender); + case "terrain-check" -> checkTerrain(sender); + case "terrain-unload" -> unloadTerrain(sender); + case "status" -> sender.sendMessage("NATIVE_STATUS available=" + runtime.available() + + " loaded=" + (Bukkit.getWorld(WORLD_KEY) != null)); + default -> throw new IllegalArgumentException("Unknown lifecycle operation"); + } + } catch (Throwable failure) { + fail(sender, failure); + } + return true; + } + + private void create(CommandSender sender) throws ReflectiveOperationException { + if (!runtime.available() || Bukkit.getWorld(WORLD_KEY) != null) { + throw new IllegalStateException("Native runtime unavailable or probe world already loaded"); + } + RuntimeInjection.installIfDeferred(); + WorldRuntimeOptions options = new WorldRuntimeOptions(WORLD_KEY.getKey(), WORLD_KEY, + World.Environment.NORMAL, null, getName() + ":runtime", false, WORLD_SEED, false, + Bukkit.getWorlds().getFirst().getWorldFolder()); + World world = runtime.create(options, this); + if (!WORLD_KEY.equals(world.getKey()) || world.getSeed() != WORLD_SEED) { + throw new IllegalStateException("World identity or seed differs from requested options"); + } + world.getChunkAtAsync(0, 0, true).whenComplete((chunk, failure) -> runGlobal(() -> { + if (failure != null) { + fail(sender, failure); + return; + } + if (chunk == null || chunk.getWorld() != world || !chunk.isLoaded()) { + fail(sender, new IllegalStateException("Native runtime world failed chunk generation")); + return; + } + sender.sendMessage("NATIVE_CREATED seed=" + world.getSeed() + " chunk=true"); + })); + } + + private void createTerrain(CommandSender sender) { + if (Bukkit.getWorld(TERRAIN_KEY) != null) { + throw new IllegalStateException("Terrain world is already loaded"); + } + runAsync(() -> { + try { + new IrisCreator().name(TERRAIN_KEY.toString()).dimension("native-terrain") + .seed(WORLD_SEED).create(); + runGlobal(() -> checkTerrain(sender)); + } catch (Throwable failure) { + runGlobal(() -> fail(sender, failure)); + } + }); + } + + private void checkTerrain(CommandSender sender) { + World world = Bukkit.getWorld(TERRAIN_KEY); + if (world == null || world.getSeed() != WORLD_SEED || !IrisToolbelt.isIrisWorld(world) + || IrisToolbelt.access(world).getEngine() == null) { + throw new IllegalStateException("Iris terrain engine or world identity is absent"); + } + world.getChunkAtAsync(2, -3, true).whenComplete((chunk, failure) -> runGlobal(() -> { + if (failure != null) { + fail(sender, failure); + return; + } + try { + int columns = 0; + for (int x = 32; x < 48; x += 5) { + for (int z = -48; z < -32; z += 5) { + int y = world.getHighestBlockYAt(x, z); + if (y < 90 || y > 100 || world.getBlockAt(x, y, z).getType() != Material.LIME_WOOL + || world.getBlockAt(x, y - 3, z).getType() != Material.GREEN_CONCRETE + || world.getBiome(x, y, z) != Biome.PLAINS) { + throw new IllegalStateException("Terrain column differs at " + x + "," + y + "," + z + + ": " + world.getBlockAt(x, y, z).getType()); + } + columns++; + } + } + sender.sendMessage("NATIVE_TERRAIN seed=" + world.getSeed() + " columns=" + columns + " biome=plains"); + } catch (Throwable assertion) { + fail(sender, assertion); + } + })); + } + + private void unloadTerrain(CommandSender sender) { + World world = Bukkit.getWorld(TERRAIN_KEY); + if (world == null) { + throw new IllegalStateException("Terrain world is absent"); + } + runtime.unload(world, true, this).whenComplete((unloaded, failure) -> runGlobal(() -> { + if (failure != null) { + fail(sender, failure); + } else if (!Boolean.TRUE.equals(unloaded) || Bukkit.getWorld(TERRAIN_KEY) != null) { + fail(sender, new IllegalStateException("Terrain world did not unload")); + } else { + sender.sendMessage("NATIVE_TERRAIN_UNLOADED true"); + } + })); + } + + private void structures(CommandSender sender) throws Exception { + NativeStructureReader reader = NativeAdapters.require(NativeStructureReader.class); + List templates = reader.templates(); + if (!templates.contains("minecraft:village/plains/town_centers/plains_fountain_01")) { + throw new IllegalStateException("Vanilla village template was not enumerated"); + } + NativeStructureReader.Session session = reader.open(); + NativeStructureReader.Structure village = session.structure("minecraft:village_plains"); + if (village == null || !village.jigsaw() || village.maxDepth() <= 0 + || village.maxDistanceFromCenter() <= 0) { + throw new IllegalStateException("Vanilla village metadata is missing"); + } + NativeStructureReader.Pool pool = session.pool(village.startPoolKey()); + if (pool == null || !"minecraft:empty".equals(pool.fallbackKey()) || pool.entries().isEmpty()) { + throw new IllegalStateException("Vanilla village start pool is missing"); + } + int connectorCount = 0; + for (NativeStructureReader.Entry entry : pool.entries()) { + NativeStructureReader.Element element = entry.element(); + if (entry.weight() <= 0 || element == null || element.templateLocation() == null) { + throw new IllegalStateException("Vanilla village pool element is invalid"); + } + NativeStructureReader.ConnectorSet connectors = element.connectors(() -> 91L); + if (connectors.status() != NativeStructureReader.ConnectorStatus.AVAILABLE) { + throw new IllegalStateException("Vanilla village connectors unavailable"); + } + for (NativeStructureReader.Connector connector : connectors.connectors()) { + NativeStructureReader.ConnectorData data = connector.read(); + if (data.front() == null || data.top() == null || data.pool() == null + || data.metadata().finalState() == null) { + throw new IllegalStateException("Vanilla village connector metadata is incomplete"); + } + connectorCount++; + } + } + if (connectorCount == 0) { + throw new IllegalStateException("Vanilla village pool has no connectors"); + } + sender.sendMessage("NATIVE_STRUCTURES templates=" + templates.size() + " village=true connectors=" + connectorCount); + } + + private void unload(CommandSender sender) { + World world = Bukkit.getWorld(WORLD_KEY); + if (world == null) { + throw new IllegalStateException("Probe world is absent"); + } + runtime.unload(world, true, this).whenComplete((unloaded, failure) -> runGlobal(() -> { + if (failure != null) { + fail(sender, failure); + } else if (!Boolean.TRUE.equals(unloaded) || Bukkit.getWorld(WORLD_KEY) != null) { + fail(sender, new IllegalStateException("Native unload did not remove the world")); + } else { + sender.sendMessage("NATIVE_UNLOADED true"); + } + })); + } + + public boolean regionized() { + return false; + } + + public boolean primaryThread() { + return Bukkit.isPrimaryThread(); + } + + public CompletableFuture runGlobal(Runnable task) { + CompletableFuture result = new CompletableFuture<>(); + Runnable wrapped = () -> { + try { + task.run(); + result.complete(null); + } catch (Throwable failure) { + result.completeExceptionally(failure); + } + }; + if (Bukkit.isPrimaryThread()) { + wrapped.run(); + } else { + Bukkit.getScheduler().runTask(this, wrapped); + } + return result; + } + + public CompletableFuture runAsync(Runnable task) { + return CompletableFuture.runAsync(task); + } + + public void reportFailure(String message, Throwable failure) { + getLogger().log(Level.SEVERE, message, failure); + } + + private void fail(CommandSender sender, Throwable failure) { + reportFailure("Native lifecycle assertion failed", failure); + sender.sendMessage("NATIVE_FAILED " + failure.getMessage()); + } +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle.mjs b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle.mjs new file mode 100644 index 000000000..99645834e --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-lifecycle.mjs @@ -0,0 +1,22 @@ +export default { + name: 'iris-native-lifecycle', + description: 'Verify native structure metadata, runtime world creation, seeded chunk generation, and asynchronous unload.', + async run(context) { + await context.step('native world capability is available', async () => { + await context.command('/irislifecycle status', /NATIVE_STATUS available=true loaded=false/, 10000) + }) + await context.step('read vanilla templates, village pools, and connector metadata', async () => { + const response = await context.command('/irislifecycle structures', /NATIVE_STRUCTURES|NATIVE_FAILED/, 30000) + context.expect(/NATIVE_STRUCTURES templates=\d+ village=true connectors=[1-9]\d*/.test(response), response) + }) + await context.step('create a world with the requested seed and generate a chunk', async () => { + const response = await context.command('/irislifecycle create', /NATIVE_CREATED|NATIVE_FAILED/, 90000) + context.expect(response.includes('NATIVE_CREATED seed=78264193 chunk=true'), response) + }) + await context.step('unload and detach the created world', async () => { + const response = await context.command('/irislifecycle unload', /NATIVE_UNLOADED|NATIVE_FAILED/, 90000) + context.expect(response.includes('NATIVE_UNLOADED true'), response) + await context.command('/irislifecycle status', /NATIVE_STATUS available=true loaded=false/, 10000) + }) + } +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/biomes/flat.json b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/biomes/flat.json new file mode 100644 index 000000000..ecc940922 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/biomes/flat.json @@ -0,0 +1,23 @@ +{ + "name": "Flat", + "derivative": "minecraft:plains", + "vanillaDerivative": "minecraft:plains", + "layers": [ + { + "minHeight": 1, + "maxHeight": 1, + "palette": [ + { + "block": "minecraft:lime_wool" + } + ] + } + ], + "generators": [ + { + "generator": "flat", + "min": 96, + "max": 96 + } + ] +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/dimensions/native-terrain.json b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/dimensions/native-terrain.json new file mode 100644 index 000000000..22f965b36 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/dimensions/native-terrain.json @@ -0,0 +1,28 @@ +{ + "name": "Native Terrain", + "version": 1, + "mode": { + "type": "OVERWORLD" + }, + "regions": [ + "flat" + ], + "focusRegion": "flat", + "focus": "flat", + "landChance": 1, + "environment": "NORMAL", + "dimensionHeight": { + "min": -64, + "max": 320 + }, + "logicalHeight": 384, + "fluidHeight": 0, + "bedrock": false, + "rockPalette": { + "palette": [ + { + "block": "minecraft:green_concrete" + } + ] + } +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/generators/flat.json b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/generators/flat.json new file mode 100644 index 000000000..7cd98b811 --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/generators/flat.json @@ -0,0 +1,15 @@ +{ + "interpolator": { + "function": "NONE", + "horizontalScale": 1 + }, + "seed": 310, + "composite": [ + { + "seed": 310, + "style": { + "style": "FLAT" + } + } + ] +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/regions/flat.json b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/regions/flat.json new file mode 100644 index 000000000..d6fff925f --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-terrain-pack/regions/flat.json @@ -0,0 +1,12 @@ +{ + "name": "Flat", + "landBiomes": [ + "flat" + ], + "seaBiomes": [ + "flat" + ], + "shoreBiomes": [ + "flat" + ] +} diff --git a/adapters/bukkit/plugin/src/test/gameplay/native-terrain.mjs b/adapters/bukkit/plugin/src/test/gameplay/native-terrain.mjs new file mode 100644 index 000000000..3beedb5ed --- /dev/null +++ b/adapters/bukkit/plugin/src/test/gameplay/native-terrain.mjs @@ -0,0 +1,18 @@ +export default { + name: 'iris-native-terrain', + description: 'Generate an Iris pack through the shared native adapter and verify terrain, biome, seed, and unload.', + async run(context) { + await context.step('create a real Iris terrain world', async () => { + const response = await context.command('/irislifecycle terrain-create', /NATIVE_TERRAIN seed=|NATIVE_FAILED/, 180000) + context.expect(response.includes('NATIVE_TERRAIN seed=78264193 columns=16 biome=plains'), response) + }) + await context.step('read generated terrain again', async () => { + const response = await context.command('/irislifecycle terrain-check', /NATIVE_TERRAIN seed=|NATIVE_FAILED/, 60000) + context.expect(response.includes('NATIVE_TERRAIN seed=78264193 columns=16 biome=plains'), response) + }) + await context.step('save and unload the terrain world', async () => { + const response = await context.command('/irislifecycle terrain-unload', /NATIVE_TERRAIN_UNLOADED|NATIVE_FAILED/, 90000) + context.expect(response.includes('NATIVE_TERRAIN_UNLOADED true'), response) + }) + } +} diff --git a/adapters/bukkit/plugin/src/test/java/art/arcane/iris/command/CommandObjectCustomBlockPlacementTest.java b/adapters/bukkit/plugin/src/test/java/art/arcane/iris/command/CommandObjectCustomBlockPlacementTest.java index 387f6a80c..6e23fd358 100644 --- a/adapters/bukkit/plugin/src/test/java/art/arcane/iris/command/CommandObjectCustomBlockPlacementTest.java +++ b/adapters/bukkit/plugin/src/test/java/art/arcane/iris/command/CommandObjectCustomBlockPlacementTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import org.bukkit.Material; import org.bukkit.World; @@ -37,7 +37,7 @@ public class CommandObjectCustomBlockPlacementTest { BlockData previousCustom = IrisCustomData.of(previous, new Identifier("craftengine", "test/lamp[lit=false]")); Identifier id = new Identifier("craftengine", "test/lamp[lit=true]"); BlockData base = mock(BlockData.class); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.nativeHandle()).thenReturn(IrisCustomData.of(base, id)); ExternalDataSVC external = mock(ExternalDataSVC.class); when(external.captureBlockData(previous)).thenReturn(previousCustom); @@ -65,7 +65,7 @@ public class CommandObjectCustomBlockPlacementTest { when(target.getType()).thenReturn(Material.STONE); BlockData base = mock(BlockData.class); Identifier id = new Identifier("craftengine", "test/chair[variant=alpha]"); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.nativeHandle()).thenReturn(IrisCustomData.of(base, id)); ExternalDataSVC external = mock(ExternalDataSVC.class); try (MockedStatic services = mockStatic(IrisServices.class)) { diff --git a/adapters/bukkit/plugin/src/test/java/art/arcane/iris/studio/wand/WandSelectionTest.java b/adapters/bukkit/plugin/src/test/java/art/arcane/iris/studio/wand/WandSelectionTest.java index 919b46ce2..8bf2daadf 100644 --- a/adapters/bukkit/plugin/src/test/java/art/arcane/iris/studio/wand/WandSelectionTest.java +++ b/adapters/bukkit/plugin/src/test/java/art/arcane/iris/studio/wand/WandSelectionTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.studio.wand; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import art.arcane.volmlib.util.data.Cuboid; import art.arcane.volmlib.util.math.M; import org.bukkit.Location; diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClient.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClient.java index 7774f2fb7..fde2f7583 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClient.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClient.java @@ -4,11 +4,10 @@ import art.arcane.iris.spi.protocol.IrisMessage; import art.arcane.iris.spi.protocol.IrisMessageCodec; import art.arcane.iris.spi.protocol.IrisProtocol; import art.arcane.iris.spi.protocol.ProtocolException; -import net.minecraft.resources.Identifier; public final class IrisClient { - public static final Identifier HUD_ELEMENT_ID = Identifier.fromNamespaceAndPath("irisworldgen", "pregen_hud"); - public static final Identifier KEYBIND_CATEGORY_ID = Identifier.fromNamespaceAndPath("irisworldgen", "iris"); + public static final String HUD_ELEMENT_ID = "irisworldgen:pregen_hud"; + public static final String KEYBIND_CATEGORY_ID = "irisworldgen:iris"; public static final String KEYBIND_TOGGLE_HUD = "key.irisworldgen.toggle_pregen_hud"; public static final String KEYBIND_OPEN_MAP = "key.irisworldgen.open_vision_map"; public static final String KEYBIND_TOGGLE_WHAT = "key.irisworldgen.toggle_what_overlay"; diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientHud.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientHud.java index 1df3a5c30..aa93bf17c 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientHud.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientHud.java @@ -1,9 +1,18 @@ package art.arcane.iris.client; -import net.minecraft.client.gui.GuiGraphicsExtractor; +import art.arcane.volmlib.nativelib.client.ClientGraphics; +import art.arcane.volmlib.nativelib.client.ClientWorldPolicy; +import art.arcane.volmlib.nativelib.client.ClientWorldPolicies; +import art.arcane.iris.modded.IrisModdedChunkGenerator; +import art.arcane.iris.modded.ModdedWorldgenIds; +import art.arcane.iris.modded.localization.ClientUiMessages; +import art.arcane.iris.localization.IrisLanguage; +import art.arcane.volmlib.nativelib.client.ClientHudBinding; + +import java.util.List; /** - * CLIENT DIST ONLY. References net.minecraft.client and must never be reachable from + * CLIENT DIST ONLY. Calls the native client implementation and must never be reachable from * art.arcane.iris.modded or art.arcane.iris.nativegen. ModdedClientPackageIsolationTest enforces that * direction; there is no @Environment annotation because net.fabricmc.api is not on the Forge or NeoForge * compile classpath and this source set builds for all three loaders. @@ -12,16 +21,31 @@ public final class IrisClientHud { private IrisClientHud() { } + public static ClientHudBinding binding() { + ClientWorldPolicies.register(new ClientWorldPolicy("irisworldgen", IrisModdedChunkGenerator.class, + ModdedWorldgenIds::displayName, true, true, + () -> IrisLanguage.plain(ClientUiMessages.CREATE_STRUCTURES_REQUIRED_TITLE), + () -> IrisLanguage.plain(ClientUiMessages.CREATE_STRUCTURES_REQUIRED_BODY))); + return new ClientHudBinding(IrisClient.HUD_ELEMENT_ID, + List.of(IrisClientKeybinds.TOGGLE_HUD, IrisClientKeybinds.OPEN_MAP, IrisClientKeybinds.TOGGLE_WHAT), + IrisClientHud::render, IrisClientHud::clientTick, IrisClientKeybinds::pollToggle); + } + + private static void clientTick() { + IrisClient.tick(); + tick(); + } + /** * Per client tick, independent of the HUD layer. Toasts are pumped here rather than from - * {@link #render(GuiGraphicsExtractor)} because the whole layered HUD draw is skipped while hideGui (F1) + * {@link #render(ClientGraphics)} because the whole layered HUD draw is skipped while hideGui (F1) * is on, which would silently strand every queued toast until the player pressed F1 again. */ public static void tick() { IrisToastPresenter.pump(); } - public static void render(GuiGraphicsExtractor graphics) { + public static void render(ClientGraphics graphics) { IrisPregenHud.render(graphics); IrisWhatOverlay.render(graphics); } diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientKeybinds.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientKeybinds.java index e50cfbe09..2f7c227cd 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientKeybinds.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisClientKeybinds.java @@ -1,25 +1,26 @@ package art.arcane.iris.client; -import net.minecraft.client.KeyMapping; -import net.minecraft.client.Minecraft; -import org.lwjgl.glfw.GLFW; +import art.arcane.volmlib.nativelib.client.ClientKeyBinding; +import art.arcane.volmlib.nativelib.client.ClientKeyCategory; +import art.arcane.volmlib.nativelib.minecraft26_2.client.NativeClientAccess; + /** - * CLIENT DIST ONLY. Static KeyMapping fields plus LWJGL constants; loading this on a dedicated server is a + * CLIENT DIST ONLY. Static key bindings call the native client runtime; loading this on a dedicated server causes a * NoClassDefFoundError. Reachable only from the per-loader client shims, which are Dist.CLIENT gated. * ModdedClientPackageIsolationTest enforces that no modded or nativegen class reaches it. No @Environment * annotation: net.fabricmc.api is absent from the Forge and NeoForge compile classpath. */ public final class IrisClientKeybinds { - private static final KeyMapping.Category CATEGORY = KeyMapping.Category.register(IrisClient.KEYBIND_CATEGORY_ID); - public static final KeyMapping TOGGLE_HUD = new KeyMapping(IrisClient.KEYBIND_TOGGLE_HUD, GLFW.GLFW_KEY_H, CATEGORY); - public static final KeyMapping OPEN_MAP = new KeyMapping(IrisClient.KEYBIND_OPEN_MAP, GLFW.GLFW_KEY_M, CATEGORY); - public static final KeyMapping TOGGLE_WHAT = new KeyMapping(IrisClient.KEYBIND_TOGGLE_WHAT, GLFW.GLFW_KEY_J, CATEGORY); + private static final ClientKeyCategory CATEGORY = NativeClientAccess.category(IrisClient.KEYBIND_CATEGORY_ID); + public static final ClientKeyBinding TOGGLE_HUD = NativeClientAccess.key(IrisClient.KEYBIND_TOGGLE_HUD, 72, CATEGORY); + public static final ClientKeyBinding OPEN_MAP = NativeClientAccess.key(IrisClient.KEYBIND_OPEN_MAP, 77, CATEGORY); + public static final ClientKeyBinding TOGGLE_WHAT = NativeClientAccess.key(IrisClient.KEYBIND_TOGGLE_WHAT, 74, CATEGORY); private IrisClientKeybinds() { } - public static KeyMapping.Category category() { + public static ClientKeyCategory category() { return CATEGORY; } @@ -36,10 +37,6 @@ public final class IrisClientKeybinds { } private static void openVisionScreen() { - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft == null || minecraft.level == null || minecraft.player == null) { - return; - } - minecraft.setScreenAndShow(new IrisVisionScreen()); + NativeClientAccess.openScreen(new IrisVisionScreen()); } } diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisPregenHud.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisPregenHud.java index 75e9dd718..93ec0b0d4 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisPregenHud.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisPregenHud.java @@ -1,13 +1,13 @@ package art.arcane.iris.client; +import art.arcane.volmlib.nativelib.client.ClientGraphics; +import art.arcane.volmlib.nativelib.minecraft26_2.client.NativeClientAccess; + import art.arcane.iris.modded.localization.ClientUiMessages; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.iris.spi.protocol.IrisMessage; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphicsExtractor; /** * CLIENT DIST ONLY. See {@link IrisClientHud} for why the dist marker is a javadoc contract plus a bytecode @@ -39,7 +39,7 @@ public final class IrisPregenHud { private IrisPregenHud() { } - public static void render(GuiGraphicsExtractor graphics) { + public static void render(ClientGraphics graphics) { if (!IrisClient.hudVisible()) { return; } @@ -48,11 +48,9 @@ public final class IrisPregenHud { if (progress == null || pregen.activeExpired()) { return; } - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft == null || minecraft.player == null) { + if (!NativeClientAccess.playerPresent()) { return; } - Font font = minecraft.font; boolean stale = pregen.activeStale(); boolean paused = progress.state() == IrisMessage.PregenProgress.STATE_PAUSED; double percent = progress.chunksTotal() > 0L @@ -68,8 +66,8 @@ public final class IrisPregenHud { String tail = tail(progress, pregen, stale, paused); int accent = stale ? STALE_COLOR : paused ? PAUSED_COLOR : BAR_RUNNING_COLOR; - int lineHeight = font.lineHeight; - int contentWidth = Math.max(MIN_WIDTH, Math.max(font.width(title), Math.max(font.width(stats), font.width(tail)))); + int lineHeight = graphics.lineHeight(); + int contentWidth = Math.max(MIN_WIDTH, Math.max(graphics.textWidth(title), Math.max(graphics.textWidth(stats), graphics.textWidth(tail)))); int contentHeight = lineHeight * 3 + ROW_GAP * 3 + BAR_HEIGHT; IrisClientRegionMap regionMap = IrisClient.regions(); @@ -91,9 +89,9 @@ public final class IrisPregenHud { graphics.fill(ORIGIN_X - PADDING, ORIGIN_Y - PADDING, ORIGIN_X + panelWidth + PADDING, ORIGIN_Y + panelHeight + PADDING, PANEL_COLOR); int cursorY = ORIGIN_Y; - graphics.text(font, title, ORIGIN_X, cursorY, stale ? STALE_COLOR : TITLE_COLOR); + graphics.text(title, ORIGIN_X, cursorY, stale ? STALE_COLOR : TITLE_COLOR); cursorY += lineHeight + ROW_GAP; - graphics.text(font, stats, ORIGIN_X, cursorY, stale ? STALE_COLOR : TEXT_COLOR); + graphics.text(stats, ORIGIN_X, cursorY, stale ? STALE_COLOR : TEXT_COLOR); cursorY += lineHeight + ROW_GAP; int fillWidth = (int) Math.round(contentWidth * (percent / 100.0D)); @@ -103,14 +101,14 @@ public final class IrisPregenHud { } cursorY += BAR_HEIGHT + ROW_GAP; - graphics.text(font, tail, ORIGIN_X, cursorY, stale ? STALE_COLOR : paused ? PAUSED_COLOR : MUTED_COLOR); + graphics.text(tail, ORIGIN_X, cursorY, stale ? STALE_COLOR : paused ? PAUSED_COLOR : MUTED_COLOR); if (showMap) { renderMinimap(graphics, regionMap, bounds, cellPx, gridWidth, gridHeight, ORIGIN_Y + contentHeight + MINIMAP_GAP); } } - private static void renderMinimap(GuiGraphicsExtractor graphics, IrisClientRegionMap regionMap, IrisClientRegionMap.Bounds bounds, int cellPx, int gridWidth, int gridHeight, int gridTop) { + private static void renderMinimap(ClientGraphics graphics, IrisClientRegionMap regionMap, IrisClientRegionMap.Bounds bounds, int cellPx, int gridWidth, int gridHeight, int gridTop) { int mapLeft = ORIGIN_X; int mapRight = mapLeft + gridWidth; int mapBottom = gridTop + gridHeight; diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisToastPresenter.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisToastPresenter.java index a806084a8..80ab7c3b6 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisToastPresenter.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisToastPresenter.java @@ -1,10 +1,9 @@ package art.arcane.iris.client; +import art.arcane.volmlib.nativelib.client.ClientToastKind; +import art.arcane.volmlib.nativelib.minecraft26_2.client.NativeClientAccess; + import art.arcane.iris.spi.protocol.IrisMessage; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.toasts.SystemToast; -import net.minecraft.client.gui.components.toasts.ToastManager; -import net.minecraft.network.chat.Component; /** * CLIENT DIST ONLY. See {@link IrisClientHud} for why the dist marker is a javadoc contract plus a bytecode @@ -15,25 +14,23 @@ public final class IrisToastPresenter { } public static void pump() { - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft == null || minecraft.gui == null) { + if (!NativeClientAccess.guiPresent()) { return; } - ToastManager manager = minecraft.gui.toastManager(); IrisClientToasts toasts = IrisClient.toasts(); IrisClientToasts.Pending next = toasts.poll(); while (next != null) { - SystemToast.addOrUpdate(manager, tokenFor(next.kind()), Component.literal(next.title()), Component.literal(next.body())); + NativeClientAccess.toast(tokenFor(next.kind()), next.title(), next.body()); next = toasts.poll(); } } - private static SystemToast.SystemToastId tokenFor(int kind) { + private static ClientToastKind tokenFor(int kind) { return switch (kind) { - case IrisMessage.Toast.KIND_SUCCESS -> SystemToast.SystemToastId.WORLD_BACKUP; - case IrisMessage.Toast.KIND_WARNING -> SystemToast.SystemToastId.UNSECURE_SERVER_WARNING; - case IrisMessage.Toast.KIND_ERROR -> SystemToast.SystemToastId.PACK_LOAD_FAILURE; - default -> SystemToast.SystemToastId.PERIODIC_NOTIFICATION; + case IrisMessage.Toast.KIND_SUCCESS -> ClientToastKind.SUCCESS; + case IrisMessage.Toast.KIND_WARNING -> ClientToastKind.WARNING; + case IrisMessage.Toast.KIND_ERROR -> ClientToastKind.ERROR; + default -> ClientToastKind.INFORMATION; }; } } diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisVisionScreen.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisVisionScreen.java index 46c90ec89..4d8f2bbb5 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisVisionScreen.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisVisionScreen.java @@ -1,20 +1,15 @@ package art.arcane.iris.client; +import art.arcane.volmlib.nativelib.client.ClientGraphics; +import art.arcane.volmlib.nativelib.client.ClientScreen; +import art.arcane.volmlib.nativelib.client.ClientTexture; +import art.arcane.volmlib.nativelib.client.ClientTextureData; +import art.arcane.volmlib.nativelib.minecraft26_2.client.NativeClientAccess; + import art.arcane.iris.modded.localization.ClientUiMessages; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.spi.protocol.IrisMessage; import art.arcane.volmlib.util.localization.MessageArgument; -import com.mojang.blaze3d.platform.NativeImage; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.client.input.MouseButtonEvent; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.texture.DynamicTexture; -import net.minecraft.client.renderer.texture.TextureManager; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; import java.util.ArrayList; import java.util.Iterator; @@ -26,7 +21,7 @@ import java.util.Map; * CLIENT DIST ONLY. See {@link IrisClientHud} for why the dist marker is a javadoc contract plus a bytecode * test rather than an @Environment annotation. */ -public final class IrisVisionScreen extends Screen { +public final class IrisVisionScreen implements ClientScreen { private static final int TILE_PIXELS = 128; private static final int MIN_ZOOM = 0; private static final int MAX_ZOOM = 8; @@ -50,6 +45,8 @@ public final class IrisVisionScreen extends Screen { private static final int MARKER_LABEL_BG = 0xE0101010; private final Map textures; + private int width; + private int height; private double centerBlockX; private double centerBlockZ; private int zoom; @@ -57,7 +54,6 @@ public final class IrisVisionScreen extends Screen { private IrisMessage.DimensionStatus renderedDimension; public IrisVisionScreen() { - super(Component.literal(IrisLanguage.plain(ClientUiMessages.VISION_TITLE))); this.textures = new LinkedHashMap<>(64, 0.75f, true); this.centerBlockX = 0.0D; this.centerBlockZ = 0.0D; @@ -67,7 +63,9 @@ public final class IrisVisionScreen extends Screen { } @Override - protected void init() { + public void init(int width, int height) { + this.width = width; + this.height = height; if (!initialized) { centerOnPlayer(); initialized = true; @@ -75,12 +73,12 @@ public final class IrisVisionScreen extends Screen { } @Override - public boolean isPauseScreen() { - return false; + public String title() { + return IrisLanguage.plain(ClientUiMessages.VISION_TITLE); } @Override - public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float partialTick) { + public void render(ClientGraphics graphics, int mouseX, int mouseY, float partialTick) { graphics.fill(0, 0, width, height, BACKGROUND_COLOR); IrisClientSession session = IrisClient.session(); if (!session.isReady()) { @@ -99,14 +97,7 @@ public final class IrisVisionScreen extends Screen { } @Override - public boolean mouseClicked(MouseButtonEvent event, boolean doubleClick) { - // The map itself has nothing clickable, but swallowing every click also swallows the ones widgets and - // the parent screen need. Let Screen route it; drag and scroll are handled below. - return super.mouseClicked(event, doubleClick); - } - - @Override - public boolean mouseDragged(MouseButtonEvent event, double dragX, double dragY) { + public boolean mouseDragged(double dragX, double dragY) { int blocksPerPixel = 1 << zoom; centerBlockX -= dragX * blocksPerPixel; centerBlockZ -= dragY * blocksPerPixel; @@ -137,10 +128,9 @@ public final class IrisVisionScreen extends Screen { @Override public void removed() { releaseTextures(); - super.removed(); } - private void renderTiles(GuiGraphicsExtractor graphics, int mouseX, int mouseY, IrisMessage.DimensionStatus status) { + private void renderTiles(ClientGraphics graphics, int mouseX, int mouseY, IrisMessage.DimensionStatus status) { int blocksPerPixel = 1 << zoom; int tileSpanBlocks = TILE_PIXELS * blocksPerPixel; int originX = (int) Math.floor(width / 2.0D - centerBlockX / blocksPerPixel); @@ -173,8 +163,8 @@ public final class IrisVisionScreen extends Screen { missing.add(key); continue; } - Identifier texture = ensureTexture(key, image); - graphics.blit(RenderPipelines.GUI_TEXTURED, texture, screenX, screenY, 0.0F, 0.0F, TILE_PIXELS, TILE_PIXELS, TILE_PIXELS, TILE_PIXELS); + ClientTexture texture = ensureTexture(key, image); + graphics.blit(texture, screenX, screenY, TILE_PIXELS, TILE_PIXELS); } } @@ -204,7 +194,7 @@ public final class IrisVisionScreen extends Screen { } } - private void drawMarkers(GuiGraphicsExtractor graphics, int mouseX, int mouseY, int minTileX, int maxTileX, int minTileZ, int maxTileZ, int originX, int originY, int blocksPerPixel) { + private void drawMarkers(ClientGraphics graphics, int mouseX, int mouseY, int minTileX, int maxTileX, int minTileZ, int maxTileZ, int originX, int originY, int blocksPerPixel) { IrisClientMarkers markers = IrisClient.markers(); String hoverLabel = null; int hoverX = 0; @@ -228,43 +218,42 @@ public final class IrisVisionScreen extends Screen { } } if (hoverLabel != null) { - int labelWidth = font.width(hoverLabel); - graphics.fill(hoverX + 6, hoverY - 6, hoverX + 12 + labelWidth, hoverY + font.lineHeight, MARKER_LABEL_BG); - graphics.text(font, hoverLabel, hoverX + 9, hoverY - 4, TEXT_COLOR); + int labelWidth = graphics.textWidth(hoverLabel); + graphics.fill(hoverX + 6, hoverY - 6, hoverX + 12 + labelWidth, hoverY + graphics.lineHeight(), MARKER_LABEL_BG); + graphics.text(hoverLabel, hoverX + 9, hoverY - 4, TEXT_COLOR); } } - private void drawPlayer(GuiGraphicsExtractor graphics, int blocksPerPixel) { - LocalPlayer player = Minecraft.getInstance().player; - if (player == null) { + private void drawPlayer(ClientGraphics graphics, int blocksPerPixel) { + if (!NativeClientAccess.playerPresent()) { return; } - int screenX = (int) Math.round(width / 2.0D + (player.getBlockX() - centerBlockX) / blocksPerPixel); - int screenY = (int) Math.round(height / 2.0D + (player.getBlockZ() - centerBlockZ) / blocksPerPixel); + int screenX = (int) Math.round(width / 2.0D + (NativeClientAccess.playerBlockX() - centerBlockX) / blocksPerPixel); + int screenY = (int) Math.round(height / 2.0D + (NativeClientAccess.playerBlockZ() - centerBlockZ) / blocksPerPixel); graphics.fill(screenX - 4, screenY - 4, screenX + 4, screenY + 4, PLAYER_BORDER_COLOR); graphics.fill(screenX - 3, screenY - 3, screenX + 3, screenY + 3, PLAYER_FILL_COLOR); } - private void drawHeader(GuiGraphicsExtractor graphics, String title, String detail) { - int lineHeight = font.lineHeight; + private void drawHeader(ClientGraphics graphics, String title, String detail) { + int lineHeight = graphics.lineHeight(); int headerHeight = lineHeight + 8; graphics.fill(0, 0, width, headerHeight, HEADER_COLOR); - graphics.text(font, title, 8, 4, TITLE_COLOR); - graphics.text(font, detail, 12 + font.width(title), 4, MUTED_COLOR); + graphics.text(title, 8, 4, TITLE_COLOR); + graphics.text(detail, 12 + graphics.textWidth(title), 4, MUTED_COLOR); } - private void drawFooter(GuiGraphicsExtractor graphics, String hint) { - int lineHeight = font.lineHeight; + private void drawFooter(ClientGraphics graphics, String hint) { + int lineHeight = graphics.lineHeight(); int footerTop = height - lineHeight - 8; graphics.fill(0, footerTop, width, height, HEADER_COLOR); - graphics.text(font, hint, 8, footerTop + 4, MUTED_COLOR); + graphics.text(hint, 8, footerTop + 4, MUTED_COLOR); } - private void drawCentered(GuiGraphicsExtractor graphics, String text, int color) { - graphics.text(font, text, (width - font.width(text)) / 2, height / 2 - font.lineHeight / 2, color); + private void drawCentered(ClientGraphics graphics, String text, int color) { + graphics.text(text, (width - graphics.textWidth(text)) / 2, height / 2 - graphics.lineHeight() / 2, color); } - private Identifier ensureTexture(IrisTileKey key, IrisTileImage image) { + private ClientTexture ensureTexture(IrisTileKey key, IrisTileImage image) { TileTexture existing = textures.get(key); if (existing != null) { if (existing.image() == image) { @@ -273,22 +262,11 @@ public final class IrisVisionScreen extends Screen { // IrisClientTileCache replaced the tile with a freshly decoded image (new sequence from the // server). Identity is the generation counter: a re-decode is always a new record instance, so an // upload keyed only on the tile coordinates would show the stale render forever. - Minecraft.getInstance().getTextureManager().release(existing.id()); + existing.id().close(); textures.remove(key); } - int tileWidth = image.width(); - int tileHeight = image.height(); - int[] argb = image.argb(); - NativeImage nativeImage = new NativeImage(NativeImage.Format.RGBA, tileWidth, tileHeight, false); - for (int y = 0; y < tileHeight; y++) { - int row = y * tileWidth; - for (int x = 0; x < tileWidth; x++) { - nativeImage.setPixelABGR(x, y, toAbgr(argb[row + x])); - } - } - DynamicTexture texture = new DynamicTexture(() -> "iris_vision_tile", nativeImage); - Identifier id = Identifier.fromNamespaceAndPath("irisworldgen", texturePath(key)); - Minecraft.getInstance().getTextureManager().register(id, texture); + ClientTexture id = NativeClientAccess.upload(new ClientTextureData( + "irisworldgen", texturePath(key), "iris_vision_tile", image.width(), image.height(), image.argb())); textures.put(key, new TileTexture(id, image)); return id; } @@ -306,22 +284,17 @@ public final class IrisVisionScreen extends Screen { if (textures.size() <= capacity) { return; } - TextureManager manager = Minecraft.getInstance().getTextureManager(); Iterator> iterator = textures.entrySet().iterator(); while (textures.size() > capacity && iterator.hasNext()) { Map.Entry entry = iterator.next(); - manager.release(entry.getValue().id()); + entry.getValue().id().close(); iterator.remove(); } } private void releaseTextures() { - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft != null) { - TextureManager manager = minecraft.getTextureManager(); - for (TileTexture texture : textures.values()) { - manager.release(texture.id()); - } + for (TileTexture texture : textures.values()) { + texture.id().close(); } textures.clear(); } @@ -339,10 +312,9 @@ public final class IrisVisionScreen extends Screen { } private void centerOnPlayer() { - LocalPlayer player = Minecraft.getInstance().player; - if (player != null) { - centerBlockX = player.getBlockX(); - centerBlockZ = player.getBlockZ(); + if (NativeClientAccess.playerPresent()) { + centerBlockX = NativeClientAccess.playerBlockX(); + centerBlockZ = NativeClientAccess.playerBlockZ(); } } @@ -365,14 +337,6 @@ public final class IrisVisionScreen extends Screen { return deltaX * deltaX + deltaZ * deltaZ; } - private static int toAbgr(int argb) { - int alpha = argb >>> 24 & 0xFF; - int red = argb >> 16 & 0xFF; - int green = argb >> 8 & 0xFF; - int blue = argb & 0xFF; - return alpha << 24 | blue << 16 | green << 8 | red; - } - private static String texturePath(IrisTileKey key) { return "vision/t_" + safe(key.tileX()) + "_" + safe(key.tileZ()) + "_" + key.zoom(); } @@ -397,6 +361,6 @@ public final class IrisVisionScreen extends Screen { * not retained: TextureManager owns it after register, and release(id) is the only handle needed. Holding * the source image is what lets {@link #ensureTexture(IrisTileKey, IrisTileImage)} notice a re-decode. */ - private record TileTexture(Identifier id, IrisTileImage image) { + private record TileTexture(ClientTexture id, IrisTileImage image) { } } diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisWhatOverlay.java b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisWhatOverlay.java index 1adf3aa03..4edf8998f 100644 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/IrisWhatOverlay.java +++ b/adapters/client-common/src/main/java/art/arcane/iris/client/IrisWhatOverlay.java @@ -1,16 +1,12 @@ package art.arcane.iris.client; +import art.arcane.volmlib.nativelib.client.ClientGraphics; +import art.arcane.volmlib.nativelib.minecraft26_2.client.NativeClientAccess; + import art.arcane.iris.modded.localization.ClientUiMessages; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.spi.protocol.IrisMessage; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.player.LocalPlayer; -import net.minecraft.core.BlockPos; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; import java.util.ArrayList; import java.util.List; @@ -31,23 +27,15 @@ public final class IrisWhatOverlay { private IrisWhatOverlay() { } - public static void render(GuiGraphicsExtractor graphics) { + public static void render(ClientGraphics graphics) { if (!IrisClient.whatVisible() || !IrisClient.cursorAvailable()) { return; } - Minecraft minecraft = Minecraft.getInstance(); - if (minecraft == null || minecraft.player == null) { + if (!NativeClientAccess.playerPresent()) { return; } - LocalPlayer player = minecraft.player; - int blockX = player.getBlockX(); - int blockZ = player.getBlockZ(); - HitResult hit = minecraft.hitResult; - if (hit != null && hit.getType() == HitResult.Type.BLOCK && hit instanceof BlockHitResult blockHit) { - BlockPos pos = blockHit.getBlockPos(); - blockX = pos.getX(); - blockZ = pos.getZ(); - } + int blockX = NativeClientAccess.targetBlockX(); + int blockZ = NativeClientAccess.targetBlockZ(); IrisClient.cursor().requestFor(blockX, blockZ); IrisMessage.CursorInfo info = IrisClient.cursor().latest(); @@ -67,15 +55,15 @@ public final class IrisWhatOverlay { MessageArgument.trusted("z", info.blockZ()) ), true)); } - draw(graphics, minecraft.font, lines); + draw(graphics, lines); } - private static void draw(GuiGraphicsExtractor graphics, Font font, List lines) { - int lineHeight = font.lineHeight; + private static void draw(ClientGraphics graphics, List lines) { + int lineHeight = graphics.lineHeight(); String title = IrisLanguage.plain(ClientUiMessages.WHAT_TITLE); - int contentWidth = font.width(title); + int contentWidth = graphics.textWidth(title); for (OverlayLine line : lines) { - contentWidth = Math.max(contentWidth, font.width(line.text())); + contentWidth = Math.max(contentWidth, graphics.textWidth(line.text())); } int originX = graphics.guiWidth() / 2 + CURSOR_OFFSET; int originY = graphics.guiHeight() / 2 + CURSOR_OFFSET; @@ -84,10 +72,10 @@ public final class IrisWhatOverlay { graphics.fill(originX - PADDING, originY - PADDING, originX + contentWidth + PADDING, originY + contentHeight + PADDING, PANEL_COLOR); int cursorY = originY; - graphics.text(font, title, originX, cursorY, TITLE_COLOR); + graphics.text(title, originX, cursorY, TITLE_COLOR); cursorY += lineHeight + ROW_GAP; for (OverlayLine line : lines) { - graphics.text(font, line.text(), originX, cursorY, line.muted() ? MUTED_COLOR : TEXT_COLOR); + graphics.text(line.text(), originX, cursorY, line.muted() ? MUTED_COLOR : TEXT_COLOR); cursorY += lineHeight + ROW_GAP; } } diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldOpenFlowsMixin.java b/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldOpenFlowsMixin.java deleted file mode 100644 index 59c83e6a6..000000000 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldOpenFlowsMixin.java +++ /dev/null @@ -1,121 +0,0 @@ -package art.arcane.iris.client.mixin; - -import art.arcane.iris.modded.localization.ClientUiMessages; -import art.arcane.iris.localization.IrisLanguage; -import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedMixinFlags; -import com.mojang.serialization.Lifecycle; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.AlertScreen; -import net.minecraft.client.gui.screens.worldselection.CreateWorldScreen; -import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState; -import net.minecraft.client.gui.screens.worldselection.WorldOpenFlows; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.CommonComponents; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.WorldStem; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.presets.WorldPreset; -import net.minecraft.world.level.storage.LevelStorageSource; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Optional; - -/** - * CLIENT DIST ONLY. Registered from irisworldgen.client.mixins.json, whose "client" block already restricts - * application to the client dist. Target verified against MC 26.2: - * {@code WorldOpenFlows.confirmWorldCreation(Minecraft, CreateWorldScreen, Lifecycle, Runnable, boolean)} is - * static, and {@code openWorldCheckWorldStemCompatibility} is a private instance method. - */ -@Mixin(WorldOpenFlows.class) -public abstract class IrisWorldOpenFlowsMixin { - @Invoker("openWorldLoadBundledResourcePack") - protected abstract void iris$openWorldLoadBundledResourcePack( - LevelStorageSource.LevelStorageAccess worldAccess, - WorldStem worldStem, - PackRepository packRepository, - Runnable onCancel); - - @Inject(method = "confirmWorldCreation", at = @At("HEAD"), cancellable = true) - private static void iris$confirmWorldCreation( - Minecraft minecraft, - CreateWorldScreen parent, - Lifecycle lifecycle, - Runnable task, - boolean skipWarning, - CallbackInfo info) { - ModdedMixinFlags.markWorldOpenFlows(); - if (!iris$selectedPresetIsIris(parent)) { - return; - } - if (!parent.getUiState().isGenerateStructures()) { - // Iris runs its own placement through the vanilla structure step, so a world created with - // Generate Structures off is refused at load by IrisModdedChunkGenerator. Stop it here, at the - // one screen that still has a toggle to fix, instead of at the load that would only report it. - iris$showStructuresRequired(minecraft, parent); - info.cancel(); - return; - } - if (skipWarning || lifecycle == Lifecycle.stable()) { - return; - } - task.run(); - info.cancel(); - } - - private static void iris$showStructuresRequired(Minecraft minecraft, CreateWorldScreen parent) { - // 5-arg ctor with shouldCloseOnEsc=false: ESC on the 3-arg ctor closes to the title screen, - // skipping CreateWorldScreen.onClose and leaking its temp datapack directory. - minecraft.gui.setScreen(new AlertScreen( - () -> minecraft.gui.setScreen(parent), - Component.literal(IrisLanguage.plain(ClientUiMessages.CREATE_STRUCTURES_REQUIRED_TITLE)), - Component.literal(IrisLanguage.plain(ClientUiMessages.CREATE_STRUCTURES_REQUIRED_BODY)), - CommonComponents.GUI_BACK, - false)); - } - - @Inject(method = "openWorldCheckWorldStemCompatibility", at = @At("HEAD"), cancellable = true) - private void iris$openWorldCheckWorldStemCompatibility( - LevelStorageSource.LevelStorageAccess worldAccess, - WorldStem worldStem, - PackRepository packRepository, - Runnable onCancel, - CallbackInfo info) { - ModdedMixinFlags.markWorldOpenFlows(); - if (!iris$containsIrisGenerator(worldStem)) { - return; - } - iris$openWorldLoadBundledResourcePack(worldAccess, worldStem, packRepository, onCancel); - info.cancel(); - } - - private static boolean iris$selectedPresetIsIris(CreateWorldScreen parent) { - WorldCreationUiState.WorldTypeEntry worldType = parent.getUiState().getWorldType(); - Holder preset = worldType.preset(); - if (preset == null) { - return false; - } - Optional> key = preset.unwrapKey(); - return key.isPresent() && "irisworldgen".equals(key.get().identifier().getNamespace()); - } - - private static boolean iris$containsIrisGenerator(WorldStem worldStem) { - Registry dimensions = worldStem.registries() - .compositeAccess() - .lookupOrThrow(Registries.LEVEL_STEM); - for (LevelStem dimension : dimensions) { - if (dimension.generator() instanceof IrisModdedChunkGenerator) { - return true; - } - } - return false; - } -} diff --git a/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldTypeEntryMixin.java b/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldTypeEntryMixin.java deleted file mode 100644 index b961cf949..000000000 --- a/adapters/client-common/src/main/java/art/arcane/iris/client/mixin/IrisWorldTypeEntryMixin.java +++ /dev/null @@ -1,43 +0,0 @@ -package art.arcane.iris.client.mixin; - -import art.arcane.iris.modded.ModdedMixinFlags; -import art.arcane.iris.modded.ModdedWorldgenIds; -import net.minecraft.client.gui.screens.worldselection.WorldCreationUiState; -import net.minecraft.core.Holder; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.levelgen.presets.WorldPreset; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Optional; - -/** - * CLIENT DIST ONLY. Registered from irisworldgen.client.mixins.json, whose "client" block already restricts - * application to the client dist. - */ -@Mixin(WorldCreationUiState.WorldTypeEntry.class) -public class IrisWorldTypeEntryMixin { - @Shadow - @Final - private Holder preset; - - @Inject(method = "describePreset", at = @At("HEAD"), cancellable = true) - private void iris$describePreset(CallbackInfoReturnable info) { - ModdedMixinFlags.markWorldTypeEntry(); - Optional> key = preset == null - ? Optional.empty() - : preset.unwrapKey(); - if (key.isEmpty() || !"irisworldgen".equals(key.get().identifier().getNamespace())) { - return; - } - String label = ModdedWorldgenIds.displayName(key.get().identifier().getPath()); - if (label != null) { - info.setReturnValue(Component.literal(label)); - } - } -} diff --git a/adapters/fabric/build.gradle b/adapters/fabric/build.gradle index 524177339..d37298015 100644 --- a/adapters/fabric/build.gradle +++ b/adapters/fabric/build.gradle @@ -76,6 +76,9 @@ java { } } +ext.nativeSourceLoader = 'fabric' +apply from: file('../../gradle/native-minecraft-sources.gradle') + sourceSets { main { java { @@ -216,7 +219,7 @@ tasks.named('test').configure { } loom { - accessWidenerPath = file('src/main/resources/irisworldgen.accesswidener') + accessWidenerPath = art.arcane.volmit.packaging.NativeAccessRules.prepare(nativeSourceImplementation, "fabric", layout.buildDirectory.dir("native-access").get().asFile) runs { client { runDir(providers.gradleProperty('irisClientRunDir').getOrElse('run')) diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricForcedDatapackSources.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricForcedDatapackSources.java deleted file mode 100644 index a67f60f07..000000000 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricForcedDatapackSources.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.fabric; - -import art.arcane.iris.modded.ModdedForcedDatapack; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.packs.repository.RepositorySource; - -import java.util.LinkedHashSet; -import java.util.Objects; -import java.util.Set; - -public final class FabricForcedDatapackSources { - private FabricForcedDatapackSources() { - } - - public static void attach(PackRepository repository) { - PackRepository activeRepository = Objects.requireNonNull(repository, - "Iris cannot attach the forced startup datapack source to a null repository"); - Set merged = new LinkedHashSet<>(activeRepository.sources); - merged.add(ModdedForcedDatapack.repositorySource()); - activeRepository.sources = merged; - } -} diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricModdedLoader.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricModdedLoader.java deleted file mode 100644 index ca6d91d55..000000000 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricModdedLoader.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.fabric; - -import art.arcane.iris.modded.ModdedLoader; -import art.arcane.iris.modded.service.ModdedTreeFellerService; -import net.fabricmc.api.EnvType; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLevelEvents; -import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; -import net.fabricmc.fabric.api.permission.v1.PermissionContextOwner; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.permissions.PermissionLevel; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; - -import java.io.File; -import java.nio.file.Path; - -public final class FabricModdedLoader implements ModdedLoader { - private static final Identifier TREE_FELLER_PERMISSION = Identifier.fromNamespaceAndPath("irisworldgen", "treefeller"); - - @Override - public String platformName() { - return "fabric"; - } - - @Override - public String minecraftVersion() { - return FabricLoader.getInstance().getModContainer("minecraft") - .map((ModContainer container) -> container.getMetadata().getVersion().getFriendlyString()) - .orElse("unknown"); - } - - @Override - public String modVersion() { - return FabricLoader.getInstance().getModContainer("irisworldgen") - .map((ModContainer container) -> container.getMetadata().getVersion().getFriendlyString()) - .orElse("unknown"); - } - - @Override - public MinecraftServer currentServer() { - Object instance = FabricLoader.getInstance().getGameInstance(); - return instance instanceof MinecraftServer server ? server : null; - } - - @Override - public void invalidateLevelCache(MinecraftServer server) { - // Intentionally empty: Fabric keeps no cached level view of its own (getAllLevels reads the live - // map). Off-thread readers rely on the ModdedServerLevels snapshot, which the caller republishes. - } - - @Override - public void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level) { - ServerLevelEvents.LOAD.invoker().onLevelLoad(server, level); - } - - @Override - public void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level) { - ServerLevelEvents.UNLOAD.invoker().onLevelUnload(server, level); - } - - @Override - public boolean clientEnvironment() { - return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT; - } - - @Override - public Path configDir() { - return FabricLoader.getInstance().getConfigDir(); - } - - @Override - public File modJar() { - return FabricLoader.getInstance().getModContainer("irisworldgen") - .flatMap((ModContainer container) -> container.getOrigin().getPaths().stream().findFirst()) - .map((Path p) -> p.toFile()) - .orElse(null); - } - - @Override - public boolean hasTreeFellerPermission(ServerPlayer player) { - return ((PermissionContextOwner) player).checkPermission( - TREE_FELLER_PERMISSION, - PermissionLevel.GAMEMASTERS - ); - } - - @Override - public boolean canTreeFellerBreak( - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState state - ) { - BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(position) : null; - return ModdedTreeFellerService.runBreakProbe(() -> PlayerBlockBreakEvents.BEFORE.invoker() - .beforeBlockBreak(level, player, position, state, blockEntity)); - } -} diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricProtocolNetworking.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricProtocolNetworking.java index c5465c243..b526e9fd8 100644 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricProtocolNetworking.java +++ b/adapters/fabric/src/main/java/art/arcane/iris/fabric/FabricProtocolNetworking.java @@ -1,54 +1,17 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.fabric; -import art.arcane.iris.modded.ModdedIrisPayload; -import art.arcane.iris.modded.ModdedProtocolChannel; +import art.arcane.iris.modded.ModdedProtocolDefinition; import art.arcane.iris.modded.ModdedProtocolHandler; -import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; -import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricProtocolNetworking; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolCallbacks; public final class FabricProtocolNetworking { private FabricProtocolNetworking() { } public static void install() { - PayloadTypeRegistry.clientboundPlay().register(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC); - PayloadTypeRegistry.serverboundPlay().register(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC); - ServerPlayNetworking.registerGlobalReceiver(ModdedIrisPayload.TYPE, - (payload, context) -> ModdedProtocolHandler.onInbound(context.player(), payload.data())); - ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> ModdedProtocolHandler.onPlayerJoin(handler.player)); - ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> ModdedProtocolHandler.onPlayerDisconnect(handler.player)); - ModdedProtocolHandler.bindChannel(new FabricProtocolChannel()); - } - - private static final class FabricProtocolChannel implements ModdedProtocolChannel { - @Override - public boolean canReceive(ServerPlayer player) { - return ServerPlayNetworking.canSend(player, ModdedIrisPayload.TYPE); - } - - @Override - public void send(ServerPlayer player, ModdedIrisPayload payload) { - ServerPlayNetworking.send(player, payload); - } + NativeProtocolCallbacks callbacks = new NativeProtocolCallbacks(ModdedProtocolHandler::onInbound, + ModdedProtocolHandler::onPlayerJoin, ModdedProtocolHandler::onPlayerDisconnect); + ModdedProtocolHandler.bindChannel(NativeFabricProtocolNetworking.install(ModdedProtocolDefinition.PROTOCOL, callbacks)); } } diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricBootstrap.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricBootstrap.java index 357e26a91..ed8a29270 100644 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricBootstrap.java +++ b/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricBootstrap.java @@ -1,86 +1,26 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.fabric; import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedBlockBreakHandler; import art.arcane.iris.modded.ModdedEngineBootstrap; -import art.arcane.iris.modded.command.IrisModdedCommands; -import art.arcane.iris.modded.command.ModdedWandService; -import com.mojang.brigadier.CommandDispatcher; +import art.arcane.iris.modded.ModdedLifecycleCallbacks; +import art.arcane.iris.modded.service.ModdedTreeFellerService; +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkGeneratorDefinition; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricBootstrap; +import art.arcane.iris.modded.ModdedForcedDatapack; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricPackSources; import net.fabricmc.api.ModInitializer; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLevelEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; -import net.fabricmc.fabric.api.event.player.AttackBlockCallback; -import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.minecraft.commands.CommandBuildContext; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; -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.BlockHitResult; public final class IrisFabricBootstrap implements ModInitializer { @Override public void onInitialize() { - ModdedEngineBootstrap.bootCommon(new FabricModdedLoader(), "Fabric", - () -> Registry.register(BuiltInRegistries.CHUNK_GENERATOR, Identifier.fromNamespaceAndPath("irisworldgen", "iris"), IrisModdedChunkGenerator.CODEC)); + NativeFabricLoader loader = new NativeFabricLoader(new NativeLoaderOptions( + "irisworldgen", "treefeller", ModdedTreeFellerService::runBreakProbe)); + NativeChunkGeneratorDefinition generator = IrisModdedChunkGenerator.DEFINITION; + NativeFabricPackSources.bind(ModdedForcedDatapack::repositorySource); + ModdedEngineBootstrap.bootCommon(loader, "Fabric", () -> NativeFabricBootstrap.registerGenerator(generator)); FabricProtocolNetworking.install(); - ServerLifecycleEvents.SERVER_STARTING.register((MinecraftServer server) -> ModdedEngineBootstrap.start(server)); - ServerLifecycleEvents.SERVER_STARTED.register((MinecraftServer server) -> ModdedEngineBootstrap.serverStarted(server)); - ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> ModdedEngineBootstrap.stop()); - ServerLevelEvents.LOAD.register((MinecraftServer server, ServerLevel level) -> ModdedEngineBootstrap.levelLoaded(level)); - ServerLevelEvents.UNLOAD.register((MinecraftServer server, ServerLevel level) -> ModdedEngineBootstrap.levelUnloaded(level)); - CommandRegistrationCallback.EVENT.register((CommandDispatcher dispatcher, CommandBuildContext buildContext, Commands.CommandSelection selection) -> IrisModdedCommands.register(dispatcher)); - PlayerBlockBreakEvents.BEFORE.register((Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity) -> { - if (level instanceof ServerLevel serverLevel && player instanceof ServerPlayer serverPlayer) { - ModdedBlockBreakHandler.prepare(serverLevel, serverPlayer, pos, state); - } - return true; - }); - PlayerBlockBreakEvents.CANCELED.register((Level level, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity) -> { - if (level instanceof ServerLevel serverLevel) { - ModdedBlockBreakHandler.cancel(serverLevel, pos); - } - }); - AttackBlockCallback.EVENT.register((Player player, Level level, InteractionHand hand, BlockPos pos, Direction direction) -> - ModdedWandService.attackBlock(player, level, hand, pos) ? InteractionResult.SUCCESS : InteractionResult.PASS); - UseBlockCallback.EVENT.register((Player player, Level level, InteractionHand hand, BlockHitResult hit) -> - ModdedWandService.useBlock(player, level, hand, hit.getBlockPos()) ? InteractionResult.SUCCESS : InteractionResult.PASS); - ServerTickEvents.END_SERVER_TICK.register((MinecraftServer server) -> { - ModdedEngineBootstrap.tick(server); - ModdedWandService.serverTick(server); - }); + NativeFabricBootstrap.install(ModdedLifecycleCallbacks.create()); } } diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricClient.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricClient.java index 4eaa7b332..632aedd91 100644 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricClient.java +++ b/adapters/fabric/src/main/java/art/arcane/iris/fabric/IrisFabricClient.java @@ -19,36 +19,17 @@ package art.arcane.iris.fabric; import art.arcane.iris.client.IrisClient; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricClientHooks; import art.arcane.iris.client.IrisClientHud; -import art.arcane.iris.client.IrisClientKeybinds; -import art.arcane.iris.modded.ModdedIrisPayload; +import art.arcane.iris.modded.ModdedProtocolDefinition; +import art.arcane.volmlib.nativelib.minecraft26_2.fabric.NativeFabricProtocolNetworking; import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; public final class IrisFabricClient implements ClientModInitializer { @Override public void onInitializeClient() { - ClientPlayNetworking.registerGlobalReceiver(ModdedIrisPayload.TYPE, - (payload, context) -> IrisClient.onInbound(payload.data())); - IrisClient.bindSender(frame -> { - if (ClientPlayNetworking.canSend(ModdedIrisPayload.TYPE)) { - ClientPlayNetworking.send(new ModdedIrisPayload(frame)); - } - }); - ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> IrisClient.onWorldJoin()); - ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> IrisClient.onDisconnect()); - KeyMappingHelper.registerKeyMapping(IrisClientKeybinds.TOGGLE_HUD); - KeyMappingHelper.registerKeyMapping(IrisClientKeybinds.OPEN_MAP); - KeyMappingHelper.registerKeyMapping(IrisClientKeybinds.TOGGLE_WHAT); - HudElementRegistry.addLast(IrisClient.HUD_ELEMENT_ID, (graphics, delta) -> IrisClientHud.render(graphics)); - ClientTickEvents.END_CLIENT_TICK.register(client -> { - IrisClient.tick(); - IrisClientHud.tick(); - IrisClientKeybinds.pollToggle(); - }); + NativeFabricProtocolNetworking.installClient(ModdedProtocolDefinition.PROTOCOL, IrisClient::onInbound); + IrisClient.bindSender(frame -> NativeFabricProtocolNetworking.sendClient(ModdedProtocolDefinition.PROTOCOL, frame)); + NativeFabricClientHooks.install(IrisClientHud.binding(), IrisClient::onWorldJoin, IrisClient::onDisconnect); } } diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockItemMixin.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockItemMixin.java deleted file mode 100644 index 723c4696b..000000000 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockItemMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package art.arcane.iris.fabric.mixin; - -import art.arcane.iris.modded.ModdedBlockBreakHandler; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.context.BlockPlaceContext; -import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(BlockItem.class) -public final class BlockItemMixin { - @Inject(method = "placeBlock", at = @At("RETURN")) - private void iris$clearTreeProvenance( - BlockPlaceContext context, - BlockState state, - CallbackInfoReturnable info - ) { - if (info.getReturnValue() && context.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.clearPlacedProvenance(level, context.getClickedPos()); - } - } -} diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockMixin.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockMixin.java deleted file mode 100644 index 76fd7366d..000000000 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/BlockMixin.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.fabric.mixin; - -import art.arcane.iris.modded.ModdedBlockBreakHandler; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemInstance; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.ArrayList; -import java.util.List; - -@Mixin(Block.class) -public class BlockMixin { - @Inject(method = "getDrops(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/entity/BlockEntity;Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/item/ItemInstance;)Ljava/util/List;", at = @At("RETURN"), cancellable = true) - private static void iris$applyBlockDrops(BlockState state, ServerLevel level, BlockPos position, BlockEntity blockEntity, - Entity breaker, ItemInstance tool, CallbackInfoReturnable> info) { - if (!(breaker instanceof Player)) { - return; - } - ModdedBlockBreakHandler.Result result = ModdedBlockBreakHandler.completePrepared(level, position); - if (result == null) { - return; - } - if (result.routeCombinedDrops(info.getReturnValue())) { - info.setReturnValue(List.of()); - return; - } - List drops = result.replaceVanillaDrops() - ? new ArrayList<>() - : new ArrayList<>(info.getReturnValue()); - drops.addAll(result.drops()); - info.setReturnValue(drops); - } -} diff --git a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/PackRepositoryMixin.java b/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/PackRepositoryMixin.java deleted file mode 100644 index 8a4e1566d..000000000 --- a/adapters/fabric/src/main/java/art/arcane/iris/fabric/mixin/PackRepositoryMixin.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.fabric.mixin; - -import art.arcane.iris.modded.ModdedIrisLog; -import art.arcane.iris.fabric.FabricForcedDatapackSources; -import net.minecraft.server.packs.repository.PackRepository; -import net.minecraft.server.packs.repository.RepositorySource; -import net.minecraft.server.packs.repository.ServerPacksSource; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Arrays; - -@Mixin(PackRepository.class) -public class PackRepositoryMixin { - @Inject(method = "", at = @At("RETURN")) - private void iris$addForcedDatapackSource(RepositorySource[] sources, CallbackInfo info) { - for (RepositorySource source : sources) { - if (source instanceof ServerPacksSource) { - FabricForcedDatapackSources.attach((PackRepository) (Object) this); - return; - } - } - // Client resource-pack repositories legitimately have no ServerPacksSource; a missing server-data - // repository is reported once at boot by ModdedForcedDatapack.verifyInjected(). - ModdedIrisLog.debug( - "Iris forced datapack source not attached: no ServerPacksSource among {} source(s) {}", - sources.length, Arrays.toString(sources)); - } -} diff --git a/adapters/fabric/src/main/resources/fabric.mod.json b/adapters/fabric/src/main/resources/fabric.mod.json index bb8a25631..37f76f980 100644 --- a/adapters/fabric/src/main/resources/fabric.mod.json +++ b/adapters/fabric/src/main/resources/fabric.mod.json @@ -4,8 +4,14 @@ "version": "${version}", "name": "Iris", "description": "Iris World Generation Engine (Fabric adapter - native chunk generator, explicit Iris world datapack workflow, engine lifecycle)", - "authors": ["Arcane Arts (Volmit Software)"], - "contributors": ["cyberpwn", "NextdoorPsycho", "Vatuu"], + "authors": [ + "Arcane Arts (Volmit Software)" + ], + "contributors": [ + "cyberpwn", + "NextdoorPsycho", + "Vatuu" + ], "contact": { "homepage": "https://docs.volmit.com/iris/", "issues": "https://github.com/VolmitSoftware/Iris/issues", @@ -14,30 +20,54 @@ "license": "GPL-3.0", "icon": "assets/irisworldgen/icon.png", "environment": "*", - "accessWidener": "irisworldgen.accesswidener", + "accessWidener": "volmlib.accesswidener", "mixins": [ - "irisworldgen.mixins.json", - "irisworldgen.entity.mixins.json", + "volmlib.fabric.mixins.json", + "volmlib.entity.mixins.json", { - "config": "irisworldgen.client.mixins.json", + "config": "volmlib.client.mixins.json", "environment": "client" } ], "entrypoints": { - "main": ["art.arcane.iris.fabric.IrisFabricBootstrap"], - "client": ["art.arcane.iris.fabric.IrisFabricClient"] + "main": [ + "art.arcane.iris.fabric.IrisFabricBootstrap" + ], + "client": [ + "art.arcane.iris.fabric.IrisFabricClient" + ] }, "jars": [ - { "file": "META-INF/jars/fabric-api-base.jar" }, - { "file": "META-INF/jars/fabric-registry-sync-v0.jar" }, - { "file": "META-INF/jars/fabric-resource-loader-v1.jar" }, - { "file": "META-INF/jars/fabric-lifecycle-events-v1.jar" }, - { "file": "META-INF/jars/fabric-command-api-v2.jar" }, - { "file": "META-INF/jars/fabric-events-interaction-v0.jar" }, - { "file": "META-INF/jars/fabric-networking-api-v1.jar" }, - { "file": "META-INF/jars/fabric-rendering-v1.jar" }, - { "file": "META-INF/jars/fabric-key-mapping-api-v1.jar" }, - { "file": "META-INF/jars/fabric-permission-api-v1.jar" } + { + "file": "META-INF/jars/fabric-api-base.jar" + }, + { + "file": "META-INF/jars/fabric-registry-sync-v0.jar" + }, + { + "file": "META-INF/jars/fabric-resource-loader-v1.jar" + }, + { + "file": "META-INF/jars/fabric-lifecycle-events-v1.jar" + }, + { + "file": "META-INF/jars/fabric-command-api-v2.jar" + }, + { + "file": "META-INF/jars/fabric-events-interaction-v0.jar" + }, + { + "file": "META-INF/jars/fabric-networking-api-v1.jar" + }, + { + "file": "META-INF/jars/fabric-rendering-v1.jar" + }, + { + "file": "META-INF/jars/fabric-key-mapping-api-v1.jar" + }, + { + "file": "META-INF/jars/fabric-permission-api-v1.jar" + } ], "depends": { "fabricloader": ">=0.19.3", diff --git a/adapters/fabric/src/main/resources/irisworldgen.accesswidener b/adapters/fabric/src/main/resources/irisworldgen.accesswidener deleted file mode 100644 index 573ad0649..000000000 --- a/adapters/fabric/src/main/resources/irisworldgen.accesswidener +++ /dev/null @@ -1,6 +0,0 @@ -accessWidener v2 official -accessible field net/minecraft/server/MinecraftServer levels Ljava/util/Map; -accessible field net/minecraft/server/MinecraftServer executor Ljava/util/concurrent/Executor; -accessible field net/minecraft/server/MinecraftServer storageSource Lnet/minecraft/world/level/storage/LevelStorageSource$LevelStorageAccess; -accessible field net/minecraft/server/packs/repository/PackRepository sources Ljava/util/Set; -mutable field net/minecraft/server/packs/repository/PackRepository sources Ljava/util/Set; diff --git a/adapters/fabric/src/main/resources/irisworldgen.mixins.json b/adapters/fabric/src/main/resources/irisworldgen.mixins.json deleted file mode 100644 index 916a8d081..000000000 --- a/adapters/fabric/src/main/resources/irisworldgen.mixins.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "art.arcane.iris.fabric.mixin", - "compatibilityLevel": "JAVA_21", - "mixins": [ - "BlockItemMixin", - "BlockMixin", - "PackRepositoryMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} diff --git a/adapters/forge/build.gradle b/adapters/forge/build.gradle index bb9af0f4f..fc24be777 100644 --- a/adapters/forge/build.gradle +++ b/adapters/forge/build.gradle @@ -84,6 +84,9 @@ java { } } +ext.nativeSourceLoader = 'forge' +apply from: file('../../gradle/native-minecraft-sources.gradle') + sourceSets { main { java { @@ -208,13 +211,13 @@ dependencies { } minecraft { - accessTransformer.from(file('src/main/resources/META-INF/accesstransformer.cfg')) + accessTransformer.from(art.arcane.volmit.packaging.NativeAccessRules.prepare(nativeSourceImplementation, "forge", layout.buildDirectory.dir("native-access").get().asFile)) runs { register('client') { workingDir = layout.projectDirectory.dir('run') jvmArgs('-Xmx8G') - args('--mixin.config', 'irisworldgen.entity.mixins.json', - '--mixin.config', 'irisworldgen.client.mixins.json') + args('--mixin.config', 'volmlib.entity.mixins.json', + '--mixin.config', 'volmlib.client.mixins.json') } register('server') { workingDir = layout.projectDirectory.dir('run') @@ -235,7 +238,7 @@ minecraft { systemProperty('iris.worldcheck', worldCheck) } jvmArgs('-Xmx8G') - args('--mixin.config', 'irisworldgen.entity.mixins.json', '--nogui') + args('--mixin.config', 'volmlib.entity.mixins.json', '--nogui') } } } @@ -272,7 +275,7 @@ tasks.named('shadowJar', ShadowJar).configure { // FML 26.2 has no TOML mixin handling at all (ModFileParser reads neither [[mixins]] nor // accessTransformers on Forge). This manifest attribute is the only registration mechanism; // do not move it into mods.toml. - attributes('MixinConfigs': 'irisworldgen.entity.mixins.json,irisworldgen.client.mixins.json') + attributes('MixinConfigs': 'volmlib.entity.mixins.json,volmlib.client.mixins.json') } archiveFileName.set(irisArtifactName('Forge', "${minecraftVersion}+${loaderDisplayVersion(forgeVersion)}")) configurations = [project.configurations.named('bundle').get()] diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeClientProtocol.java b/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeClientProtocol.java deleted file mode 100644 index fdef8f260..000000000 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeClientProtocol.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.forge; - -import art.arcane.iris.client.IrisClient; -import net.minecraftforge.event.network.CustomPayloadEvent; - -public final class ForgeClientProtocol { - private ForgeClientProtocol() { - } - - public static void handleInbound(CustomPayloadEvent.Context context, byte[] data) { - context.enqueueWork(() -> IrisClient.onInbound(data)); - } -} diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeModdedLoader.java b/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeModdedLoader.java deleted file mode 100644 index e7f40264b..000000000 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeModdedLoader.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.forge; - -import art.arcane.iris.modded.ModdedLoader; -import art.arcane.iris.modded.service.ModdedTreeFellerService; -import net.minecraft.commands.Commands; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.util.Result; -import net.minecraftforge.event.level.BlockEvent; -import net.minecraftforge.event.level.LevelEvent; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.loading.FMLEnvironment; -import net.minecraftforge.fml.loading.FMLLoader; -import net.minecraftforge.fml.loading.FMLPaths; -import net.minecraftforge.forgespi.language.IModFileInfo; -import net.minecraftforge.server.ServerLifecycleHooks; -import net.minecraftforge.server.permission.PermissionAPI; -import net.minecraftforge.server.permission.nodes.PermissionNode; -import net.minecraftforge.server.permission.nodes.PermissionTypes; - -import java.io.File; -import java.nio.file.Path; - -public final class ForgeModdedLoader implements ModdedLoader { - public static final PermissionNode TREE_FELLER_PERMISSION = new PermissionNode<>( - "irisworldgen", - "treefeller", - PermissionTypes.BOOLEAN, - (player, playerId, contexts) -> - player != null && Commands.LEVEL_GAMEMASTERS.check(player.permissions()) - ); - - @Override - public String platformName() { - return "forge"; - } - - @Override - public String minecraftVersion() { - return FMLLoader.versionInfo().mcVersion(); - } - - @Override - public String modVersion() { - return ModList.getModContainerById("irisworldgen") - .map((net.minecraftforge.fml.ModContainer container) -> container.getModInfo().getVersion().toString()) - .orElse("unknown"); - } - - @Override - public MinecraftServer currentServer() { - return ServerLifecycleHooks.getCurrentServer(); - } - - @Override - public void invalidateLevelCache(MinecraftServer server) { - server.markWorldsDirty(); - } - - @Override - public void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level) { - LevelEvent.Load.BUS.post(new LevelEvent.Load(level)); - } - - @Override - public void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level) { - LevelEvent.Unload.BUS.post(new LevelEvent.Unload(level)); - } - - @Override - public boolean clientEnvironment() { - return FMLEnvironment.dist.isClient(); - } - - @Override - public Path configDir() { - return FMLPaths.CONFIGDIR.get(); - } - - @Override - public File modJar() { - IModFileInfo info = ModList.getModFileById("irisworldgen"); - return info == null ? null : info.getFile().getFilePath().toFile(); - } - - @Override - public boolean hasTreeFellerPermission(ServerPlayer player) { - return PermissionAPI.getPermission(player, TREE_FELLER_PERMISSION); - } - - @Override - public boolean canTreeFellerBreak( - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState state - ) { - return ModdedTreeFellerService.runBreakProbe(() -> { - BlockEvent.BreakEvent event = new BlockEvent.BreakEvent( - level, - position, - state, - player, - Result.DEFAULT - ); - BlockEvent.BreakEvent.BUS.post(event); - return !event.getResult().isDenied(); - }); - } -} diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeProtocolNetworking.java b/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeProtocolNetworking.java index c04f252af..19548fa17 100644 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeProtocolNetworking.java +++ b/adapters/forge/src/main/java/art/arcane/iris/forge/ForgeProtocolNetworking.java @@ -1,89 +1,26 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.forge; -import art.arcane.iris.modded.ModdedIrisPayload; -import art.arcane.iris.modded.ModdedProtocolChannel; +import art.arcane.iris.modded.ModdedProtocolDefinition; import art.arcane.iris.modded.ModdedProtocolHandler; -import art.arcane.iris.spi.protocol.IrisProtocol; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerPlayer; -import net.minecraftforge.event.network.CustomPayloadEvent; -import net.minecraftforge.network.Channel; -import net.minecraftforge.network.ChannelBuilder; -import net.minecraftforge.network.NetworkProtocol; -import net.minecraftforge.network.PacketDistributor; +import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeProtocolNetworking; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolCallbacks; +import art.arcane.iris.client.IrisClient; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolChannel; public final class ForgeProtocolNetworking { - private static volatile Channel channel; + private static volatile NativeProtocolChannel channel; private ForgeProtocolNetworking() { } public static void register() { - Channel boundChannel = ChannelBuilder.named(Identifier.parse(IrisProtocol.CHANNEL)) - .optional() - .payloadChannel() - .protocol(NetworkProtocol.PLAY) - .bidirectional() - .add(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC, ForgeProtocolNetworking::onPayload) - .build(); - channel = boundChannel; - ModdedProtocolHandler.bindChannel(new ForgeProtocolChannel(boundChannel)); + NativeProtocolCallbacks callbacks = new NativeProtocolCallbacks(ModdedProtocolHandler::onInbound, + ModdedProtocolHandler::onPlayerJoin, ModdedProtocolHandler::onPlayerDisconnect); + channel = NativeForgeProtocolNetworking.register(ModdedProtocolDefinition.PROTOCOL, callbacks, IrisClient::onInbound); + ModdedProtocolHandler.bindChannel(channel); } - public static Channel channel() { + public static NativeProtocolChannel channel() { return channel; } - - private static void onPayload(ModdedIrisPayload payload, CustomPayloadEvent.Context context) { - context.setPacketHandled(true); - if (context.isClientSide()) { - ForgeClientProtocol.handleInbound(context, payload.data()); - return; - } - ServerPlayer player = context.getSender(); - if (player == null) { - return; - } - ModdedProtocolHandler.onInbound(player, payload.data()); - } - - private static final class ForgeProtocolChannel implements ModdedProtocolChannel { - private final Channel channel; - - private ForgeProtocolChannel(Channel channel) { - this.channel = channel; - } - - @Override - public boolean canReceive(ServerPlayer player) { - // Forge tracks the channels the remote side announced during handshake. Without this the - // server pushes Iris payloads at vanilla clients, which drop them as unknown custom - // payloads. Mirrors the NeoForge ICommonPacketListener.hasChannel check. - return channel.isRemotePresent(player.connection.getConnection()); - } - - @Override - public void send(ServerPlayer player, ModdedIrisPayload payload) { - channel.send(payload, PacketDistributor.PLAYER.with(player)); - } - } } diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBlockLootModifier.java b/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBlockLootModifier.java deleted file mode 100644 index 1fecce628..000000000 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBlockLootModifier.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.forge; - -import art.arcane.iris.modded.ModdedBlockBreakHandler; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.storage.loot.LootContext; -import net.minecraft.world.level.storage.loot.LootTable; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.level.storage.loot.predicates.LootItemCondition; -import net.minecraft.world.phys.Vec3; -import net.minecraftforge.common.loot.IGlobalLootModifier; -import net.minecraftforge.common.loot.LootModifier; -import org.jetbrains.annotations.NotNull; - -public final class IrisForgeBlockLootModifier extends LootModifier { - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> - codecStart(instance).apply(instance, IrisForgeBlockLootModifier::new)); - - public IrisForgeBlockLootModifier(LootItemCondition[] conditions) { - super(conditions); - } - - @Override - public MapCodec codec() { - return CODEC; - } - - @NotNull - @Override - protected ObjectArrayList doApply(LootTable table, ObjectArrayList generatedLoot, LootContext context) { - Entity breaker = context.getOptionalParameter(LootContextParams.THIS_ENTITY); - BlockState state = context.getOptionalParameter(LootContextParams.BLOCK_STATE); - Vec3 origin = context.getOptionalParameter(LootContextParams.ORIGIN); - if (!(breaker instanceof Player) || state == null || origin == null) { - return generatedLoot; - } - ServerLevel level = context.getLevel(); - BlockPos position = BlockPos.containing(origin); - ModdedBlockBreakHandler.Result result = ModdedBlockBreakHandler.completePrepared(level, position); - if (result == null) { - return generatedLoot; - } - if (result.routeCombinedDrops(generatedLoot)) { - generatedLoot.clear(); - return generatedLoot; - } - if (result.replaceVanillaDrops()) { - generatedLoot.clear(); - } - generatedLoot.addAll(result.drops()); - return generatedLoot; - } -} diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBootstrap.java b/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBootstrap.java index 253a01690..3901d6166 100644 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBootstrap.java +++ b/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeBootstrap.java @@ -1,147 +1,29 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.forge; import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedBlockBreakHandler; import art.arcane.iris.modded.ModdedEngineBootstrap; -import art.arcane.iris.modded.ModdedForcedDatapack; -import art.arcane.iris.modded.ModdedProtocolHandler; -import art.arcane.iris.modded.command.IrisModdedCommands; -import art.arcane.iris.modded.command.ModdedWandService; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.registries.Registries; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.packs.PackType; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraftforge.event.AddPackFindersEvent; -import net.minecraftforge.event.RegisterCommandsEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.entity.player.PlayerEvent; -import net.minecraftforge.event.entity.player.PlayerInteractEvent; -import net.minecraftforge.event.level.BlockEvent; -import net.minecraftforge.event.level.LevelEvent; -import net.minecraftforge.event.server.ServerAboutToStartEvent; -import net.minecraftforge.event.server.ServerStartedEvent; -import net.minecraftforge.event.server.ServerStartingEvent; -import net.minecraftforge.event.server.ServerStoppingEvent; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.common.loot.IGlobalLootModifier; -import net.minecraftforge.common.util.BlockSnapshot; -import net.minecraftforge.eventbus.api.listener.Priority; +import art.arcane.iris.modded.ModdedLifecycleCallbacks; +import art.arcane.iris.modded.service.ModdedTreeFellerService; +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkGeneratorDefinition; +import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeBootstrap; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -import net.minecraftforge.fml.loading.FMLEnvironment; import net.minecraftforge.fml.loading.FMLLoader; -import net.minecraftforge.registries.DeferredRegister; -import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.server.permission.events.PermissionGatherEvent; - -import java.util.function.Predicate; @Mod("irisworldgen") public final class IrisForgeBootstrap { public IrisForgeBootstrap(FMLJavaModLoadingContext context) { - ModdedEngineBootstrap.bootCommon(new ForgeModdedLoader(), "Forge " + FMLLoader.versionInfo().forgeVersion(), () -> { - DeferredRegister> chunkGenerators = DeferredRegister.create(Registries.CHUNK_GENERATOR, "irisworldgen"); - chunkGenerators.register("iris", () -> IrisModdedChunkGenerator.CODEC); - chunkGenerators.register(context.getModBusGroup()); - DeferredRegister> lootModifiers = DeferredRegister.create(ForgeRegistries.GLOBAL_LOOT_MODIFIER_SERIALIZERS, "irisworldgen"); - lootModifiers.register("block_drops", () -> IrisForgeBlockLootModifier.CODEC); - lootModifiers.register(context.getModBusGroup()); - }); - + NativeForgeLoader loader = new NativeForgeLoader(new NativeLoaderOptions( + "irisworldgen", "treefeller", ModdedTreeFellerService::runBreakProbe)); + NativeChunkGeneratorDefinition generator = IrisModdedChunkGenerator.DEFINITION; + ModdedEngineBootstrap.bootCommon(loader, "Forge " + FMLLoader.versionInfo().forgeVersion(), + () -> NativeForgeBootstrap.registerGenerators(context, generator, "block_drops")); ForgeProtocolNetworking.register(); - - // Dist gate for every client-tainted class. DistExecutor is gone in Forge 26.2 (65.x) - only - // net.minecraftforge.api.distmarker.Dist survives, from mergetool-api - so the guard is this branch. - // It is equally safe: IrisForgeClient appears only as an invokestatic target, so the JVM resolves the - // constant-pool entry lazily on first execution and a dedicated server never loads the class. - if (FMLEnvironment.dist == Dist.CLIENT) { + if (loader.clientEnvironment()) { IrisForgeClient.init(); } - - ServerAboutToStartEvent.BUS.addListener((ServerAboutToStartEvent event) -> ModdedEngineBootstrap.serverAboutToStart(event.getServer())); - ServerStartingEvent.BUS.addListener((ServerStartingEvent event) -> ModdedEngineBootstrap.start(event.getServer())); - ServerStartedEvent.BUS.addListener((ServerStartedEvent event) -> ModdedEngineBootstrap.serverStarted(event.getServer())); - ServerStoppingEvent.BUS.addListener((ServerStoppingEvent event) -> ModdedEngineBootstrap.stop()); - LevelEvent.Load.BUS.addListener((LevelEvent.Load event) -> { - if (event.getLevel() instanceof ServerLevel level) { - ModdedEngineBootstrap.levelLoaded(level); - } - }); - LevelEvent.Unload.BUS.addListener((LevelEvent.Unload event) -> { - if (event.getLevel() instanceof ServerLevel level) { - ModdedEngineBootstrap.levelUnloaded(level); - } - }); - PlayerEvent.PlayerLoggedInEvent.BUS.addListener((PlayerEvent.PlayerLoggedInEvent event) -> { - if (event.getEntity() instanceof ServerPlayer player) { - ModdedProtocolHandler.onPlayerJoin(player); - } - }); - PlayerEvent.PlayerLoggedOutEvent.BUS.addListener((PlayerEvent.PlayerLoggedOutEvent event) -> { - if (event.getEntity() instanceof ServerPlayer player) { - ModdedProtocolHandler.onPlayerDisconnect(player); - } - }); - AddPackFindersEvent.BUS.addListener((AddPackFindersEvent event) -> { - if (event.getPackType() == PackType.SERVER_DATA) { - event.addRepositorySource(ModdedForcedDatapack.repositorySource()); - } - }); - RegisterCommandsEvent.BUS.addListener((RegisterCommandsEvent event) -> IrisModdedCommands.register(event.getDispatcher())); - PermissionGatherEvent.Nodes.BUS.addListener((PermissionGatherEvent.Nodes event) -> - event.addNodes(ForgeModdedLoader.TREE_FELLER_PERMISSION)); - BlockEvent.BreakEvent.BUS.addListener((BlockEvent.BreakEvent event) -> { - if (event.getLevel() instanceof ServerLevel level - && event.getPlayer() instanceof ServerPlayer player - && !event.getResult().isDenied()) { - ModdedBlockBreakHandler.prepare(level, player, event.getPos(), event.getState()); - } - }); - // EventBus 7: the (priority, boolean, consumer) overload's boolean means "always cancels"; passing - // false is rejected at registration for listeners that never cancel. Use (priority, consumer). - BlockEvent.EntityPlaceEvent.BUS.addListener(Priority.MONITOR, (BlockEvent.EntityPlaceEvent event) -> { - if (!(event instanceof BlockEvent.EntityMultiPlaceEvent) - && event.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.clearPlacedProvenance(level, event.getPos()); - } - }); - BlockEvent.EntityMultiPlaceEvent.BUS.addListener( - Priority.MONITOR, - (BlockEvent.EntityMultiPlaceEvent event) -> { - for (BlockSnapshot snapshot : event.getReplacedBlockSnapshots()) { - if (snapshot.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.clearPlacedProvenance(level, snapshot.getPos()); - } - } - } - ); - PlayerInteractEvent.LeftClickBlock.BUS.addListener((Predicate) (PlayerInteractEvent.LeftClickBlock event) -> - ModdedWandService.attackBlock(event.getEntity(), event.getLevel(), event.getHand(), event.getPos())); - PlayerInteractEvent.RightClickBlock.BUS.addListener((Predicate) (PlayerInteractEvent.RightClickBlock event) -> - ModdedWandService.useBlock(event.getEntity(), event.getLevel(), event.getHand(), event.getPos())); - TickEvent.ServerTickEvent.Post.BUS.addListener((TickEvent.ServerTickEvent.Post event) -> { - ModdedEngineBootstrap.tick(event.server()); - ModdedWandService.serverTick(event.server()); - }); + NativeForgeBootstrap.install(loader, ModdedLifecycleCallbacks.create()); } } diff --git a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeClient.java b/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeClient.java index 0dedbcdd4..a2c5d951b 100644 --- a/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeClient.java +++ b/adapters/forge/src/main/java/art/arcane/iris/forge/IrisForgeClient.java @@ -19,17 +19,9 @@ package art.arcane.iris.forge; import art.arcane.iris.client.IrisClient; +import art.arcane.volmlib.nativelib.minecraft26_2.forge.NativeForgeClientHooks; import art.arcane.iris.client.IrisClientHud; -import art.arcane.iris.client.IrisClientKeybinds; -import art.arcane.iris.modded.ModdedIrisPayload; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraftforge.client.event.AddGuiOverlayLayersEvent; -import net.minecraftforge.client.event.ClientPlayerNetworkEvent; -import net.minecraftforge.client.event.InputEvent; -import net.minecraftforge.client.event.RegisterKeyMappingsEvent; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.network.Channel; -import net.minecraftforge.network.PacketDistributor; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolChannel; public final class IrisForgeClient { private IrisForgeClient() { @@ -37,28 +29,14 @@ public final class IrisForgeClient { public static void init() { IrisClient.bindSender(IrisForgeClient::sendToServer); - AddGuiOverlayLayersEvent.BUS.addListener((AddGuiOverlayLayersEvent event) -> - event.getLayeredDraw().add(IrisClient.HUD_ELEMENT_ID, (graphics, delta) -> IrisClientHud.render(graphics))); - RegisterKeyMappingsEvent.BUS.addListener((RegisterKeyMappingsEvent event) -> { - event.register(IrisClientKeybinds.TOGGLE_HUD); - event.register(IrisClientKeybinds.OPEN_MAP); - event.register(IrisClientKeybinds.TOGGLE_WHAT); - }); - ClientPlayerNetworkEvent.LoggingIn.BUS.addListener((ClientPlayerNetworkEvent.LoggingIn event) -> IrisClient.onWorldJoin()); - ClientPlayerNetworkEvent.LoggingOut.BUS.addListener((ClientPlayerNetworkEvent.LoggingOut event) -> IrisClient.onDisconnect()); - InputEvent.Key.BUS.addListener((InputEvent.Key event) -> IrisClientKeybinds.pollToggle()); - TickEvent.ClientTickEvent.Post.BUS.addListener((TickEvent.ClientTickEvent.Post event) -> { - IrisClientKeybinds.pollToggle(); - IrisClient.tick(); - IrisClientHud.tick(); - }); + NativeForgeClientHooks.install(IrisClientHud.binding(), IrisClient::onWorldJoin, IrisClient::onDisconnect); } private static void sendToServer(byte[] frame) { - Channel channel = ForgeProtocolNetworking.channel(); + NativeProtocolChannel channel = ForgeProtocolNetworking.channel(); if (channel == null) { return; } - channel.send(new ModdedIrisPayload(frame), PacketDistributor.SERVER.noArg()); + channel.sendToServer(frame); } } diff --git a/adapters/forge/src/main/resources/META-INF/accesstransformer.cfg b/adapters/forge/src/main/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index 5a2faeeb3..000000000 --- a/adapters/forge/src/main/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,3 +0,0 @@ -public net.minecraft.server.MinecraftServer levels -public net.minecraft.server.MinecraftServer executor -public net.minecraft.server.MinecraftServer storageSource diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/ForcedStructureChunkGenerator.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/ForcedStructureChunkGenerator.java deleted file mode 100644 index d59ed631f..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/ForcedStructureChunkGenerator.java +++ /dev/null @@ -1,94 +0,0 @@ -package art.arcane.iris.nativegen; - -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.FixedBiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.blending.Blender; - -import java.util.List; -import java.util.concurrent.CompletableFuture; - -final class ForcedStructureChunkGenerator extends ChunkGenerator { - private final ChunkGenerator delegate; - - ForcedStructureChunkGenerator(ChunkGenerator delegate, Holder sourceBiome) { - super(new FixedBiomeSource(sourceBiome)); - this.delegate = delegate; - } - - @Override - protected MapCodec codec() { - return MapCodec.unit(this); - } - - @Override - public void applyCarvers(WorldGenRegion region, long seed, RandomState randomState, - BiomeManager biomeManager, StructureManager structureManager, - ChunkAccess chunk) { - delegate.applyCarvers(region, seed, randomState, biomeManager, structureManager, chunk); - } - - @Override - public void buildSurface(WorldGenRegion level, StructureManager structureManager, - RandomState randomState, ChunkAccess protoChunk) { - delegate.buildSurface(level, structureManager, randomState, protoChunk); - } - - @Override - public void spawnOriginalMobs(WorldGenRegion worldGenRegion) { - delegate.spawnOriginalMobs(worldGenRegion); - } - - @Override - public int getGenDepth() { - return delegate.getGenDepth(); - } - - @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, - StructureManager structureManager, - ChunkAccess centerChunk) { - return delegate.fillFromNoise( - blender, randomState, structureManager, centerChunk); - } - - @Override - public int getSeaLevel() { - return delegate.getSeaLevel(); - } - - @Override - public int getMinY() { - return delegate.getMinY(); - } - - @Override - public int getBaseHeight(int x, int z, Heightmap.Types type, - LevelHeightAccessor heightAccessor, RandomState randomState) { - return delegate.getBaseHeight(x, z, type, heightAccessor, randomState); - } - - @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, - RandomState randomState) { - return delegate.getBaseColumn(x, z, heightAccessor, randomState); - } - - @Override - public void addDebugScreenInfo(List result, RandomState randomState, - BlockPos feetPos) { - delegate.addDebugScreenInfo(result, randomState, feetPos); - } - -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeBuildFutures.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeBuildFutures.java deleted file mode 100644 index d4782f6f1..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeBuildFutures.java +++ /dev/null @@ -1,31 +0,0 @@ -package art.arcane.iris.nativegen; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; - -/** - * Shared waiter for the racy build caches in this package: the loser of a putIfAbsent race - * must surface the same exception the builder threw, not a CompletionException wrapper. - */ -final class NativeBuildFutures { - private NativeBuildFutures() { - } - - static T awaitBuild(CompletableFuture future, String what) { - try { - return future.join(); - } catch (CompletionException error) { - Throwable cause = error.getCause(); - if (cause instanceof RuntimeException runtime) { - throw runtime; - } - if (cause instanceof Error fatal) { - throw fatal; - } - if (cause == null) { - throw error; - } - throw new IllegalStateException(what + " failed", cause); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeGenerationWriteGuard.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeGenerationWriteGuard.java deleted file mode 100644 index 96d3120c9..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeGenerationWriteGuard.java +++ /dev/null @@ -1,78 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.status.ChunkPyramid; -import net.minecraft.world.level.chunk.status.ChunkStatus; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; - -public final class NativeGenerationWriteGuard { - private NativeGenerationWriteGuard() { - } - - public static boolean allowsDecoration(Engine engine, WorldGenLevel region, ChunkPos center) { - int radius = ChunkPyramid.GENERATION_PYRAMID.getStepTo(ChunkStatus.FEATURES).blockStateWriteRadius(); - for (int offsetX = -radius; offsetX <= radius; offsetX++) { - for (int offsetZ = -radius; offsetZ <= radius; offsetZ++) { - int chunkX = Math.addExact(center.x(), offsetX); - int chunkZ = Math.addExact(center.z(), offsetZ); - ChunkAccess target = region.getChunk(chunkX, chunkZ); - if (target.getPersistedStatus().isOrAfter(ChunkStatus.FULL) - && !engine.getComplex().allowsMantleChunkWrite(chunkX, chunkZ)) { - return false; - } - } - } - return true; - } - - public static boolean isHistoricalStructure(Engine engine, long activation) { - if (activation <= 0 || !(engine instanceof IrisEngine irisEngine)) { - return false; - } - GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); - return router != null && activation < router.history().activeActivation().activationId(); - } - - public static boolean allowsPendingStage(Engine engine, ChunkAccess chunk, ChunkStatus stage) { - return stage.isOrAfter(ChunkStatus.FEATURES) - && chunk.getPersistedStatus().isOrAfter(ChunkStatus.NOISE) - && !chunk.getPersistedStatus().isOrAfter(stage) - && !engine.getComplex().allowsMantleChunkWrite(chunk.getPos().x(), chunk.getPos().z()); - } - - public static Predicate protectedPositions( - IrisStaticObjectLayer staticObjects, - DimensionStackContext stackContext, - int minimumY - ) { - Map layouts = new ConcurrentHashMap<>(); - return position -> { - if (!staticObjects.isEmpty() && staticObjects.contains( - position.getX(), position.getY() - minimumY, position.getZ())) { - return true; - } - if (stackContext == null) { - return false; - } - long columnKey = ((long) position.getX() << 32) - ^ (position.getZ() & 0xFFFFFFFFL); - DimensionStackLayout layout = layouts.computeIfAbsent( - columnKey, - ignored -> stackContext.sample(position.getX(), position.getZ()) - ); - return layout.isHostFeatureProtectedY(position.getY() - minimumY); - }; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFactory.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFactory.java deleted file mode 100644 index 14d9828ed..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFactory.java +++ /dev/null @@ -1,284 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.jigsaw.IrisJigsawConfiguration; -import art.arcane.iris.structure.jigsaw.IrisJigsawHeightmap; -import art.arcane.iris.structure.jigsaw.IrisJigsawLiquidSettings; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; -import net.minecraft.core.Holder; -import net.minecraft.core.RegistryAccess; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtOps; -import net.minecraft.nbt.Tag; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.Locale; -import java.util.Objects; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; - -public final class NativeStructureFactory { - private NativeStructureFactory() { - } - - public static StructureStart generate(GenerationContext context, Holder sourceHolder, - NativeStructureStartPlan plan, int references) { - Structure source = sourceHolder.value(); - Structure configured = configure( - context.registryAccess(), source, plan.source().getJigsaw(), - plan.placement().isUnderground(), plan.baseY()); - Holder sourceBiome = source.biomes().stream().findFirst() - .or(() -> context.biomeSource().possibleBiomes().stream().findFirst()) - .orElseThrow(() -> new IllegalStateException("Configured native structure '" - + plan.source().getStructure() + "' has no usable generation biome")); - ChunkGenerator forcedGenerator = new ForcedStructureChunkGenerator( - context.generator(), sourceBiome); - StructureStart generated = configured.generate( - sourceHolder, - context.levelKey(), - context.registryAccess(), - forcedGenerator, - forcedGenerator.getBiomeSource(), - context.randomState(), - context.templateManager(), - context.levelSeed(), - new ChunkPos(plan.chunkX(), plan.chunkZ()), - references, - context.heightAccessor(), - context.biomePredicate() - ); - if (generated == null || !generated.isValid()) { - return StructureStart.INVALID_START; - } - StructureStart positioned; - if (plan.placement().isUnderground()) { - positioned = NativeStructureVerticalPlacer.relocateToMinY( - generated, source, plan.baseY(), context.heightAccessor()); - } else { - NativeStructureVerticalPlacer.applyVerticalPlacement( - generated, - plan.source().getStructure(), - 0, - context.seaLevel(), - context.heightAccessor().getMinY(), - Math.addExact(context.heightAccessor().getMinY(), context.heightAccessor().getHeight()), - false, - false, - null, - context.surfaceHeight()); - positioned = generated; - } - if (!positioned.isValid()) { - return StructureStart.INVALID_START; - } - return NativeStructureReferenceEnvelope.wrapForPublication( - positioned, - source, - references, - plan.placement().resolvedTerrain(), - plan.source().getStructure() - ); - } - - public static JigsawSourceMetadata sourceMetadata(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source) { - CompoundTag sourceTag = encodeJigsaw(registryAccess, source); - return new JigsawSourceMetadata( - sourceDistance(sourceTag, "horizontal"), - horizontalReferenceExpansion(source), - NativeStructureTemplatePoolBounds.sourceHorizontalSpan( - registryAccess, templateManager, source)); - } - - public static int templatePoolHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - String templatePoolKey) { - return NativeStructureTemplatePoolBounds.horizontalSpan( - registryAccess, templateManager, templatePoolKey); - } - - public static int jigsawStartPoolHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source, - String templatePoolKey) { - return NativeStructureTemplatePoolBounds.horizontalSpan( - registryAccess, templateManager, source, templatePoolKey); - } - - static Structure configure(RegistryAccess registryAccess, Structure source, - IrisJigsawConfiguration configuration, - boolean underground, int baseY) { - Objects.requireNonNull(source, "Native structure source must not be null"); - if (!(source instanceof JigsawStructure sourceJigsaw)) { - if (configuration != null) { - throw new IllegalStateException("Jigsaw options require a registered jigsaw structure, found " - + source.getClass().getName()); - } - return source; - } - RegistryOps registryOps = RegistryOps.create(NbtOps.INSTANCE, registryAccess); - CompoundTag structureTag = encodeJigsaw(registryOps, sourceJigsaw); - CompoundTag configuredTag = structureTag.copy(); - if (underground) { - CompoundTag height = new CompoundTag(); - height.putInt("absolute", baseY); - configuredTag.put("start_height", height); - configuredTag.remove("project_start_to_heightmap"); - } - applyConfiguration(configuredTag, configuration); - Structure decoded = Structure.DIRECT_CODEC.parse(registryOps, configuredTag).getOrThrow(); - if (!(decoded instanceof JigsawStructure configured)) { - throw new IllegalStateException("Configured native jigsaw codec produced " - + decoded.getClass().getName()); - } - return configured; - } - - private static CompoundTag encodeJigsaw(RegistryAccess registryAccess, - JigsawStructure source) { - return encodeJigsaw(RegistryOps.create(NbtOps.INSTANCE, registryAccess), source); - } - - private static CompoundTag encodeJigsaw(RegistryOps registryOps, - JigsawStructure source) { - Tag encoded = Structure.DIRECT_CODEC.encodeStart(registryOps, source).getOrThrow(); - if (!(encoded instanceof CompoundTag structureTag)) { - throw new IllegalStateException("Native jigsaw codec did not produce a compound"); - } - return structureTag; - } - - private static void applyConfiguration(CompoundTag tag, IrisJigsawConfiguration configuration) { - if (configuration == null) { - return; - } - if (configuration.getStartPool() != null && !configuration.getStartPool().isBlank()) { - tag.putString("start_pool", configuration.getStartPool().trim()); - } - if (configuration.getStartJigsawName() != null - && !configuration.getStartJigsawName().isBlank()) { - if ("NONE".equalsIgnoreCase(configuration.getStartJigsawName().trim())) { - tag.remove("start_jigsaw_name"); - } else { - tag.putString("start_jigsaw_name", configuration.getStartJigsawName().trim()); - } - } - if (configuration.getMaxDepth() != null) { - tag.putInt("size", configuration.getMaxDepth()); - } - applyDistance(tag, configuration); - if (configuration.getUseExpansionHack() != null) { - tag.putBoolean("use_expansion_hack", configuration.getUseExpansionHack()); - } - applyHeightmap(tag, configuration.getProjectStartToHeightmap()); - applyDimensionPadding(tag, configuration); - applyLiquidSettings(tag, configuration.getLiquidSettings()); - } - - private static void applyDistance(CompoundTag tag, IrisJigsawConfiguration configuration) { - Integer horizontal = configuration.getMaxDistanceHorizontal(); - Integer vertical = configuration.getMaxDistanceVertical(); - if (horizontal == null && vertical == null) { - return; - } - int resolvedHorizontal = horizontal == null ? sourceDistance(tag, "horizontal") : horizontal; - int resolvedVertical = vertical == null ? sourceDistance(tag, "vertical") : vertical; - if (resolvedHorizontal == resolvedVertical) { - tag.putInt("max_distance_from_center", resolvedHorizontal); - return; - } - CompoundTag distance = new CompoundTag(); - distance.putInt("horizontal", resolvedHorizontal); - distance.putInt("vertical", resolvedVertical); - tag.put("max_distance_from_center", distance); - } - - static int sourceDistance(CompoundTag tag, String axis) { - Tag raw = tag.get("max_distance_from_center"); - if (raw instanceof CompoundTag compound) { - return compound.getIntOr(axis, 128); - } - return tag.getIntOr("max_distance_from_center", 128); - } - - static int horizontalReferenceExpansion(Structure structure) { - BoundingBox content = new BoundingBox(0, 0, 0, 0, 0, 0); - BoundingBox adjusted = structure.adjustBoundingBox(content); - int expansion = Math.max(content.minX() - adjusted.minX(), adjusted.maxX() - content.maxX()); - expansion = Math.max(expansion, content.minZ() - adjusted.minZ()); - return Math.max(0, Math.max(expansion, adjusted.maxZ() - content.maxZ())); - } - - private static void applyHeightmap(CompoundTag tag, IrisJigsawHeightmap heightmap) { - if (heightmap == null || heightmap == IrisJigsawHeightmap.SOURCE) { - return; - } - if (heightmap == IrisJigsawHeightmap.NONE) { - tag.remove("project_start_to_heightmap"); - return; - } - tag.putString("project_start_to_heightmap", heightmap.name()); - } - - private static void applyDimensionPadding(CompoundTag tag, IrisJigsawConfiguration configuration) { - Integer bottom = configuration.getDimensionPaddingBottom(); - Integer top = configuration.getDimensionPaddingTop(); - if (bottom == null && top == null) { - return; - } - int resolvedBottom = bottom == null ? sourcePadding(tag, "bottom") : bottom; - int resolvedTop = top == null ? sourcePadding(tag, "top") : top; - if (resolvedBottom == resolvedTop) { - tag.putInt("dimension_padding", resolvedBottom); - return; - } - CompoundTag padding = new CompoundTag(); - padding.putInt("bottom", resolvedBottom); - padding.putInt("top", resolvedTop); - tag.put("dimension_padding", padding); - } - - private static int sourcePadding(CompoundTag tag, String side) { - Tag raw = tag.get("dimension_padding"); - if (raw instanceof CompoundTag compound) { - return compound.getIntOr(side, 0); - } - return tag.getIntOr("dimension_padding", 0); - } - - private static void applyLiquidSettings(CompoundTag tag, IrisJigsawLiquidSettings settings) { - if (settings == null || settings == IrisJigsawLiquidSettings.SOURCE) { - return; - } - tag.putString("liquid_settings", settings.name().toLowerCase(Locale.ROOT)); - } - - public record GenerationContext( - RegistryAccess registryAccess, - ChunkGenerator generator, - BiomeSource biomeSource, - RandomState randomState, - StructureTemplateManager templateManager, - long levelSeed, - ResourceKey levelKey, - LevelHeightAccessor heightAccessor, - Predicate> biomePredicate, - int seaLevel, - IntBinaryOperator surfaceHeight - ) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFoundationBuilder.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFoundationBuilder.java deleted file mode 100644 index daded307f..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureFoundationBuilder.java +++ /dev/null @@ -1,212 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.placement.IrisStructureStiltSettings; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.SupportType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.List; -import java.util.Objects; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureFoundationBuilder { - private static final int FOUNDATION_VERTICAL_TOLERANCE = 1; - - private NativeStructureFoundationBuilder() { - } - - private static List foundationEnvelope(BoundingBox area, StructureStart start) { - BoundingBox structure = NativeStructureReferenceEnvelope.contentBounds(start); - int minX = Math.max(area.minX(), structure.minX()); - int minZ = Math.max(area.minZ(), structure.minZ()); - int maxX = Math.min(area.maxX(), structure.maxX()); - int maxZ = Math.min(area.maxZ(), structure.maxZ()); - if (minX > maxX || minZ > maxZ) { - return List.of(); - } - List pieces = start.getPieces(); - List columns = new ArrayList<>((maxX - minX + 1) * (maxZ - minZ + 1)); - BitSet envelope = new BitSet(area.getYSpan()); - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - envelope.clear(); - markFoundationEnvelope(envelope, pieces, area, x, z); - int cellCount = envelope.cardinality(); - if (cellCount == 0) { - continue; - } - int[] ys = new int[cellCount]; - int cell = 0; - for (int bit = envelope.nextSetBit(0); bit >= 0; bit = envelope.nextSetBit(bit + 1)) { - ys[cell++] = area.minY() + bit; - } - columns.add(new FoundationColumn(x, z, ys)); - } - } - return List.copyOf(columns); - } - - private static void markFoundationEnvelope(BitSet envelope, List pieces, BoundingBox area, - int x, int z) { - for (StructurePiece piece : pieces) { - BoundingBox bounds = piece.getBoundingBox(); - if (x < bounds.minX() || x > bounds.maxX() || z < bounds.minZ() || z > bounds.maxZ()) { - continue; - } - int groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - if (groundY < bounds.minY()) { - continue; - } - } - int minY = Math.max(area.minY(), bounds.minY()); - int maxY = Math.min(area.maxY(), Math.min(bounds.maxY(), groundY + FOUNDATION_VERTICAL_TOLERANCE)); - if (minY <= maxY) { - envelope.set(minY - area.minY(), maxY - area.minY() + 1); - } - } - } - - static void placeStilts(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureStiltSettings settings, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver, - IntBinaryOperator surfaceHeight, - boolean surfaceStructure) { - Objects.requireNonNull(surfaceHeight, "Structure stilts require an Iris terrain height resolver"); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - int structureHash = structureId == null ? 0 : structureId.hashCode(); - RNG rng = new RNG(world.getSeed() ^ structureHash); - for (FoundationColumn column : foundationEnvelope(area, start)) { - if (!isStiltColumn(column.x(), column.z(), settings.getSpacing())) { - continue; - } - int foundationY = findFoundationY(world, column, position); - if (foundationY == Integer.MIN_VALUE) { - continue; - } - int terrainY = surfaceStructure - ? Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(column.x(), column.z()))) - : area.minY() - 1; - int anchorY = findStiltAnchorY( - world, column.x(), column.z(), foundationY, - Math.max(1, settings.getMaxDepth()), terrainY, area.minY(), position); - if (anchorY == Integer.MIN_VALUE) { - continue; - } - for (int y = foundationY - 1; y > anchorY; y--) { - position.set(column.x(), y, column.z()); - BlockState stilt = settings.getPalette() == null - ? Blocks.COBBLESTONE.defaultBlockState() - : Objects.requireNonNull( - paletteBlockResolver.resolve( - settings.getPalette(), rng, column.x(), y, column.z()), - "Stilt palette returned no block for " + structureId + " at " - + column.x() + "," + y + "," + column.z()); - world.setBlock(position, stilt, 2); - } - } - } - - static boolean isStiltColumn(int x, int z, int spacing) { - int resolvedSpacing = Math.max(1, spacing); - return resolvedSpacing == 1 - || Math.floorMod(x, resolvedSpacing) == 0 - && Math.floorMod(z, resolvedSpacing) == 0; - } - - static int findStiltAnchorY( - WorldGenLevel world, int x, int z, int foundationY, int maxDepth, - int terrainY, int areaMinY, BlockPos.MutableBlockPos position) { - int minimumAnchorY = Math.max( - areaMinY, Math.max(terrainY, foundationY - maxDepth - 1)); - for (int y = foundationY - 1; y >= minimumAnchorY; y--) { - BlockState state = world.getBlockState(position.set(x, y, z)); - boolean vegetation = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - if (!vegetation && state.isFaceSturdy( - world, position, Direction.UP, SupportType.FULL)) { - return y; - } - } - return Integer.MIN_VALUE; - } - - public static StiltSupportAudit auditStiltSupport(WorldGenLevel world, BoundingBox area, - StructureStart start, BlockState expectedStilt, - IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(expectedStilt, "Expected stilt state must not be null"); - Objects.requireNonNull(surfaceHeight, "Structure stilt audit requires an Iris terrain height resolver"); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - int baseColumns = 0; - int stiltBlocks = 0; - int stiltColumns = 0; - int unsupportedColumns = 0; - for (FoundationColumn column : foundationEnvelope(area, start)) { - int foundationY = findFoundationY(world, column, position); - if (foundationY == Integer.MIN_VALUE) { - continue; - } - baseColumns++; - int terrainY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(column.x(), column.z()))); - boolean grounded = foundationY <= terrainY + 1; - boolean stiltColumn = false; - for (int y = foundationY - 1; y >= area.minY(); y--) { - if (y <= terrainY) { - grounded = true; - break; - } - BlockState state = world.getBlockState(position.set(column.x(), y, column.z())); - if (state.is(expectedStilt.getBlock())) { - stiltBlocks++; - stiltColumn = true; - if (y == area.minY()) { - grounded = true; - } - continue; - } - boolean vegetation = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - grounded = state.isSolid() && !vegetation; - break; - } - if (stiltColumn) { - stiltColumns++; - } - if (!grounded) { - unsupportedColumns++; - } - } - return new StiltSupportAudit(baseColumns, stiltBlocks, stiltColumns, unsupportedColumns); - } - - private static int findFoundationY(WorldGenLevel world, FoundationColumn column, - BlockPos.MutableBlockPos position) { - for (int cell = 0; cell < column.ys().length; cell++) { - int y = column.ys()[cell]; - BlockState state = world.getBlockState(position.set(column.x(), y, column.z())); - if (state.isSolid()) { - return y; - } - } - return Integer.MIN_VALUE; - } - - private record FoundationColumn(int x, int z, int[] ys) { - } - - public record StiltSupportAudit(int baseColumns, int stiltBlocks, int stiltColumns, - int unsupportedColumns) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationException.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationException.java deleted file mode 100644 index 738613a3e..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationException.java +++ /dev/null @@ -1,35 +0,0 @@ -package art.arcane.iris.nativegen; - -public final class NativeStructureGenerationException extends IllegalStateException { - private NativeStructureGenerationException(FailureContext context, Throwable cause) { - super(message(context), cause); - } - - private NativeStructureGenerationException(FailureContext context) { - super(message(context)); - } - - public static NativeStructureGenerationException failure(String operation, String structureContext, - int chunkX, int chunkZ, Throwable cause) { - return new NativeStructureGenerationException( - new FailureContext(operation, structureContext, chunkX, chunkZ), cause); - } - - public static NativeStructureGenerationException failure(String operation, String structureContext, - int chunkX, int chunkZ) { - return new NativeStructureGenerationException( - new FailureContext(operation, structureContext, chunkX, chunkZ)); - } - - private static String message(FailureContext context) { - String resolvedContext = context.structureContext() == null || context.structureContext().isBlank() - ? "" - : context.structureContext(); - return "Iris native structure " + context.operation() + " failed for " + resolvedContext - + " in chunk " + context.chunkX() + "," + context.chunkZ() - + "; chunk generation was aborted to prevent partial structure state"; - } - - private record FailureContext(String operation, String structureContext, int chunkX, int chunkZ) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationKeys.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationKeys.java deleted file mode 100644 index aa88abae9..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureGenerationKeys.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.nativegen; - -import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -public final class NativeStructureGenerationKeys { - private static final Method FREQUENCY_METHOD = resolveFrequencyMethod(); - - private NativeStructureGenerationKeys() { - } - - public static Set reachable(ServerLevel level, Set possibleBiomeKeys) { - ServerLevel activeLevel = Objects.requireNonNull(level, "Native structure reachability requires a level"); - Set possibleBiomes = Objects.requireNonNull( - possibleBiomeKeys, "Native structure reachability requires possible biome keys"); - if (!activeLevel.getServer().getWorldGenSettings().options().generateStructures()) { - return Collections.emptySet(); - } - - Set keys = new LinkedHashSet<>(); - List> structureSets = - activeLevel.getChunkSource().getGeneratorState().possibleStructureSets(); - for (Holder structureSetHolder : structureSets) { - StructureSet structureSet = structureSetHolder.value(); - if (!isEnabledPlacement(structureSet.placement())) { - continue; - } - for (StructureSet.StructureSelectionEntry entry : structureSet.structures()) { - if (entry.weight() <= 0 || !hasPossibleBiome(entry.structure().value(), possibleBiomes)) { - continue; - } - Optional> key = entry.structure().unwrapKey(); - key.ifPresent(resourceKey -> keys.add(resourceKey.identifier().toString())); - } - } - return Collections.unmodifiableSet(keys); - } - - static boolean hasPossibleBiome(Structure structure, Set possibleBiomeKeys) { - for (Holder holder : structure.biomes()) { - Optional> key = holder.unwrapKey(); - if (key.isPresent() && possibleBiomeKeys.contains(key.get().identifier().toString())) { - return true; - } - } - return false; - } - - static boolean isEnabledPlacement(StructurePlacement placement) { - return placementFrequency(placement) > 0.0F; - } - - private static float placementFrequency(StructurePlacement placement) { - StructurePlacement activePlacement = Objects.requireNonNull( - placement, "Native structure placement frequency requires a placement"); - try { - return ((Float) FREQUENCY_METHOD.invoke(activePlacement)).floatValue(); - } catch (ReflectiveOperationException error) { - throw new IllegalStateException( - "Unable to read native structure placement frequency from " - + activePlacement.getClass().getName(), error); - } - } - - private static Method resolveFrequencyMethod() { - Method frequencyMethod = null; - for (Method method : StructurePlacement.class.getDeclaredMethods()) { - if (Modifier.isStatic(method.getModifiers()) - || method.getParameterCount() != 0 - || method.getReturnType() != float.class) { - continue; - } - if (frequencyMethod != null) { - throw new IllegalStateException( - "Native structure placement exposes multiple zero-argument float accessors"); - } - frequencyMethod = method; - } - if (frequencyMethod == null || !frequencyMethod.trySetAccessible()) { - throw new IllegalStateException( - "Native structure placement frequency accessor is unavailable"); - } - return frequencyMethod; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocatePersistence.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocatePersistence.java deleted file mode 100644 index 4dfc6df06..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocatePersistence.java +++ /dev/null @@ -1,351 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.placement.IrisStructureLocator; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.SharedConstants; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.StreamTagVisitor; -import net.minecraft.nbt.Tag; -import net.minecraft.nbt.visitors.CollectFields; -import net.minecraft.nbt.visitors.FieldSelector; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.datafix.DataFixTypes; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SimpleRegionStorage; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -public final class NativeStructureLocatePersistence { - public static final int MAX_SELECTED_CANDIDATE_RETRIES = 64; - private static final int MAX_STORAGE_PROBES = 512; - private static final Method PAPER_LEVEL_TYPE_KEY = paperLevelTypeKey(); - - private NativeStructureLocatePersistence() { - } - - public static Probe probe(ServerLevel level, Structure structure, boolean requireUnreferenced) { - return probe(level, structure, requireUnreferenced, new ProbeBudget(MAX_STORAGE_PROBES)); - } - - public static ProbeBudget probeBudget() { - return new ProbeBudget(MAX_STORAGE_PROBES); - } - - public static Probe probe(ServerLevel level, Structure structure, boolean requireUnreferenced, - ProbeBudget budget) { - return new Probe(level, structure, requireUnreferenced, budget); - } - - public static Search search(Engine engine, String structureKey, - int blockX, int blockZ, int radius, Probe probe) { - return new Search(engine, structureKey, blockX, blockZ, radius, probe); - } - - public static final class Probe { - private final ServerLevel level; - private final Structure structure; - private final String structureKey; - private final boolean requireUnreferenced; - private final ProbeBudget budget; - private final Map storedDecisions = new HashMap<>(); - - Probe(ServerLevel level, Structure structure, boolean requireUnreferenced, - ProbeBudget budget) { - this.level = level; - this.structure = structure; - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Identifier identifier = registry.getKey(structure); - if (identifier == null) { - throw new IllegalStateException("Cannot probe an unregistered native structure"); - } - this.structureKey = identifier.toString(); - this.requireUnreferenced = requireUnreferenced; - this.budget = budget; - } - - public boolean accepts(int chunkX, int chunkZ) { - ChunkAccess loaded = level.getChunkSource().getChunk( - chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS, false); - if (loaded != null) { - StructureStart start = loaded.getStartForStructure(structure); - return start != null && start.isValid() - && (!requireUnreferenced || start.canBeReferenced()); - } - - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - Boolean storedDecision = storedDecisions.get(chunkPos.pack()); - if (storedDecision != null) { - return storedDecision; - } - boolean accepted = budget.acceptsStored( - level, chunkPos, structureKey, requireUnreferenced); - storedDecisions.put(chunkPos.pack(), accepted); - return accepted; - } - - public StructureStart verifySelected(int chunkX, int chunkZ) { - ChunkAccess chunk = level.getChunk(chunkX, chunkZ, ChunkStatus.STRUCTURE_STARTS); - StructureStart start = chunk.getStartForStructure(structure); - if (start == null || !start.isValid()) { - return null; - } - if (requireUnreferenced) { - if (!start.canBeReferenced()) { - return null; - } - } - return start; - } - - private void reference(StructureStart start) { - if (requireUnreferenced) { - level.structureManager().addReference(start); - } - } - } - - public static final class ProbeBudget { - private final int maximum; - private final Map storedChunks = new HashMap<>(); - private final ChunkStorageScanner scanner; - private final StoredChunkDatafixer datafixer; - private int used; - private boolean scanFailureReported; - - private ProbeBudget(int maximum) { - this(maximum, null, ProbeBudget::datafix); - } - - ProbeBudget(int maximum, ChunkStorageScanner scanner) { - this(maximum, scanner, ProbeBudget::datafix); - } - - ProbeBudget(int maximum, ChunkStorageScanner scanner, - StoredChunkDatafixer datafixer) { - this.maximum = maximum; - this.scanner = scanner; - this.datafixer = Objects.requireNonNull( - datafixer, "Stored chunk datafixer must not be null"); - } - - private void claim() { - if (used >= maximum) { - throw new IrisStructureLocator.CandidateSearchLimitException(); - } - used++; - } - - private StoredChunkState storedState(ServerLevel level, ChunkPos chunkPos) { - StoredChunkState cached = storedChunks.get(chunkPos.pack()); - if (cached != null) { - return cached; - } - claim(); - CollectFields fields = new CollectFields( - new FieldSelector(IntTag.TYPE, "DataVersion"), - new FieldSelector("Level", "Structures", CompoundTag.TYPE, "Starts"), - new FieldSelector("structures", CompoundTag.TYPE, "starts")); - ChunkStorageScanner activeScanner = scanner == null - ? level.getChunkSource().chunkMap.chunkScanner()::scanChunk : scanner; - try { - activeScanner.scan(chunkPos, fields).join(); - } catch (RuntimeException error) { - if (!scanFailureReported) { - scanFailureReported = true; - IrisLogging.reportError( - "Native structure locate could not scan stored chunk state; candidates will be verified by loading their structure starts.", - error); - } - StoredChunkState unresolved = StoredChunkState.missing(); - storedChunks.put(chunkPos.pack(), unresolved); - return unresolved; - } - Tag result = fields.getResult(); - StoredChunkState resolved; - try { - resolved = result instanceof CompoundTag storedChunk - ? StoredChunkState.parse(datafixer.datafix(level, storedChunk)) - : StoredChunkState.missing(); - } catch (RuntimeException error) { - if (!scanFailureReported) { - scanFailureReported = true; - IrisLogging.reportError( - "Native structure locate could not datafix stored chunk state; candidates will be verified by loading their structure starts.", - error); - } - resolved = StoredChunkState.missing(); - } - storedChunks.put(chunkPos.pack(), resolved); - return resolved; - } - - private static CompoundTag datafix(ServerLevel level, CompoundTag storedChunk) { - int dataVersion = NbtUtils.getDataVersion(storedChunk); - int currentDataVersion = SharedConstants.getCurrentVersion().dataVersion().version(); - if (dataVersion >= currentDataVersion) { - return storedChunk; - } - if (level == null) { - throw new IllegalStateException("Stored chunk datafix requires an active level"); - } - CompoundTag context = chunkDataFixContext(level); - SimpleRegionStorage.injectDatafixingContext(storedChunk, context); - return DataFixTypes.CHUNK.updateToCurrentVersion( - level.getServer().getFixerUpper(), storedChunk, dataVersion); - } - - private static CompoundTag chunkDataFixContext(ServerLevel level) { - CompoundTag context = new CompoundTag(); - String levelIdentifier = level.dimension().identifier().toString(); - if (PAPER_LEVEL_TYPE_KEY == null) { - context.putString("dimension", levelIdentifier); - } else { - context.putString("dimension", paperLevelTypeKey(level).identifier().toString()); - context.putString("level_identifier", levelIdentifier); - } - level.getChunkSource().getGenerator().getTypeNameForDataFixer() - .ifPresent(identifier -> context.putString("generator", identifier.toString())); - return context; - } - - boolean acceptsStored(ServerLevel level, ChunkPos chunkPos, - String structureKey, boolean requireUnreferenced) { - return storedState(level, chunkPos).accepts(structureKey, requireUnreferenced); - } - - int used() { - return used; - } - } - - public static final class Search { - private final Engine engine; - private final String structureKey; - private final int blockX; - private final int blockZ; - private final int radius; - private final Probe probe; - private final Set rejectedChunks = new HashSet<>(); - - private Search(Engine engine, String structureKey, int blockX, int blockZ, - int radius, Probe probe) { - this.engine = engine; - this.structureKey = structureKey; - this.blockX = blockX; - this.blockZ = blockZ; - this.radius = radius; - this.probe = probe; - } - - public IrisStructureLocator.LocateResult predict() { - if (rejectedChunks.size() >= MAX_SELECTED_CANDIDATE_RETRIES) { - return new IrisStructureLocator.LocateResult( - IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED, 0, 0, 0); - } - return IrisStructureLocator.locateInPlacementRings( - engine, structureKey, blockX, blockZ, radius, - (chunkX, chunkZ) -> !rejectedChunks.contains(ChunkPos.pack(chunkX, chunkZ)) - && probe.accepts(chunkX, chunkZ)); - } - - public VerifiedStart verify(IrisStructureLocator.LocateResult result) { - int chunkX = result.originX() >> 4; - int chunkZ = result.originZ() >> 4; - StructureStart start = probe.verifySelected(chunkX, chunkZ); - if (start == null) { - return null; - } - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecovery.resolve( - engine, probe.level, structureKey, probe.structure, start); - if (ownership == null) { - return null; - } - return new VerifiedStart(start, ownership); - } - - public void reject(IrisStructureLocator.LocateResult result) { - rejectedChunks.add(ChunkPos.pack(result.originX() >> 4, result.originZ() >> 4)); - } - - public void reference(VerifiedStart verified) { - probe.reference(verified.start()); - } - } - - private static Method paperLevelTypeKey() { - try { - return ServerLevel.class.getMethod("getTypeKey"); - } catch (NoSuchMethodException ignored) { - return null; - } - } - - private static ResourceKey paperLevelTypeKey(ServerLevel level) { - try { - Object value = PAPER_LEVEL_TYPE_KEY.invoke(level); - if (value instanceof ResourceKey key) { - return key; - } - throw new IllegalStateException("Paper level type key has an unexpected value"); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot read Paper level type key", error); - } - } - - public record VerifiedStart(StructureStart start, - NativeStructureOwnershipRecord ownership) { - } - - @FunctionalInterface - interface ChunkStorageScanner { - CompletableFuture scan(ChunkPos chunkPos, StreamTagVisitor visitor); - } - - @FunctionalInterface - interface StoredChunkDatafixer { - CompoundTag datafix(ServerLevel level, CompoundTag storedChunk); - } - - private record StoredChunkState(boolean stored, boolean startsPresent, CompoundTag starts) { - private static StoredChunkState missing() { - return new StoredChunkState(false, false, new CompoundTag()); - } - - private static StoredChunkState parse(CompoundTag chunk) { - CompoundTag structures = chunk.getCompoundOrEmpty("structures"); - return structures.getCompound("starts") - .map(starts -> new StoredChunkState(true, true, starts)) - .orElseGet(() -> new StoredChunkState(true, false, new CompoundTag())); - } - - private boolean accepts(String structureKey, boolean requireUnreferenced) { - if (!stored || !startsPresent) { - return true; - } - return starts.getCompound(structureKey).map(start -> { - String id = start.getStringOr("id", ""); - return !StructureStart.INVALID_START_ID.equals(id) - && (!requireUnreferenced || start.getIntOr("references", 0) == 0); - }).orElse(false); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocateResults.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocateResults.java deleted file mode 100644 index af9fb21eb..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureLocateResults.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.nativegen; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; - -public final class NativeStructureLocateResults { - private NativeStructureLocateResults() { - } - - public static Pair nearest(BlockPos origin, Pair replacement, - Pair nativeResult) { - if (replacement == null) { - return nativeResult; - } - if (nativeResult == null) { - return replacement; - } - return horizontalDistanceSquared(origin, nativeResult.getFirst()) - <= horizontalDistanceSquared(origin, replacement.getFirst()) ? nativeResult : replacement; - } - - public static Pair selectAndReference( - BlockPos origin, - Pair replacement, Runnable replacementReference, - Pair nativeResult, Runnable nativeReference) { - Pair selected = nearest(origin, replacement, nativeResult); - if (selected == replacement && replacement != null) { - replacementReference.run(); - } else if (selected == nativeResult && nativeResult != null) { - nativeReference.run(); - } - return selected; - } - - private static long horizontalDistanceSquared(BlockPos origin, BlockPos target) { - long dx = (long) target.getX() - origin.getX(); - long dz = (long) target.getZ() - origin.getZ(); - return dx * dx + dz * dz; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprint.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprint.java deleted file mode 100644 index 966047de5..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipFingerprint.java +++ /dev/null @@ -1,239 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.placement.StructurePlacementGrid; -import net.minecraft.core.Direction; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; - -import java.nio.charset.StandardCharsets; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HexFormat; -import java.util.List; -import java.util.Locale; -import java.util.Objects; - -public final class NativeStructureOwnershipFingerprint { - private static final Comparator PIECE_ORDER = Comparator - .comparing(PieceIdentity::type) - .thenComparingInt(PieceIdentity::minX) - .thenComparingInt(PieceIdentity::minY) - .thenComparingInt(PieceIdentity::minZ) - .thenComparingInt(PieceIdentity::maxX) - .thenComparingInt(PieceIdentity::maxY) - .thenComparingInt(PieceIdentity::maxZ) - .thenComparingInt(PieceIdentity::orientation) - .thenComparingInt(PieceIdentity::generationDepth) - .thenComparing(PieceIdentity::detail); - - private NativeStructureOwnershipFingerprint() { - } - - public static NativeStructureOwnershipRecord capture(String structureKey, - StructureStart start, - NativeStructureStartPlan plan, - BoundingBox referenceEnvelope) { - StructureStart resolvedStart = requireValid(start); - NativeStructureStartPlan resolvedPlan = Objects.requireNonNull( - plan, "Native structure start plan must not be null"); - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(resolvedStart); - BoundingBox references = Objects.requireNonNull( - referenceEnvelope, "Native structure reference envelope must not be null"); - ChunkPos origin = resolvedStart.getChunkPos(); - return NativeStructureOwnershipRecord.create( - structureKey, - origin.x(), - origin.z(), - StructurePlacementGrid.placementIdentity(resolvedPlan.placement()), - resolvedPlan.baseY(), - content.minX(), - content.minY(), - content.minZ(), - content.maxX(), - content.maxY(), - content.maxZ(), - locatorY(resolvedStart), - references.minX() >> 4, - references.maxX() >> 4, - references.minZ() >> 4, - references.maxZ() >> 4, - fingerprint(structureKey, resolvedStart), - NativeStructurePlacementPlanner.decisionFor(resolvedPlan) - ); - } - - public static boolean matches(NativeStructureOwnershipRecord record, StructureStart start) { - if (record == null || start == null || !start.isValid()) { - return false; - } - ChunkPos origin = start.getChunkPos(); - if (origin.x() != record.originChunkX() - || origin.z() != record.originChunkZ() - || !fingerprint(record.structureKey(), start).equals(record.contentFingerprint())) { - return false; - } - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(start); - if (content.getXSpan() != (record.contentMaxX() - record.contentMinX()) + 1 - || content.getYSpan() != (record.contentMaxY() - record.contentMinY()) + 1 - || content.getZSpan() != (record.contentMaxZ() - record.contentMinZ()) + 1) { - return false; - } - return start.getStructure() instanceof OceanMonumentStructure - || content.minX() == record.contentMinX() - && content.minY() == record.contentMinY() - && content.minZ() == record.contentMinZ() - && content.maxX() == record.contentMaxX() - && content.maxY() == record.contentMaxY() - && content.maxZ() == record.contentMaxZ(); - } - - public static String fingerprint(String structureKey, StructureStart start) { - StructureStart resolvedStart = requireValid(start); - MessageDigest digest = sha256(); - updateString(digest, Objects.requireNonNull(structureKey, - "Native structure key must not be null").trim().toLowerCase(Locale.ROOT)); - updateInt(digest, resolvedStart.getChunkPos().x()); - updateInt(digest, resolvedStart.getChunkPos().z()); - BoundingBox content = NativeStructureReferenceEnvelope.contentBounds(resolvedStart); - List pieces = contentPieces(resolvedStart, content.minY()); - updateInt(digest, pieces.size()); - for (PieceIdentity piece : pieces) { - updateString(digest, piece.type()); - updateInt(digest, piece.minX()); - updateInt(digest, piece.minY()); - updateInt(digest, piece.minZ()); - updateInt(digest, piece.maxX()); - updateInt(digest, piece.maxY()); - updateInt(digest, piece.maxZ()); - updateInt(digest, piece.orientation()); - updateInt(digest, piece.generationDepth()); - updateString(digest, piece.detail()); - } - return HexFormat.of().formatHex(digest.digest()); - } - - public static int locatorY(StructureStart start) { - StructureStart resolvedStart = requireValid(start); - if (!resolvedStart.getPieces().isEmpty()) { - return resolvedStart.getPieces().getFirst().getLocatorPosition().getY(); - } - throw new IllegalStateException("Native structure contains no locatable content pieces"); - } - - private static List contentPieces(StructureStart start, int contentMinY) { - List pieces = new ArrayList<>(); - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - Identifier type = BuiltInRegistries.STRUCTURE_PIECE.getKey(piece.getType()); - Direction orientation = piece.getOrientation(); - String detail = pieceDetail(piece, contentMinY); - pieces.add(new PieceIdentity( - type == null ? "" : type.toString(), - bounds.minX(), bounds.minY() - contentMinY, bounds.minZ(), - bounds.maxX(), bounds.maxY() - contentMinY, bounds.maxZ(), - orientation == null ? -1 : orientation.get2DDataValue(), - piece.getGenDepth(), - detail - )); - } - if (pieces.isEmpty()) { - throw new IllegalStateException("Native structure contains no content pieces"); - } - pieces.sort(PIECE_ORDER); - return List.copyOf(pieces); - } - - private static String pieceDetail(StructurePiece piece, int contentMinY) { - if (!(piece instanceof PoolElementStructurePiece poolPiece)) { - return ""; - } - return poolElementIdentity(poolPiece.getElement()) + "|" - + poolPiece.getPosition().getX() + "," - + (poolPiece.getPosition().getY() - contentMinY) + "," - + poolPiece.getPosition().getZ() + "|" - + poolPiece.getGroundLevelDelta() + "|" - + poolPiece.getRotation().getSerializedName(); - } - - private static String poolElementIdentity(StructurePoolElement element) { - Identifier type = BuiltInRegistries.STRUCTURE_POOL_ELEMENT.getKey(element.getType()); - StringBuilder identity = new StringBuilder(element.getClass().getName()) - .append('|') - .append(type == null ? "" : type) - .append('|') - .append(element.getProjection().getSerializedName()) - .append('|') - .append(element.getGroundLevelDelta()); - if (element == EmptyPoolElement.INSTANCE) { - return identity.append("|empty").toString(); - } - if (element instanceof SinglePoolElement single) { - try { - return identity.append("|template=") - .append(single.getTemplateLocation()).toString(); - } catch (RuntimeException inlineTemplate) { - return identity.append("|runtime-template").toString(); - } - } - if (element instanceof ListPoolElement list) { - identity.append("|list["); - for (StructurePoolElement child : list.getElements()) { - String childIdentity = poolElementIdentity(child); - identity.append(childIdentity.length()).append(':').append(childIdentity); - } - return identity.append(']').toString(); - } - return identity.append("|bounded-custom").toString(); - } - - private static StructureStart requireValid(StructureStart start) { - StructureStart resolved = Objects.requireNonNull( - start, "Native structure start must not be null"); - if (!resolved.isValid()) { - throw new IllegalArgumentException("Native structure start must be valid"); - } - return resolved; - } - - private static MessageDigest sha256() { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException error) { - throw new IllegalStateException("SHA-256 is unavailable", error); - } - } - - private static void updateString(MessageDigest digest, String value) { - byte[] encoded = value.getBytes(StandardCharsets.UTF_8); - updateInt(digest, encoded.length); - digest.update(encoded); - } - - private static void updateInt(MessageDigest digest, int value) { - digest.update((byte) (value >>> 24)); - digest.update((byte) (value >>> 16)); - digest.update((byte) (value >>> 8)); - digest.update((byte) value); - } - - private record PieceIdentity(String type, - int minX, int minY, int minZ, - int maxX, int maxY, int maxZ, - int orientation, int generationDepth, String detail) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecovery.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecovery.java deleted file mode 100644 index 805b6cc9e..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureOwnershipRecovery.java +++ /dev/null @@ -1,195 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.placement.StructurePlacementGrid; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.structure.nativegen.NativeStructureSuppression; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.util.Locale; -import java.util.Objects; - -public final class NativeStructureOwnershipRecovery { - private NativeStructureOwnershipRecovery() { - } - - public static NativeStructureOwnershipRecord resolve( - Engine engine, ServerLevel level, String structureKey, - Structure structure, StructureStart start) { - Objects.requireNonNull(engine, "Native structure ownership recovery requires an engine"); - ServerLevel activeLevel = Objects.requireNonNull( - level, "Native structure ownership recovery requires a level"); - Structure activeStructure = Objects.requireNonNull( - structure, "Native structure ownership recovery requires a structure"); - if (start == null || !start.isValid() || start.getStructure() != activeStructure) { - return null; - } - ChunkPos origin = start.getChunkPos(); - NativeStructureOwnershipRecord persisted = NativeStructureOwnershipStore.findPersisted( - engine, structureKey, origin.x(), origin.z()); - if (persisted != null) { - NativeStructureOwnershipRecord refreshed = refreshReferenceEnvelope( - structureKey, activeStructure, start, persisted); - if (refreshed != null) { - if (refreshed != persisted) { - NativeStructureOwnershipStore.record(engine, refreshed); - } - return refreshed; - } - NativeStructureOwnershipStore.discard( - engine, structureKey, origin.x(), origin.z()); - } - NativeStructureStartPlan plan = NativeStructurePlacementPlanner.matchingPlan( - engine, structureKey, origin.x(), origin.z()); - if (!matchesPlan(structureKey, origin, plan)) { - return null; - } - Registry registry = activeLevel.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Holder holder = registry.wrapAsHolder(activeStructure); - ChunkGeneratorStructureState state = activeLevel.getChunkSource().getGeneratorState(); - if (naturalStartIsAmbiguous(engine, structureKey, activeStructure, holder, state, plan)) { - return null; - } - StructureStart expected = generateExpected( - engine, activeLevel, holder, plan, start.getReferences()); - NativeStructureOwnershipRecord recovered = proveCandidate( - structureKey, activeStructure, start, plan, expected, false); - if (recovered == null) { - return null; - } - NativeStructureOwnershipStore.record(engine, recovered); - return recovered; - } - - static NativeStructureOwnershipRecord refreshReferenceEnvelope( - String structureKey, Structure structure, StructureStart start, - NativeStructureOwnershipRecord ownership) { - if (structure == null || start == null || !start.isValid() - || start.getStructure() != structure || ownership == null - || !ownership.structureKey().equals(normalize(structureKey)) - || !NativeStructureOwnershipFingerprint.matches(ownership, start)) { - return null; - } - IrisStructureTerrain terrain = NativeStructureTerrainIntegrator.resolveNativeTerrain( - start, ownership.restoredDecision().terrain()); - if (terrain.resolvedMode() != IrisStructureTerrainMode.VACUUM) { - return ownership; - } - BoundingBox expected = NativeStructureReferenceEnvelope.referenceBounds( - start, structure, terrain, structureKey); - int referenceMinChunkX = expected.minX() >> 4; - int referenceMaxChunkX = expected.maxX() >> 4; - int referenceMinChunkZ = expected.minZ() >> 4; - int referenceMaxChunkZ = expected.maxZ() >> 4; - if (ownership.referenceMinChunkX() == referenceMinChunkX - && ownership.referenceMaxChunkX() == referenceMaxChunkX - && ownership.referenceMinChunkZ() == referenceMinChunkZ - && ownership.referenceMaxChunkZ() == referenceMaxChunkZ) { - return ownership; - } - return ownership.withReferenceEnvelope( - referenceMinChunkX, referenceMaxChunkX, - referenceMinChunkZ, referenceMaxChunkZ); - } - - static NativeStructureOwnershipRecord proveCandidate( - String structureKey, Structure structure, StructureStart persisted, - NativeStructureStartPlan plan, StructureStart expected, - boolean naturalStartAmbiguous) { - if (naturalStartAmbiguous || structure == null - || persisted == null || !persisted.isValid() - || expected == null || !expected.isValid() - || persisted.getStructure() != structure - || expected.getStructure() != structure - || !matchesPlan(structureKey, persisted.getChunkPos(), plan) - || !persisted.getChunkPos().equals(expected.getChunkPos())) { - return null; - } - BoundingBox referenceEnvelope = NativeStructureReferenceEnvelope.referenceBounds( - expected, structure, plan.placement().resolvedTerrain(), structureKey); - NativeStructureOwnershipRecord candidate = NativeStructureOwnershipFingerprint.capture( - structureKey, expected, plan, referenceEnvelope); - if (candidate.placementIdentity() - != StructurePlacementGrid.placementIdentity(plan.placement())) { - return null; - } - return NativeStructureOwnershipFingerprint.matches(candidate, persisted) - ? candidate : null; - } - - private static boolean matchesPlan(String structureKey, ChunkPos origin, - NativeStructureStartPlan plan) { - if (structureKey == null || structureKey.isBlank() || plan == null) { - return false; - } - return plan.chunkX() == origin.x() - && plan.chunkZ() == origin.z() - && normalize(structureKey).equals(normalize(plan.source().getStructure())); - } - - private static StructureStart generateExpected( - Engine engine, ServerLevel level, Holder holder, - NativeStructureStartPlan plan, int references) { - ChunkGeneratorStructureState state = level.getChunkSource().getGeneratorState(); - ChunkGenerator generator = level.getChunkSource().getGenerator(); - BiomeSource biomeSource = generator.getBiomeSource(); - NativeStructureFactory.GenerationContext generationContext = - new NativeStructureFactory.GenerationContext( - level.registryAccess(), - generator, - biomeSource, - state.randomState(), - level.getStructureManager(), - state.getLevelSeed(), - level.dimension(), - level, - biome -> true, - generator.getSeaLevel(), - (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight() - ); - return NativeStructureFactory.generate(generationContext, holder, plan, references); - } - - private static boolean naturalStartIsAmbiguous( - Engine engine, String structureKey, Structure structure, - Holder holder, ChunkGeneratorStructureState state, - NativeStructureStartPlan plan) { - if (plan.placement().getNativeSuppression() == NativeStructureSuppression.REPLACE_SOURCE) { - return false; - } - NativeStructureGenerationStatus sourceStatus = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())).status(); - if (sourceStatus == NativeStructureGenerationStatus.REPLACED_BY_IRIS) { - return false; - } - for (StructurePlacement placement : state.getPlacementsForStructure(holder)) { - if (placement.isStructureChunk(state, plan.chunkX(), plan.chunkZ())) { - return true; - } - } - return false; - } - - private static String normalize(String structureKey) { - return structureKey == null ? "" : structureKey.trim().toLowerCase(Locale.ROOT); - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructurePostProcessor.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructurePostProcessor.java deleted file mode 100644 index 12498c842..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructurePostProcessor.java +++ /dev/null @@ -1,66 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.structure.placement.IrisStructureStiltSettings; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.List; -import java.util.function.IntBinaryOperator; - -public final class NativeStructurePostProcessor { - private NativeStructurePostProcessor() { - } - - public static void place(WorldGenLevel world, StructureManager structureManager, ChunkGenerator generator, - WorldgenRandom random, BoundingBox area, ChunkPos chunkPos, String structureId, - StructureStart start, IrisNativeStructureDecision decision, - PaletteBlockResolver paletteBlockResolver, - IntBinaryOperator surfaceHeight) { - IrisStructureStiltSettings stilt = decision.stilt(); - NativeStructureVerticalPlacer.ensureMonumentSeaLevelAlignment(start, structureId, decision.yShift(), - generator.getSeaLevel(), area.minY(), area.maxY() + 1); - start.placeInChunk(world, structureManager, generator, random, area, chunkPos); - if (stilt != null) { - NativeStructureFoundationBuilder.placeStilts(world, area, structureId, start, stilt, - paletteBlockResolver, surfaceHeight, - !NativeStructureVegetationClearer.isUndergroundStep(start.getStructure().step())); - } - } - - public static void prepareTerrain(WorldGenLevel world, BoundingBox area, - List targets, - PaletteBlockResolver paletteBlockResolver) { - if (targets == null || targets.isEmpty()) { - return; - } - NativeStructureTerrainIntegrator.SourceTerrainSnapshot sourceTerrain = - NativeStructureTerrainIntegrator.captureSourceTerrain(world, area, targets); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - NativeStructureTerrainIntegrator.integrateTerrain(world, area, target.structureId(), target.start(), - target.terrain(), paletteBlockResolver, sourceTerrain); - } - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (NativeStructureTerrainIntegrator.clearsLegacyTemplateAir( - target.start(), target.terrain())) { - NativeStructureTerrainIntegrator.clearLegacyTemplateAir( - world, area, target.start(), () -> world.getLevel().getStructureManager()); - } - } - NativeStructureSurfaceSupportBuilder.bridgeRigidPieceSupport( - world, area, targets, () -> world.getLevel().getStructureManager()); - } - - @FunctionalInterface - public interface PaletteBlockResolver { - BlockState resolve(IrisMaterialPalette palette, RNG rng, int x, int y, int z); - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceEnvelope.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceEnvelope.java deleted file mode 100644 index 9ee177491..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceEnvelope.java +++ /dev/null @@ -1,239 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; - -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureReferenceEnvelope { - private static final int MAX_REFERENCE_DISTANCE_CHUNKS = 8; - private static final Set WARNED_CLIPPED_TERRAIN = ConcurrentHashMap.newKeySet(); - private static final Set WARNED_SKIPPED_CONTENT = ConcurrentHashMap.newKeySet(); - - private NativeStructureReferenceEnvelope() { - } - - public static StructureStart wrap(StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain) { - return wrap(generated, source, references, terrain, null); - } - - public static StructureStart wrap(StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain, String structureKey) { - referenceBounds(generated, source, terrain, structureKey); - return new StructureStart( - source, - generated.getChunkPos(), - references, - new PiecesContainer(List.copyOf(generated.getPieces())) - ); - } - - public static StructureStart wrapForPublication( - StructureStart generated, Structure source, int references, - IrisStructureTerrain terrain, String structureKey) { - try { - return wrap(generated, source, references, terrain, structureKey); - } catch (UnrepresentableContentException exception) { - warnSkippedContent(generated, source, structureKey); - return StructureStart.INVALID_START; - } - } - - public static BoundingBox referenceBounds(StructureStart start, Structure source, - IrisStructureTerrain terrain) { - return referenceBounds(start, source, terrain, null); - } - - public static BoundingBox referenceBounds(StructureStart start, Structure source, - IrisStructureTerrain terrain, - String structureKey) { - IrisStructureTerrainMode mode = terrain == null - ? IrisStructureTerrainMode.PRESERVE : terrain.resolvedMode(); - boolean usesEnvelope = mode == IrisStructureTerrainMode.BORE - || mode == IrisStructureTerrainMode.FORCE_CARVE - || mode == IrisStructureTerrainMode.VACUUM - || mode == IrisStructureTerrainMode.FLATTEN - || mode == IrisStructureTerrainMode.ENCASE; - int horizontalPadding = mode == IrisStructureTerrainMode.VACUUM - ? NativeStructureSurfaceFitter.surfaceTerrainRadius() - : usesEnvelope ? Math.max(0, terrain.getHorizontalPadding()) : 0; - BoundingBox content = contentBounds(start); - if (!fitsReferenceRange(start.getChunkPos(), content)) { - throw new UnrepresentableContentException("Native structure content at " - + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " exceeds Minecraft's " + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk structure reference range"); - } - BoundingBox adjustedEnvelope; - boolean arithmeticClipped = false; - try { - BoundingBox envelope = horizontalPadding == 0 - ? content - : new BoundingBox( - Math.subtractExact(content.minX(), horizontalPadding), - content.minY(), - Math.subtractExact(content.minZ(), horizontalPadding), - Math.addExact(content.maxX(), horizontalPadding), - content.maxY(), - Math.addExact(content.maxZ(), horizontalPadding) - ); - adjustedEnvelope = mode == IrisStructureTerrainMode.SOURCE - ? source.adjustBoundingBox(content) : envelope; - adjustedEnvelope = encapsulating(adjustedEnvelope, content); - } catch (ArithmeticException exception) { - adjustedEnvelope = maximumReferenceBounds(start.getChunkPos(), content); - arithmeticClipped = true; - } - BoundingBox referencedEnvelope = clampReferenceRange(start.getChunkPos(), adjustedEnvelope); - if (arithmeticClipped || !sameHorizontalBounds(adjustedEnvelope, referencedEnvelope)) { - warnClippedTerrain(start, source, structureKey); - return referencedEnvelope; - } - return adjustedEnvelope; - } - - public static boolean contentFitsReferenceRange(StructureStart start) { - try { - return fitsReferenceRange(start.getChunkPos(), contentBounds(start)); - } catch (UnrepresentableContentException exception) { - return false; - } - } - - public static BoundingBox contentBounds(StructureStart start) { - return contentBounds(start.getPieces()); - } - - private static BoundingBox contentBounds(List pieces) { - BoundingBox bounds = null; - for (StructurePiece piece : pieces) { - bounds = bounds == null - ? copy(piece.getBoundingBox()) - : bounds.encapsulate(piece.getBoundingBox()); - } - if (bounds == null) { - throw new IllegalStateException("Native jigsaw generated no content pieces"); - } - return bounds; - } - - private static BoundingBox clampReferenceRange(ChunkPos startChunk, BoundingBox envelope) { - int minX = minimumReferenceBlock(startChunk.x()); - int maxX = maximumReferenceBlock(startChunk.x()); - int minZ = minimumReferenceBlock(startChunk.z()); - int maxZ = maximumReferenceBlock(startChunk.z()); - return new BoundingBox( - Math.max(envelope.minX(), minX), - envelope.minY(), - Math.max(envelope.minZ(), minZ), - Math.min(envelope.maxX(), maxX), - envelope.maxY(), - Math.min(envelope.maxZ(), maxZ) - ); - } - - private static BoundingBox maximumReferenceBounds(ChunkPos startChunk, BoundingBox content) { - return new BoundingBox( - minimumReferenceBlock(startChunk.x()), content.minY(), - minimumReferenceBlock(startChunk.z()), - maximumReferenceBlock(startChunk.x()), content.maxY(), - maximumReferenceBlock(startChunk.z()) - ); - } - - private static boolean fitsReferenceRange(ChunkPos startChunk, BoundingBox content) { - return content.minX() >= minimumReferenceBlock(startChunk.x()) - && content.maxX() <= maximumReferenceBlock(startChunk.x()) - && content.minZ() >= minimumReferenceBlock(startChunk.z()) - && content.maxZ() <= maximumReferenceBlock(startChunk.z()); - } - - private static int minimumReferenceBlock(int originChunk) { - return checkedReferenceBlock( - ((long) originChunk - MAX_REFERENCE_DISTANCE_CHUNKS) << 4, originChunk); - } - - private static int maximumReferenceBlock(int originChunk) { - return checkedReferenceBlock( - (((long) originChunk + MAX_REFERENCE_DISTANCE_CHUNKS) << 4) + 15L, originChunk); - } - - private static int checkedReferenceBlock(long block, int originChunk) { - if (block < Integer.MIN_VALUE || block > Integer.MAX_VALUE) { - throw new UnrepresentableContentException("Native structure origin chunk " + originChunk - + " exceeds Minecraft's block coordinate range"); - } - return (int) block; - } - - private static boolean sameHorizontalBounds(BoundingBox left, BoundingBox right) { - return left.minX() == right.minX() - && left.minZ() == right.minZ() - && left.maxX() == right.maxX() - && left.maxZ() == right.maxZ(); - } - - private static BoundingBox copy(BoundingBox bounds) { - return new BoundingBox( - bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ()); - } - - private static BoundingBox encapsulating(BoundingBox left, BoundingBox right) { - return new BoundingBox( - Math.min(left.minX(), right.minX()), - Math.min(left.minY(), right.minY()), - Math.min(left.minZ(), right.minZ()), - Math.max(left.maxX(), right.maxX()), - Math.max(left.maxY(), right.maxY()), - Math.max(left.maxZ(), right.maxZ()) - ); - } - - private static void warnClippedTerrain( - StructureStart start, Structure source, String structureKey) { - String key = warningKey(source, structureKey); - if (WARNED_CLIPPED_TERRAIN.add(key)) { - IrisLogging.warn("Clipping optional terrain envelope for native structure '" - + key + "' at " + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " to Minecraft's " + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk reference range; all generated content remains exactly referenced"); - } - } - - private static void warnSkippedContent( - StructureStart start, Structure source, String structureKey) { - String key = warningKey(source, structureKey); - if (WARNED_SKIPPED_CONTENT.add(key)) { - IrisLogging.warn("Skipping native structure '" + key + "' at " - + start.getChunkPos().x() + "," + start.getChunkPos().z() - + " because its generated content exceeds Minecraft's " - + MAX_REFERENCE_DISTANCE_CHUNKS - + "-chunk reference range; no structure start, ownership, references, or blocks were published"); - } - } - - private static String warningKey(Structure source, String structureKey) { - if (structureKey != null && !structureKey.isBlank()) { - return structureKey.trim().toLowerCase(Locale.ROOT); - } - return source.getClass().getName(); - } - - public static final class UnrepresentableContentException extends IllegalStateException { - private UnrepresentableContentException(String message) { - super(message); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceRepair.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceRepair.java deleted file mode 100644 index 91bb8196e..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReferenceRepair.java +++ /dev/null @@ -1,246 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.spi.IrisLogging; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; -import it.unimi.dsi.fastutil.longs.LongSet; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.ArrayList; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureReferenceRepair { - private static final int REFERENCE_DISTANCE_CHUNKS = - NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS; - private static final Set WARNED_POLICY_INVALIDATIONS = ConcurrentHashMap.newKeySet(); - - private NativeStructureReferenceRepair() { - } - - public static void createReferences(Engine engine, WorldGenLevel level, - StructureManager structureManager, ChunkAccess targetChunk) { - ChunkPos target = targetChunk.getPos(); - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - ServerLevel serverLevel = level.getLevel(); - List starts = new ArrayList<>(); - for (int originChunkX = target.x() - REFERENCE_DISTANCE_CHUNKS; - originChunkX <= target.x() + REFERENCE_DISTANCE_CHUNKS; originChunkX++) { - for (int originChunkZ = target.z() - REFERENCE_DISTANCE_CHUNKS; - originChunkZ <= target.z() + REFERENCE_DISTANCE_CHUNKS; originChunkZ++) { - try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = - openOriginRuntimeScope(engine, originChunkX, originChunkZ)) { - ChunkAccess originChunk = level.getChunk( - originChunkX, originChunkZ, ChunkStatus.STRUCTURE_STARTS); - for (Map.Entry entry : originChunk.getAllStarts().entrySet()) { - ScannedStart start = scanStart( - engine, serverLevel, structureManager, targetChunk, registry, - originChunk, entry.getKey(), entry.getValue()); - if (start != null && isTargetRelevant(engine, targetChunk, start)) { - starts.add(start); - } - } - } - } - } - for (ScannedStart start : starts) { - boolean valid; - synchronized (start.originChunk()) { - StructureStart current = start.originChunk().getStartForStructure(start.structure()); - valid = current == start.start() && current.isValid(); - } - if (!valid) { - continue; - } - structureManager.addReferenceForStructure( - SectionPos.bottomOf(targetChunk), start.structure(), - start.origin().pack(), targetChunk); - } - } - - private static GenerationHistoryRuntimeRouter.CoordinateScope openOriginRuntimeScope( - Engine engine, - int chunkX, - int chunkZ - ) { - if (!(engine instanceof IrisEngine irisEngine)) { - return null; - } - try { - return irisEngine.openGenerationHistoryCoordinateScope(chunkX << 4, chunkZ << 4); - } catch (IOException failure) { - throw new IllegalStateException("Unable to resolve native structure references for origin " - + chunkX + "," + chunkZ + " through generation history.", failure); - } - } - - private static boolean isTargetRelevant( - Engine engine, ChunkAccess targetChunk, ScannedStart scanned) { - StructureStart start = scanned.start(); - Structure structure = scanned.structure(); - ChunkPos target = targetChunk.getPos(); - if (scanned.ownership() != null) { - return requiresReference(target, scanned.structureKey(), start, scanned.ownership()); - } - if (scanned.registered()) { - return requiresNaturalReference( - engine, target, scanned.structureKey(), structure, start); - } - return NativeStructureReferenceEnvelope.contentBounds(start).intersects( - target.getMinBlockX(), target.getMinBlockZ(), - target.getMaxBlockX(), target.getMaxBlockZ()); - } - - private static ScannedStart scanStart( - Engine engine, ServerLevel level, StructureManager structureManager, - ChunkAccess targetChunk, Registry registry, - ChunkAccess originChunk, Structure structure, StructureStart start) { - Identifier identifier = registry.getKey(structure); - String structureKey = identifier == null - ? structure.getClass().getName() : identifier.toString(); - if (start == null || !start.isValid()) { - return null; - } - if (!NativeStructureReferenceEnvelope.contentFitsReferenceRange(start)) { - if (identifier != null) { - NativeStructureOwnershipStore.discard( - engine, structureKey, start.getChunkPos().x(), start.getChunkPos().z()); - } - invalidateStart(structureManager, originChunk, structure, start, targetChunk); - return null; - } - ChunkPos startOrigin = start.getChunkPos(); - NativeStructureOwnershipRecord ownership = identifier == null ? null - : NativeStructureOwnershipRecovery.resolve( - engine, level, structureKey, structure, start); - if (ownership != null) { - return new ScannedStart( - originChunk, structure, start, structureKey, ownership, true); - } - if (identifier != null && !naturalPolicyAllows(engine, structureKey, structure)) { - invalidateStart(structureManager, originChunk, structure, start, targetChunk); - if (WARNED_POLICY_INVALIDATIONS.add(structureKey)) { - IrisLogging.warn("Invalidating persisted natural structure start '" - + structureKey + "' because the current Iris dimension policy disables it"); - } - return null; - } - return new ScannedStart( - originChunk, structure, start, structureKey, null, identifier != null); - } - - private static boolean naturalPolicyAllows( - Engine engine, String structureKey, Structure structure) { - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - return naturalDecisionAllows(decision); - } - - static boolean naturalDecisionAllows(IrisNativeStructureDecision decision) { - return Objects.requireNonNull( - decision, "Native structure decision must not be null").generate(); - } - - private static void invalidateStart( - StructureManager structureManager, ChunkAccess originChunk, - Structure structure, StructureStart start, ChunkAccess targetChunk) { - synchronized (originChunk) { - StructureStart current = originChunk.getStartForStructure(structure); - if (current == start && current.isValid()) { - structureManager.setStartForStructure( - SectionPos.bottomOf(originChunk), structure, - StructureStart.INVALID_START, originChunk); - } - } - if (targetChunk != null) { - removeTargetReference(targetChunk, structure, start.getChunkPos().pack()); - } - } - - private static void removeTargetReference( - ChunkAccess targetChunk, Structure structure, long origin) { - LongSet current = targetChunk.getAllReferences().get(structure); - if (current == null || !current.contains(origin)) { - return; - } - Map updated = new HashMap<>(targetChunk.getAllReferences()); - LongSet retained = new LongOpenHashSet(current); - retained.remove(origin); - if (retained.isEmpty()) { - updated.remove(structure); - } else { - updated.put(structure, retained); - } - targetChunk.setAllReferences(updated); - } - - static boolean requiresNaturalReference(Engine engine, ChunkPos target, String structureKey, - Structure structure, StructureStart start) { - if (target == null || start == null || !start.isValid()) { - return false; - } - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureKey, - NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - if (!decision.generate()) { - return false; - } - BoundingBox referenceBounds = NativeStructureReferenceEnvelope.referenceBounds( - start, structure, - NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()), - structureKey); - return referenceBounds.intersects( - target.getMinBlockX(), target.getMinBlockZ(), - target.getMaxBlockX(), target.getMaxBlockZ()); - } - - static boolean requiresReference(ChunkPos target, String structureKey, - StructureStart start, - NativeStructureOwnershipRecord ownership) { - if (target == null || start == null || !start.isValid() - || ownership == null || !ownership.structureKey().equals(structureKey) - || !ownership.covers(target.x(), target.z())) { - return false; - } - return NativeStructureOwnershipFingerprint.matches(ownership, start); - } - - private record ScannedStart( - ChunkAccess originChunk, - Structure structure, - StructureStart start, - String structureKey, - NativeStructureOwnershipRecord ownership, - boolean registered - ) { - private ChunkPos origin() { - return ownership == null - ? start.getChunkPos() - : new ChunkPos(ownership.originChunkX(), ownership.originChunkZ()); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReflection.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReflection.java deleted file mode 100644 index 96641037f..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureReflection.java +++ /dev/null @@ -1,386 +0,0 @@ -package art.arcane.iris.nativegen; - -import com.mojang.datafixers.util.Either; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ServerLevelAccessor; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.levelgen.structure.ScatteredFeaturePiece; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces; -import net.minecraft.world.level.levelgen.structure.structures.RuinedPortalPiece; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Supplier; - -final class NativeStructureReflection { - private NativeStructureReflection() { - } - - static Field resolveScatteredHeightPositionField() { - Field resolved = null; - for (Field field : ScatteredFeaturePiece.class.getDeclaredFields()) { - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || field.getType() != int.class - || !Modifier.isProtected(modifiers) || Modifier.isFinal(modifiers)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("ScatteredFeaturePiece has multiple mutable protected int fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("ScatteredFeaturePiece height-position field is missing"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("ScatteredFeaturePiece height-position field is inaccessible"); - } - return resolved; - } - - static RuinedPortalPiece.VerticalPlacement ruinedPortalVerticalPlacement(RuinedPortalPiece piece) { - try { - return (RuinedPortalPiece.VerticalPlacement) RuinedPortalPlacementAccess.FIELD.get(piece); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native ruined portal vertical placement", error); - } - } - - private static Field resolveRuinedPortalPlacementField() { - Field resolved = null; - for (Field field : RuinedPortalPiece.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) - || field.getType() != RuinedPortalPiece.VerticalPlacement.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("RuinedPortalPiece has multiple vertical-placement fields"); - } - resolved = field; - } - if (resolved == null || !resolved.trySetAccessible()) { - throw new IllegalStateException("RuinedPortalPiece vertical-placement field is inaccessible"); - } - return resolved; - } - - private static Field resolveMonumentChildPiecesField() { - Field resolved = null; - for (Field field : OceanMonumentPieces.MonumentBuilding.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != List.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("Ocean Monument has multiple instance List fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("Ocean Monument child-pieces List field is missing"); - } - if (!Modifier.isPrivate(resolved.getModifiers()) || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("Ocean Monument child-pieces field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("Ocean Monument child-pieces field is inaccessible"); - } - return resolved; - } - - static Field resolveSinglePoolTemplateField() { - Field resolved = null; - for (Field field : SinglePoolElement.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != Either.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("SinglePoolElement has multiple instance Either fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("SinglePoolElement template Either field is missing"); - } - if (!Modifier.isProtected(resolved.getModifiers()) || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("SinglePoolElement template field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("SinglePoolElement template field is inaccessible"); - } - return resolved; - } - - static StructureTemplate resolveTemplate(SinglePoolElement element, - Supplier templates) { - Object value; - try { - value = SinglePoolTemplateAccess.FIELD.get(element); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure pool template", error); - } - if (!(value instanceof Either reference)) { - throw new IllegalStateException("Native structure pool template field is not an Either"); - } - return resolveTemplateReference(reference, templates); - } - - @SuppressWarnings("unchecked") - static List resolveTemplateBlocks( - StructureTemplate template, StructurePlaceSettings settings, - BlockPos origin) { - Object value; - try { - value = StructureTemplatePalettesAccess.FIELD.get(template); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure template palettes", error); - } - if (!(value instanceof List paletteValues)) { - throw new IllegalStateException("Native structure template palettes field is not a List"); - } - List palettes = - (List) paletteValues; - if (palettes.isEmpty()) { - return List.of(); - } - return settings.getRandomPalette(palettes, origin).blocks(); - } - - static StructurePlaceSettings resolvePlacementSettings( - SinglePoolElement element, PoolElementStructurePiece piece, - BoundingBox area) { - LiquidSettings liquidSettings; - try { - liquidSettings = (LiquidSettings) PoolPieceLiquidSettingsAccess.FIELD.get(piece); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read native structure piece liquid settings", error); - } - try { - return (StructurePlaceSettings) SinglePoolSettingsAccess.METHOD.invoke( - element, piece.getRotation(), area, liquidSettings, false); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot create native structure placement settings", error); - } - } - - @SuppressWarnings("unchecked") - static List processTemplateBlocks( - ServerLevelAccessor world, BlockPos origin, BlockPos referencePos, - StructurePlaceSettings settings, - List blocks, - StructureTemplate template) { - Method forgeMethod = StructureTemplateProcessingAccess.FORGE_METHOD; - if (forgeMethod == null) { - return StructureTemplate.processBlockInfos( - world, origin, referencePos, settings, blocks); - } - try { - return (List) forgeMethod.invoke( - null, world, origin, referencePos, settings, blocks, template); - } catch (IllegalAccessException | InvocationTargetException error) { - throw new IllegalStateException("Cannot process native structure template blocks", error); - } - } - - static StructureTemplate resolveTemplateReference(Either reference, - Supplier templates) { - return reference.map( - location -> resolveNamedTemplate(location, templates), - NativeStructureReflection::requireRuntimeTemplate); - } - - private static StructureTemplate resolveNamedTemplate(Object value, - Supplier templates) { - if (!(value instanceof Identifier identifier)) { - throw new IllegalStateException("Native structure pool template identifier is " - + (value == null ? "null" : value.getClass().getName())); - } - return Objects.requireNonNull(templates == null ? null : templates.get(), - "Native structure template manager is unavailable").getOrCreate(identifier); - } - - private static StructureTemplate requireRuntimeTemplate(Object value) { - if (!(value instanceof StructureTemplate template)) { - throw new IllegalStateException("Native runtime structure pool template is " - + (value == null ? "null" : value.getClass().getName())); - } - return template; - } - - private static Field resolveStructureTemplatePalettesField() { - Field resolved = null; - for (Field field : StructureTemplate.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != List.class - || !isPaletteList(field.getGenericType())) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("StructureTemplate has multiple palette List fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("StructureTemplate palette List field is missing"); - } - if (!Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("StructureTemplate palette field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("StructureTemplate palette field is inaccessible"); - } - return resolved; - } - - private static boolean isPaletteList(Type type) { - if (!(type instanceof ParameterizedType parameterized)) { - return false; - } - Type[] arguments = parameterized.getActualTypeArguments(); - return arguments.length == 1 && arguments[0] == StructureTemplate.Palette.class; - } - - private static Method resolveSinglePoolSettingsMethod() { - Method resolved = null; - Class[] parameterTypes = { - Rotation.class, BoundingBox.class, LiquidSettings.class, boolean.class - }; - for (Method method : SinglePoolElement.class.getDeclaredMethods()) { - if (Modifier.isStatic(method.getModifiers()) - || method.getReturnType() != StructurePlaceSettings.class - || !Arrays.equals(method.getParameterTypes(), parameterTypes)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("SinglePoolElement has multiple placement-settings methods"); - } - resolved = method; - } - if (resolved == null) { - throw new IllegalStateException("SinglePoolElement placement-settings method is missing"); - } - if (!Modifier.isProtected(resolved.getModifiers())) { - throw new IllegalStateException("SinglePoolElement placement-settings method has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("SinglePoolElement placement-settings method is inaccessible"); - } - return resolved; - } - - private static Field resolvePoolPieceLiquidSettingsField() { - Field resolved = null; - for (Field field : PoolElementStructurePiece.class.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) - || field.getType() != LiquidSettings.class) { - continue; - } - if (resolved != null) { - throw new IllegalStateException("PoolElementStructurePiece has multiple liquid-settings fields"); - } - resolved = field; - } - if (resolved == null) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is missing"); - } - if (!Modifier.isPrivate(resolved.getModifiers()) - || !Modifier.isFinal(resolved.getModifiers())) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field has an unexpected access contract"); - } - if (!resolved.trySetAccessible()) { - throw new IllegalStateException("PoolElementStructurePiece liquid-settings field is inaccessible"); - } - return resolved; - } - - private static Method resolveForgeTemplateProcessingMethod() { - Class[] parameterTypes = { - ServerLevelAccessor.class, BlockPos.class, BlockPos.class, - StructurePlaceSettings.class, List.class, StructureTemplate.class - }; - Method resolved = null; - for (Method method : StructureTemplate.class.getDeclaredMethods()) { - if (!Modifier.isPublic(method.getModifiers()) - || !Modifier.isStatic(method.getModifiers()) - || method.getReturnType() != List.class - || !Arrays.equals(method.getParameterTypes(), parameterTypes)) { - continue; - } - if (resolved != null) { - throw new IllegalStateException( - "StructureTemplate has multiple Forge block-processing methods"); - } - resolved = method; - } - return resolved; - } - - static final class MonumentChildPiecesAccess { - static final Field FIELD = resolveMonumentChildPiecesField(); - - private MonumentChildPiecesAccess() { - } - } - - private static final class RuinedPortalPlacementAccess { - private static final Field FIELD = resolveRuinedPortalPlacementField(); - - private RuinedPortalPlacementAccess() { - } - } - - static final class ScatteredHeightPositionAccess { - static final Field FIELD = resolveScatteredHeightPositionField(); - - private ScatteredHeightPositionAccess() { - } - } - - private static final class SinglePoolTemplateAccess { - private static final Field FIELD = resolveSinglePoolTemplateField(); - - private SinglePoolTemplateAccess() { - } - } - - private static final class StructureTemplatePalettesAccess { - private static final Field FIELD = resolveStructureTemplatePalettesField(); - - private StructureTemplatePalettesAccess() { - } - } - - private static final class SinglePoolSettingsAccess { - private static final Method METHOD = resolveSinglePoolSettingsMethod(); - - private SinglePoolSettingsAccess() { - } - } - - private static final class PoolPieceLiquidSettingsAccess { - private static final Field FIELD = resolvePoolPieceLiquidSettingsField(); - - private PoolPieceLiquidSettingsAccess() { - } - } - - private static final class StructureTemplateProcessingAccess { - private static final Method FORGE_METHOD = resolveForgeTemplateProcessingMethod(); - - private StructureTemplateProcessingAccess() { - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureStartInjector.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureStartInjector.java deleted file mode 100644 index a1d915f8e..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureStartInjector.java +++ /dev/null @@ -1,147 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.structure.nativegen.NativeStructureSuppression; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -public final class NativeStructureStartInjector { - private static final Set WARNED_DUPLICATE_STRUCTURES = ConcurrentHashMap.newKeySet(); - - private NativeStructureStartInjector() { - } - - public static Map inject(InjectionContext context) { - Objects.requireNonNull(context, "Native structure injection context must not be null"); - ChunkAccess chunk = context.chunk(); - Registry registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE); - SectionPos section = SectionPos.bottomOf(chunk); - Map configuredStarts = new LinkedHashMap<>(); - for (NativeStructureStartPlan plan : NativeStructurePlacementPlanner.plansAt( - context.engine(), chunk.getPos().x(), chunk.getPos().z())) { - Identifier identifier = Identifier.tryParse(plan.source().getStructure()); - if (identifier == null) { - throw new IllegalStateException("Configured native structure key is invalid: " - + plan.source().getStructure()); - } - Structure structure = registry.getValue(identifier); - if (structure == null) { - throw new IllegalStateException("Configured native structure is not registered: " + identifier); - } - Holder holder = registry.wrapAsHolder(structure); - if (configuredStarts.containsKey(structure)) { - if (WARNED_DUPLICATE_STRUCTURES.add(identifier.toString())) { - IrisLogging.warn("Ignoring duplicate native structure placements for '" - + identifier + "'; the first deterministic candidate owns each chunk start"); - } - continue; - } - StructureStart existing = context.structureManager().getStartForStructure( - section, structure, chunk); - boolean replacement = plan.placement().getNativeSuppression() - == NativeStructureSuppression.REPLACE_SOURCE; - if (!replacement && existing != null && existing.isValid()) { - NativeStructureGenerationStatus sourceStatus = NativeStructureGenerationPolicy.resolve( - context.engine(), identifier.toString(), - NativeStructureVegetationClearer.isUndergroundStep(structure.step())).status(); - replacement = sourceStatus == NativeStructureGenerationStatus.REPLACED_BY_IRIS; - } - if (!replacement && existing != null && existing.isValid()) { - continue; - } - int references = existing != null && existing.isValid() ? existing.getReferences() : 0; - NativeStructureFactory.GenerationContext generationContext = - new NativeStructureFactory.GenerationContext( - context.registryAccess(), - context.generator(), - context.biomeSource(), - context.structureState().randomState(), - context.templateManager(), - context.structureState().getLevelSeed(), - context.levelKey(), - chunk, - biome -> true, - context.generator().getSeaLevel(), - (x, z) -> Engine.hostHeight(context.engine(), x, z, true) - + context.engine().getMinHeight() - ); - StructureStart generated = NativeStructureFactory.generate( - generationContext, holder, plan, references); - if (!isUsableGeneratedStart(generated)) { - if (replacement) { - context.structureManager().setStartForStructure( - section, structure, StructureStart.INVALID_START, chunk); - } - NativeStructureOwnershipStore.discard( - context.engine(), identifier.toString(), - chunk.getPos().x(), chunk.getPos().z()); - continue; - } - BoundingBox referenceBounds = NativeStructureReferenceEnvelope.referenceBounds( - generated, structure, plan.placement().resolvedTerrain(), identifier.toString()); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - identifier.toString(), generated, plan, referenceBounds); - NativeStructureOwnershipStore.record(context.engine(), ownership); - try { - context.structureManager().setStartForStructure( - section, structure, generated, chunk); - } catch (RuntimeException | Error publicationError) { - try { - NativeStructureOwnershipStore.discard( - context.engine(), identifier.toString(), - chunk.getPos().x(), chunk.getPos().z()); - } catch (RuntimeException | Error cleanupError) { - publicationError.addSuppressed(cleanupError); - } - throw publicationError; - } - configuredStarts.put(structure, plan); - } - return Map.copyOf(configuredStarts); - } - - static boolean isUsableGeneratedStart(StructureStart start) { - return start != null && start.isValid(); - } - - public record InjectionContext( - Engine engine, - RegistryAccess registryAccess, - ChunkGeneratorStructureState structureState, - StructureManager structureManager, - ChunkAccess chunk, - StructureTemplateManager templateManager, - ResourceKey levelKey, - ChunkGenerator generator, - BiomeSource biomeSource - ) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceFitter.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceFitter.java deleted file mode 100644 index 81e0407af..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceFitter.java +++ /dev/null @@ -1,1221 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.object.IrisObjectVacuum; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.TerrainAdjustment; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.structures.IglooPieces; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; -import net.minecraft.world.level.levelgen.structure.structures.OceanRuinStructure; -import net.minecraft.world.level.levelgen.structure.structures.RuinedPortalPiece; -import net.minecraft.world.level.levelgen.structure.structures.ShipwreckStructure; -import net.minecraft.world.level.levelgen.structure.structures.StrongholdStructure; -import net.minecraft.world.level.levelgen.structure.structures.SwampHutPiece; -import net.minecraft.world.level.levelgen.structure.structures.WoodlandMansionPieces; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureSurfaceFitter { - private static final double SURFACE_TERRAIN_FALLOFF = 2.0; - private static final long SURFACE_TERRAIN_INFLUENCE_SCALE = 1_000_000L; - private static final int SURFACE_TERRAIN_RADIUS = 12; - private static final int MAX_SOURCE_SURFACE_DELTA = 6; - private static final int MAX_SURFACE_TEMPLATE_CELLS = 4_194_304; - private static final Set WARNED_SOURCE_BUDGET = - ConcurrentHashMap.newKeySet(); - private static final Set WARNED_VACUUM_BUDGET = - ConcurrentHashMap.newKeySet(); - private static final Set WARNED_FLATTEN_BUDGET = - ConcurrentHashMap.newKeySet(); - - private NativeStructureSurfaceFitter() { - } - - public static SurfaceTerrainPlan prepareSurfaceStructures( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight) { - return prepareSurfaceStructures( - world, area, targets, surfaceHeight, MAX_SURFACE_TEMPLATE_CELLS); - } - - static SurfaceTerrainPlan prepareSurfaceStructures( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight, int maximumTemplateCells) { - if (targets == null || targets.isEmpty()) { - return SurfaceTerrainPlan.empty(); - } - Objects.requireNonNull(surfaceHeight, "Surface structure terrain fitting requires an Iris height resolver"); - List anchors = collectSourceSurfaceAnchors( - world, area, targets, maximumTemplateCells); - if (!anchors.isEmpty()) { - fitSurfaceTerrain(world, area, anchors, surfaceHeight); - } - FlattenFootprint flatten = collectFlattenFootprint( - world, area, targets, surfaceHeight, maximumTemplateCells); - Map flattenedHeights = flatten.anchors().isEmpty() && flatten.projectedSupport().isEmpty() ? Map.of() - : fitFlattenTerrain(world, area, flatten, surfaceHeight); - VacuumFootprint vacuum = collectVacuumFootprint( - world, area, targets, maximumTemplateCells); - if (!vacuum.anchors().isEmpty()) { - fitVacuumTerrain(world, area, vacuum, surfaceHeight); - } - return SurfaceTerrainPlan.create( - vacuum, area, flattenedHeights); - } - - public static void repairVacuumFoundations( - WorldGenLevel world, BoundingBox area, - SurfaceTerrainPlan plan) { - if (plan == null || plan.foundationBases.isEmpty()) { - return; - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (long packedBase : plan.foundationBases) { - BlockPos base = BlockPos.of(packedBase); - if (!area.isInside(base)) { - continue; - } - BlockState baseState = world.getBlockState(position.set(base)); - if (!NativeStructureTemplateOccupancy.isSolidBase(baseState)) { - continue; - } - int supportY = findVacuumFoundationSupport( - world, area, position, base.getX(), base.getY() - 1, base.getZ()); - if (supportY == Integer.MIN_VALUE || supportY == base.getY() - 1) { - continue; - } - if (intersectsVacuumOccupancy( - plan.occupiedCells, base.getX(), base.getZ(), - supportY + 1, base.getY() - 1)) { - continue; - } - BlockState fill = resolveSurfaceMaterials( - world, position, base.getX(), base.getZ(), supportY, - area.minY()).subsurface(); - for (int y = supportY + 1; y < base.getY(); y++) { - BlockState existing = world.getBlockState( - position.set(base.getX(), y, base.getZ())); - if (existing.isAir() || !existing.getFluidState().isEmpty()) { - world.setBlock(position, fill, 2); - } - } - } - } - - static boolean shouldPrepareSurfaceTerrain(TerrainAdjustment adjustment, - GenerationStep.Decoration step) { - return step == GenerationStep.Decoration.SURFACE_STRUCTURES - && (adjustment == TerrainAdjustment.BEARD_THIN - || adjustment == TerrainAdjustment.BEARD_BOX); - } - - static int surfaceTerrainRadius() { - return SURFACE_TERRAIN_RADIUS; - } - - static int resolveSurfaceTarget(List anchors, int worldX, int worldZ, - int originalY) { - return resolveSurface(anchors, worldX, worldZ, originalY).targetY(); - } - - private static SurfaceResolution resolveSurface(List anchors, - int worldX, int worldZ, int originalY) { - int localTargetY = originalY; - SurfaceAnchor selectedLocal = null; - long totalInfluence = 0L; - long weightedTargetY = 0L; - long maximumInfluence = 0L; - for (SurfaceAnchor anchor : anchors) { - int outX = IrisObjectVacuum.outset(worldX, anchor.minX(), anchor.maxX()); - int outZ = IrisObjectVacuum.outset(worldZ, anchor.minZ(), anchor.maxZ()); - boolean containsColumn = outX == 0 && outZ == 0; - long distanceSquared = (long) outX * outX + (long) outZ * outZ; - long radiusSquared = (long) SURFACE_TERRAIN_RADIUS * SURFACE_TERRAIN_RADIUS; - if (distanceSquared > radiusSquared) { - continue; - } - double distance = Math.sqrt(distanceSquared); - double factor = Math.pow( - 1D - distance / SURFACE_TERRAIN_RADIUS, SURFACE_TERRAIN_FALLOFF); - if (factor <= 0D) { - continue; - } - int boundedTargetY = boundedSurfaceTarget(originalY, anchor.meetY()); - if (containsColumn) { - if (precedes(anchor, selectedLocal)) { - localTargetY = boundedTargetY; - selectedLocal = anchor; - } - continue; - } - long influence = Math.round(factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - if (influence <= 0L) { - continue; - } - long weightedInfluence = influence * Math.max(1, anchor.strength()); - totalInfluence += weightedInfluence; - weightedTargetY += weightedInfluence * boundedTargetY; - maximumInfluence = Math.max(maximumInfluence, influence); - } - if (selectedLocal != null) { - return new SurfaceResolution( - localTargetY, - selectedLocal.strength() > 1 - && localTargetY == selectedLocal.meetY()); - } - if (totalInfluence == 0L) { - return new SurfaceResolution(originalY, false); - } - double blendedTargetY = weightedTargetY / (double) totalInfluence; - double factor = maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE; - return new SurfaceResolution( - blendSurfaceTarget(originalY, blendedTargetY, factor), false); - } - - private static int boundedSurfaceTarget(int originalY, int meetY) { - int delta = Math.max( - -MAX_SOURCE_SURFACE_DELTA, - Math.min(MAX_SOURCE_SURFACE_DELTA, meetY - originalY)); - return originalY + delta; - } - - private static int blendSurfaceTarget(int originalY, double targetY, double factor) { - double delta = (targetY - originalY) * factor; - int magnitude = (int) Math.round(Math.abs(delta)); - return originalY + (delta < 0D ? -magnitude : magnitude); - } - - private static boolean precedes(SurfaceAnchor candidate, SurfaceAnchor selected) { - if (selected == null) { - return true; - } - if (candidate.strength() != selected.strength()) { - return candidate.strength() > selected.strength(); - } - if (candidate.meetY() != selected.meetY()) { - return candidate.meetY() < selected.meetY(); - } - if (candidate.minX() != selected.minX()) { - return candidate.minX() < selected.minX(); - } - if (candidate.minZ() != selected.minZ()) { - return candidate.minZ() < selected.minZ(); - } - if (candidate.maxX() != selected.maxX()) { - return candidate.maxX() < selected.maxX(); - } - if (candidate.maxZ() != selected.maxZ()) { - return candidate.maxZ() < selected.maxZ(); - } - return false; - } - - private static List collectSourceSurfaceAnchors( - WorldGenLevel world, BoundingBox area, - List targets, - int maximumTemplateCells) { - BoundingBox influenceArea = new BoundingBox( - area.minX() - SURFACE_TERRAIN_RADIUS, area.minY(), - area.minZ() - SURFACE_TERRAIN_RADIUS, - area.maxX() + SURFACE_TERRAIN_RADIUS, area.maxY(), - area.maxZ() + SURFACE_TERRAIN_RADIUS); - List anchors = new ArrayList<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (!requiresSourceSurfaceTerrain(target)) { - continue; - } - StructureStart start = target.start(); - if (!seenStarts.add(start)) { - continue; - } - BoundingBox firstBounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos firstCenter = firstBounds.getCenter(); - BlockPos referencePosition = new BlockPos( - firstCenter.getX(), firstBounds.minY(), firstCenter.getZ()); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - boolean budgetExceeded = false; - for (StructurePiece piece : start.getPieces()) { - if (piece instanceof PoolElementStructurePiece poolPiece) { - StructureTemplatePool.Projection projection = - poolPiece.getElement().getProjection(); - if (!budgetExceeded - && (projection == StructureTemplatePool.Projection.RIGID - || projection == StructureTemplatePool.Projection.TERRAIN_MATCHING) - && intersectsHorizontally(poolPiece.getBoundingBox(), influenceArea)) { - try { - List pieceAnchors = new ArrayList<>(); - addProcessedSurfaceAnchors( - world, influenceArea, poolPiece, referencePosition, - projection, budget, pieceAnchors); - anchors.addAll(pieceAnchors); - } catch (TemplateBudgetExceeded ignored) { - budgetExceeded = true; - warnSourceBudget(target); - } - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - anchors.add(new SurfaceAnchor( - junction.getSourceX(), junction.getSourceX(), - junction.getSourceZ(), junction.getSourceZ(), - junction.getSourceGroundY() - 1, 1)); - } - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - anchors.add(surfaceAnchor(bounds, bounds.minY(), 2)); - } - } - return List.copyOf(anchors); - } - - private static void addProcessedSurfaceAnchors( - WorldGenLevel world, BoundingBox influenceArea, - PoolElementStructurePiece piece, BlockPos referencePosition, - StructureTemplatePool.Projection projection, - TemplateCellBudget budget, - List anchors) { - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, piece, referencePosition, influenceArea, - () -> world.getLevel().getStructureManager(), - influenceArea::isInside, budget::consume); - if (!occupancy.resolved()) { - return; - } - int maximumFoundationY = piece.getBoundingBox().minY() - + piece.getGroundLevelDelta(); - Map lowest = - NativeStructureTemplateOccupancy.lowestProcessedSolidCells(occupancy.cells()); - for (NativeStructureTemplateOccupancy.LowestCell cell : lowest.values()) { - budget.consume(1); - if (projection == StructureTemplatePool.Projection.RIGID - && cell.y() > maximumFoundationY) { - continue; - } - int groundY = projection == StructureTemplatePool.Projection.RIGID - ? maximumFoundationY : cell.y(); - BoundingBox column = new BoundingBox( - cell.x(), groundY, cell.z(), cell.x(), groundY, cell.z()); - anchors.add(surfaceAnchor(column, groundY, 2)); - } - } - - private static void warnSourceBudget( - NativeStructureTerrainIntegrator.TerrainTarget target) { - String structureId = target.structureId() == null - ? target.start().getStructure().getClass().getName() - : target.structureId(); - if (WARNED_SOURCE_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure SOURCE fitting for '" - + structureId + "' exceeded its bounded template budget; " - + "skipping remaining processed footprints for this start"); - } - } - - private static VacuumFootprint collectVacuumFootprint( - WorldGenLevel world, BoundingBox area, - List targets, - int maximumTemplateCells) { - BoundingBox influenceArea = new BoundingBox( - area.minX() - SURFACE_TERRAIN_RADIUS, area.minY(), - area.minZ() - SURFACE_TERRAIN_RADIUS, - area.maxX() + SURFACE_TERRAIN_RADIUS, area.maxY(), - area.maxZ() + SURFACE_TERRAIN_RADIUS); - Map anchors = new HashMap<>(); - Map columnCaps = new HashMap<>(); - Set foundationBases = new HashSet<>(); - Set occupiedCells = new HashSet<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (target == null || target.terrain() == null - || target.terrain().resolvedMode() != IrisStructureTerrainMode.VACUUM - || target.start() == null || !target.start().isValid() - || !seenStarts.add(target.start())) { - continue; - } - StructureStart start = target.start(); - BoundingBox firstBounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos firstCenter = firstBounds.getCenter(); - BlockPos referencePosition = new BlockPos( - firstCenter.getX(), firstBounds.minY(), firstCenter.getZ()); - Map targetAnchors = new HashMap<>(); - Map targetCaps = new HashMap<>(); - Set targetFoundationBases = new HashSet<>(); - Set targetOccupiedCells = new HashSet<>(); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - try { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!intersectsHorizontally(bounds, influenceArea) - || !(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() - != StructureTemplatePool.Projection.RIGID) { - continue; - } - addVacuumJunctionAnchors( - anchors, influenceArea, poolPiece); - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, poolPiece, referencePosition, influenceArea, - () -> world.getLevel().getStructureManager(), - influenceArea::isInside, budget::consume); - if (!occupancy.resolved()) { - continue; - } - targetOccupiedCells.addAll(occupancy.cells().keySet()); - Map lowest = - NativeStructureTemplateOccupancy.lowestCells( - occupancy.cells()); - int groundY = bounds.minY() - + poolPiece.getGroundLevelDelta() - 1; - for (NativeStructureTemplateOccupancy.LowestCell cell : lowest.values()) { - budget.consume(1); - long column = NativeStructureTemplateOccupancy.columnKey( - cell.x(), cell.z()); - targetCaps.merge( - column, Math.min(cell.y(), groundY), Math::min); - if (cell.occupancy().blocker() - || cell.y() > groundY + 1 - || !NativeStructureTemplateOccupancy.isSolidBase( - cell.occupancy().state())) { - continue; - } - addVacuumAnchor(targetAnchors, cell.x(), cell.z(), - new VacuumAnchor(cell.y() - 1, 2)); - targetFoundationBases.add(BlockPos.asLong( - cell.x(), cell.y(), cell.z())); - } - } - } catch (TemplateBudgetExceeded ignored) { - warnVacuumBudget(target); - continue; - } - targetAnchors.forEach((column, anchor) -> anchors.merge( - column, anchor, NativeStructureSurfaceFitter::preferredVacuumAnchor)); - targetCaps.forEach((column, cap) -> columnCaps.merge( - column, cap, Math::min)); - foundationBases.addAll(targetFoundationBases); - occupiedCells.addAll(targetOccupiedCells); - } - return new VacuumFootprint( - Map.copyOf(anchors), Map.copyOf(columnCaps), - Set.copyOf(foundationBases), Set.copyOf(occupiedCells)); - } - - private static void addVacuumJunctionAnchors( - Map anchors, BoundingBox influenceArea, - PoolElementStructurePiece poolPiece) { - for (JigsawJunction junction : poolPiece.getJunctions()) { - if (junction.getSourceX() < influenceArea.minX() - || junction.getSourceX() > influenceArea.maxX() - || junction.getSourceZ() < influenceArea.minZ() - || junction.getSourceZ() > influenceArea.maxZ()) { - continue; - } - addVacuumAnchor(anchors, - junction.getSourceX(), junction.getSourceZ(), - new VacuumAnchor(junction.getSourceGroundY() - 1, 1)); - } - } - - private static void warnVacuumBudget( - NativeStructureTerrainIntegrator.TerrainTarget target) { - String structureId = target.structureId() == null - ? target.start().getStructure().getClass().getName() - : target.structureId(); - if (WARNED_VACUUM_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure VACUUM fitting for '" - + structureId + "' exceeded its bounded template budget; " - + "using jigsaw junction smoothing for oversized starts"); - } - } - - private static boolean intersectsHorizontally( - BoundingBox first, BoundingBox second) { - return first.maxX() >= second.minX() && first.minX() <= second.maxX() - && first.maxZ() >= second.minZ() && first.minZ() <= second.maxZ(); - } - - private static void addVacuumAnchor( - Map anchors, int x, int z, - VacuumAnchor candidate) { - anchors.merge(NativeStructureTemplateOccupancy.columnKey(x, z), candidate, - NativeStructureSurfaceFitter::preferredVacuumAnchor); - } - - private static VacuumAnchor preferredVacuumAnchor( - VacuumAnchor first, VacuumAnchor second) { - if (first.strength() != second.strength()) { - return first.strength() > second.strength() ? first : second; - } - return first.surfaceY() <= second.surfaceY() ? first : second; - } - - static SurfaceAnchor surfaceAnchor(BoundingBox bounds, int groundY, int strength) { - return new SurfaceAnchor(bounds.minX(), bounds.maxX(), bounds.minZ(), bounds.maxZ(), - groundY - 1, strength); - } - - static boolean requiresSurfaceTerrain(StructureStart start) { - return requiresSurfaceTerrain(new NativeStructureTerrainIntegrator.TerrainTarget( - null, start, new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE))); - } - - static boolean requiresSurfaceTerrain(NativeStructureTerrainIntegrator.TerrainTarget target) { - if (target == null || target.terrain() == null) { - return false; - } - StructureStart start = target.start(); - if (start == null || !start.isValid()) { - return false; - } - IrisStructureTerrainMode mode = target.terrain().resolvedMode(); - return mode == IrisStructureTerrainMode.VACUUM - || requiresFlattenTerrain(target) - || requiresSourceSurfaceTerrain(target); - } - - private static boolean requiresSourceSurfaceTerrain( - NativeStructureTerrainIntegrator.TerrainTarget target) { - return target != null && target.terrain() != null - && target.start() != null && target.start().isValid() - && target.terrain().resolvedMode() == IrisStructureTerrainMode.SOURCE - && shouldPrepareSurfaceTerrain( - target.start().getStructure().terrainAdaptation(), - target.start().getStructure().step()); - } - - private static void fitSurfaceTerrain(WorldGenLevel world, BoundingBox area, - List anchors, - IntBinaryOperator surfaceHeight) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - int[] originalHeights = new int[width * depth]; - int[] targetHeights = new int[width * depth]; - boolean[] rigidBaseSupport = new boolean[width * depth]; - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - int originalY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(x, z))); - SurfaceResolution resolution = resolveSurface(anchors, x, z, originalY); - originalHeights[column] = originalY; - targetHeights[column] = Math.max(area.minY(), Math.min( - area.maxY(), resolution.targetY())); - rigidBaseSupport[column] = resolution.rigidBaseSupport(); - } - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - applySurfaceColumn(world, position, x, z, - originalHeights[column], targetHeights[column], area.minY(), area.maxY(), - rigidBaseSupport[column]); - } - } - } - - static boolean requiresFlattenTerrain(NativeStructureTerrainIntegrator.TerrainTarget target) { - if (target == null || target.terrain() == null - || target.terrain().resolvedMode() != IrisStructureTerrainMode.FLATTEN - || target.terrain().resolvedFlattenRange() == 0 - || target.start() == null || !target.start().isValid()) { - return false; - } - StructureStart start = target.start(); - return start.getStructure().step() == GenerationStep.Decoration.SURFACE_STRUCTURES - && start.getStructure().terrainAdaptation() != TerrainAdjustment.BURY - && start.getStructure().terrainAdaptation() != TerrainAdjustment.ENCAPSULATE - && !(start.getStructure() instanceof StrongholdStructure) - && !(start.getStructure() instanceof OceanMonumentStructure) - && !(start.getStructure() instanceof OceanRuinStructure) - && (!(start.getStructure() instanceof ShipwreckStructure) - || "minecraft:shipwreck_beached".equals(target.structureId())); - } - - private static FlattenFootprint collectFlattenFootprint( - WorldGenLevel world, BoundingBox area, - List targets, - IntBinaryOperator surfaceHeight, int maximumTemplateCells) { - List anchors = new ArrayList<>(); - Map projectedSupport = new HashMap<>(); - Set seen = Collections.newSetFromMap(new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (!requiresFlattenTerrain(target) || !seen.add(target.start())) { - continue; - } - int radius = Math.max(0, Math.min(128, target.terrain().getHorizontalPadding())); - int range = target.terrain().resolvedFlattenRange(); - BoundingBox influenceArea = new BoundingBox( - area.minX() - radius, area.minY(), area.minZ() - radius, - area.maxX() + radius, area.maxY(), area.maxZ() + radius); - StructureStart start = target.start(); - if (!hasExposedFlattenPiece(start, surfaceHeight, range)) { - continue; - } - BoundingBox first = start.getPieces().getFirst().getBoundingBox(); - BlockPos reference = new BlockPos(first.getCenter().getX(), first.minY(), first.getCenter().getZ()); - TemplateCellBudget budget = new TemplateCellBudget(maximumTemplateCells); - List sources = new ArrayList<>(); - Set projectedColumns = new HashSet<>(); - try { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!intersectsHorizontally(bounds, influenceArea) || !flattenPieceAllowed(start, piece)) { - continue; - } - if (piece instanceof PoolElementStructurePiece poolPiece) { - if (poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID) { - addProcessedSurfaceAnchors(world, influenceArea, poolPiece, reference, - StructureTemplatePool.Projection.RIGID, budget, sources); - } else { - addProjectedFlattenSupport(world, area, poolPiece, reference, budget, projectedColumns); - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - sources.add(new SurfaceAnchor(junction.getSourceX(), junction.getSourceX(), - junction.getSourceZ(), junction.getSourceZ(), junction.getSourceGroundY() - 1, 1)); - } - } else { - sources.add(new SurfaceAnchor(bounds.minX(), bounds.maxX(), bounds.minZ(), bounds.maxZ(), - flattenGroundY(start, piece, surfaceHeight), 2)); - } - } - } catch (TemplateBudgetExceeded ignored) { - String structureId = target.structureId() == null - ? start.getStructure().getClass().getName() : target.structureId(); - if (WARNED_FLATTEN_BUDGET.add(structureId)) { - IrisLogging.warn("Native structure FLATTEN fitting for '" + structureId - + "' exceeded its bounded template budget; skipping this start"); - } - continue; - } - for (SurfaceAnchor source : sources) { - anchors.add(new FlattenAnchor(source, radius, range)); - } - for (long column : projectedColumns) { - projectedSupport.merge(column, range, Math::max); - } - } - return new FlattenFootprint(List.copyOf(anchors), Map.copyOf(projectedSupport)); - } - - private static void addProjectedFlattenSupport( - WorldGenLevel world, BoundingBox area, PoolElementStructurePiece piece, - BlockPos reference, TemplateCellBudget budget, Set columns) { - NativeStructureTemplateOccupancy.OccupancyResult occupancy = NativeStructureTemplateOccupancy.resolve( - world, piece, reference, area, () -> world.getLevel().getStructureManager(), - area::isInside, budget::consume); - for (Map.Entry entry : occupancy.cells().entrySet()) { - NativeStructureTemplateOccupancy.OccupancyCell cell = entry.getValue(); - if (cell.blocker() || !NativeStructureTemplateOccupancy.isSolidBase(cell.state())) { - continue; - } - BlockPos position = BlockPos.of(entry.getKey()); - columns.add(NativeStructureTemplateOccupancy.columnKey(position.getX(), position.getZ())); - } - } - - private static boolean flattenPieceAllowed(StructureStart start, StructurePiece piece) { - if (piece instanceof RuinedPortalPiece portal) { - RuinedPortalPiece.VerticalPlacement placement = NativeStructureReflection.ruinedPortalVerticalPlacement(portal); - return placement == RuinedPortalPiece.VerticalPlacement.ON_LAND_SURFACE - || placement == RuinedPortalPiece.VerticalPlacement.IN_NETHER; - } - if (piece instanceof IglooPieces.IglooPiece) { - int highest = Integer.MIN_VALUE; - for (StructurePiece candidate : start.getPieces()) { - if (candidate instanceof IglooPieces.IglooPiece) { - highest = Math.max(highest, candidate.getBoundingBox().minY()); - } - } - return piece.getBoundingBox().minY() == highest; - } - return true; - } - - static boolean hasExposedFlattenPiece( - StructureStart start, IntBinaryOperator surfaceHeight, int range) { - for (StructurePiece piece : start.getPieces()) { - if (!flattenPieceAllowed(start, piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int groundY = flattenGroundY(start, piece, surfaceHeight); - int roofY = piece instanceof IglooPieces.IglooPiece || piece instanceof SwampHutPiece - ? groundY + bounds.getYSpan() : bounds.maxY(); - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int surfaceY = surfaceHeight.applyAsInt(x, z); - if (surfaceY <= roofY && (long) groundY - surfaceY <= range) { - return true; - } - } - } - } - return false; - } - - private static int flattenGroundY( - StructureStart start, StructurePiece piece, IntBinaryOperator surfaceHeight) { - BoundingBox bounds = piece.getBoundingBox(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - return bounds.minY() + poolPiece.getGroundLevelDelta() - 1; - } - if (piece instanceof IglooPieces.IglooPiece igloo) { - BlockPos entrance = igloo.templatePosition().offset( - StructureTemplate.calculateRelativePosition(igloo.placeSettings(), new BlockPos(3, 0, 0))); - return surfaceHeight.applyAsInt(entrance.getX(), entrance.getZ()); - } - if (piece instanceof SwampHutPiece || start.getStructure() instanceof ShipwreckStructure) { - long sum = 0; - int minimum = Integer.MAX_VALUE; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int surfaceY = surfaceHeight.applyAsInt(x, z); - sum += surfaceY; - minimum = Math.min(minimum, surfaceY); - } - } - return piece instanceof SwampHutPiece - ? (int) Math.floorDiv(sum, (long) bounds.getXSpan() * bounds.getZSpan()) : minimum; - } - if (piece instanceof WoodlandMansionPieces.WoodlandMansionPiece) { - return start.getBoundingBox().minY() - 1; - } - return bounds.minY() - 1; - } - - static FlattenResolution resolveFlattenSurface( - List anchors, int x, int z, int originalY) { - FlattenAnchor selected = null; - long weightedY = 0; - long totalWeight = 0; - long maximumInfluence = 0; - int range = 0; - for (FlattenAnchor anchor : anchors) { - SurfaceAnchor source = anchor.source(); - int outX = IrisObjectVacuum.outset(x, source.minX(), source.maxX()); - int outZ = IrisObjectVacuum.outset(z, source.minZ(), source.maxZ()); - boolean containsColumn = outX == 0 && outZ == 0; - if (containsColumn && source.strength() > 1) { - if (selected == null || precedes(source, selected.source())) { - selected = anchor; - } - continue; - } - double distance = Math.sqrt((double) outX * outX + (double) outZ * outZ); - if (!containsColumn && (anchor.radius() == 0 || distance >= anchor.radius())) { - continue; - } - double progress = containsColumn ? 0D : distance / anchor.radius(); - double factor = 1D - progress * progress * (3D - 2D * progress); - long influence = Math.round(factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - long weight = influence * source.strength(); - int targetY = originalY + Math.max(-anchor.range(), Math.min(anchor.range(), source.meetY() - originalY)); - weightedY += weight * targetY; - totalWeight += weight; - maximumInfluence = Math.max(maximumInfluence, influence); - range = Math.max(range, anchor.range()); - } - if (selected != null) { - int targetY = originalY + Math.max(-selected.range(), - Math.min(selected.range(), selected.source().meetY() - originalY)); - return new FlattenResolution(targetY, selected.range()); - } - if (totalWeight == 0) { - return new FlattenResolution(originalY, 0); - } - int targetY = blendSurfaceTarget(originalY, weightedY / (double) totalWeight, - maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE); - return new FlattenResolution(targetY, range); - } - - private static Map fitFlattenTerrain( - WorldGenLevel world, BoundingBox area, FlattenFootprint footprint, - IntBinaryOperator surfaceHeight) { - Map heights = new HashMap<>(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int x = area.minX(); x <= area.maxX(); x++) { - for (int z = area.minZ(); z <= area.maxZ(); z++) { - int originalY = Math.max(area.minY(), Math.min(area.maxY(), surfaceHeight.applyAsInt(x, z))); - long column = NativeStructureTemplateOccupancy.columnKey(x, z); - FlattenResolution resolution = resolveFlattenSurface(footprint.anchors(), x, z, originalY); - int projectedRange = footprint.projectedSupport().getOrDefault(column, 0); - int range = Math.max(resolution.range(), projectedRange); - if (range == 0 || originalY < area.maxY() - && !world.getBlockState(position.set(x, originalY + 1, z)).getFluidState().isEmpty()) { - continue; - } - int targetY = Math.max(area.minY(), Math.min(area.maxY(), resolution.targetY())); - int minimumY = Math.max(area.minY(), targetY - range); - applySurfaceColumn(world, position, x, z, originalY, targetY, - minimumY, Math.min(area.maxY(), originalY + range)); - fillFlattenFoundation(world, position, x, z, targetY, minimumY); - heights.put(column, targetY + 1); - } - } - return Map.copyOf(heights); - } - - private static void fillFlattenFoundation( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int targetY, int minimumY) { - SurfaceMaterials materials = resolveSurfaceMaterials(world, position, x, z, targetY, minimumY); - if (!isTerrainBlock(world.getBlockState(position.set(x, targetY, z)))) { - world.setBlock(position, materials.surface(), 2); - } - for (int y = targetY - 1; y >= minimumY; y--) { - BlockState existing = world.getBlockState(position.set(x, y, z)); - if (!existing.getFluidState().isEmpty() - || NativeStructureVegetationClearer.isTreeBlock(existing)) { - break; - } - if (!isTerrainBlock(existing)) { - world.setBlock(position, materials.subsurface(), 2); - } - } - } - - private static void fitVacuumTerrain( - WorldGenLevel world, BoundingBox area, VacuumFootprint footprint, - IntBinaryOperator surfaceHeight) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - int[] originalHeights = new int[width * depth]; - int[] targetHeights = new int[width * depth]; - boolean[] rigidBaseSupport = new boolean[width * depth]; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - int nominalY = Math.max(area.minY(), Math.min( - area.maxY(), surfaceHeight.applyAsInt(x, z))); - long columnKey = NativeStructureTemplateOccupancy.columnKey(x, z); - VacuumAnchor local = footprint.anchors().get(columnKey); - Integer cap = footprint.columnCaps().get(columnKey); - boolean suppressed = local != null && local.strength() > 1 - && cap != null && cap < local.surfaceY(); - int scanStartY = local != null && local.strength() > 1 && !suppressed - ? Math.max(area.minY(), Math.min(area.maxY(), local.surfaceY())) - : nominalY; - int originalY = resolveVacuumSurfaceHeight( - world, position, x, z, scanStartY, area.minY()); - SurfaceResolution resolution = resolveVacuumSurface( - footprint, x, z, originalY); - originalHeights[column] = originalY; - targetHeights[column] = Math.max(originalY, Math.min( - area.maxY(), resolution.targetY())); - rigidBaseSupport[column] = resolution.rigidBaseSupport() - && targetHeights[column] > originalY; - } - } - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - applySurfaceColumn(world, position, x, z, - originalHeights[column], targetHeights[column], - area.minY(), area.maxY(), rigidBaseSupport[column]); - } - } - } - - private static int resolveVacuumSurfaceHeight( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int nominalY, int worldMinY) { - for (int y = nominalY; y >= worldMinY; y--) { - if (isTerrainBlock(world.getBlockState(position.set(x, y, z)))) { - return y; - } - } - return nominalY; - } - - private static int findVacuumFoundationSupport( - WorldGenLevel world, BoundingBox area, - BlockPos.MutableBlockPos position, int x, int startY, int z) { - if (startY < area.minY()) { - return Integer.MIN_VALUE; - } - int maximumY = Math.min(area.maxY(), startY); - for (int y = maximumY; y >= area.minY(); y--) { - BlockState state = world.getBlockState(position.set(x, y, z)); - if (isTerrainBlock(state)) { - return y; - } - if (!state.isAir() && state.getFluidState().isEmpty()) { - return Integer.MIN_VALUE; - } - } - return Integer.MIN_VALUE; - } - - private static boolean intersectsVacuumOccupancy( - Set occupiedCells, int x, int z, int minimumY, int maximumY) { - for (int y = minimumY; y <= maximumY; y++) { - if (occupiedCells.contains(BlockPos.asLong(x, y, z))) { - return true; - } - } - return false; - } - - private static SurfaceResolution resolveVacuumSurface( - VacuumFootprint footprint, int worldX, int worldZ, int originalY) { - long column = NativeStructureTemplateOccupancy.columnKey(worldX, worldZ); - Integer cap = footprint.columnCaps().get(column); - VacuumAnchor local = footprint.anchors().get(column); - if (local != null && local.strength() > 1) { - int localTargetY = cap == null - ? local.surfaceY() : Math.min(local.surfaceY(), cap); - if (localTargetY <= originalY) { - return new SurfaceResolution(originalY, false); - } - return new SurfaceResolution( - localTargetY, localTargetY == local.surfaceY()); - } - long totalInfluence = 0L; - long weightedSurfaceY = 0L; - long maximumInfluence = 0L; - int radiusSquared = SURFACE_TERRAIN_RADIUS * SURFACE_TERRAIN_RADIUS; - for (int offsetX = -SURFACE_TERRAIN_RADIUS; - offsetX <= SURFACE_TERRAIN_RADIUS; offsetX++) { - for (int offsetZ = -SURFACE_TERRAIN_RADIUS; - offsetZ <= SURFACE_TERRAIN_RADIUS; offsetZ++) { - int horizontalDistanceSquared = offsetX * offsetX + offsetZ * offsetZ; - if (horizontalDistanceSquared > radiusSquared) { - continue; - } - VacuumAnchor anchor = footprint.anchors().get( - NativeStructureTemplateOccupancy.columnKey( - worldX + offsetX, worldZ + offsetZ)); - if (anchor == null || anchor.surfaceY() <= originalY) { - continue; - } - int verticalDistance = Math.abs(anchor.surfaceY() + 1 - originalY); - long distanceSquared = horizontalDistanceSquared - + (long) verticalDistance * verticalDistance; - double factor = 0D; - if (distanceSquared <= radiusSquared) { - factor = surfaceFactor(Math.sqrt(distanceSquared)); - } - if (anchor.strength() > 1 - && verticalDistance > SURFACE_TERRAIN_RADIUS) { - double rescueProgress = Math.min(1D, - (verticalDistance - SURFACE_TERRAIN_RADIUS) - / (double) SURFACE_TERRAIN_RADIUS); - double rescueWeight = rescueProgress * rescueProgress - * (3D - 2D * rescueProgress); - factor = Math.max(factor, - surfaceFactor(Math.sqrt(horizontalDistanceSquared)) - * rescueWeight); - } - long influence = Math.round( - factor * SURFACE_TERRAIN_INFLUENCE_SCALE); - if (influence <= 0L) { - continue; - } - long weightedInfluence = influence * Math.max(1, anchor.strength()); - totalInfluence += weightedInfluence; - weightedSurfaceY += weightedInfluence * anchor.surfaceY(); - maximumInfluence = Math.max(maximumInfluence, influence); - } - } - if (totalInfluence == 0L) { - return new SurfaceResolution(originalY, false); - } - double blendedSurfaceY = weightedSurfaceY / (double) totalInfluence; - double factor = maximumInfluence / (double) SURFACE_TERRAIN_INFLUENCE_SCALE; - int targetY = (int) Math.round( - originalY + ((blendedSurfaceY - originalY) * factor)); - if (cap != null) { - targetY = Math.min(targetY, cap); - } - return new SurfaceResolution(Math.max(originalY, targetY), false); - } - - private static double surfaceFactor(double distance) { - return Math.pow(1D - distance / SURFACE_TERRAIN_RADIUS, - SURFACE_TERRAIN_FALLOFF); - } - - static void applySurfaceColumn(WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int targetY, - int worldMinY, int worldMaxY) { - applySurfaceColumn(world, position, x, z, originalY, targetY, - worldMinY, worldMaxY, false); - } - - static void applySurfaceColumn(WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int targetY, - int worldMinY, int worldMaxY, - boolean requireRigidBaseSupport) { - if (targetY == originalY) { - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY); - } - return; - } - SurfaceMaterials materials = resolveSurfaceMaterials(world, position, x, z, originalY, worldMinY); - if (targetY < originalY) { - BlockState clearedState = clearSurfaceDecorationAndResolveFill( - world, position, x, z, originalY, worldMaxY); - for (int y = originalY; y > targetY; y--) { - world.setBlock(position.set(x, y, z), clearedState, 2); - } - world.setBlock(position.set(x, targetY, z), materials.surface(), 2); - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - return; - } - for (int y = originalY + 1; y < targetY; y++) { - position.set(x, y, z); - if (!NativeStructureVegetationClearer.isTreeBlock(world.getBlockState(position))) { - world.setBlock(position, materials.subsurface(), 2); - } - } - position.set(x, targetY, z); - if (!NativeStructureVegetationClearer.isTreeBlock(world.getBlockState(position))) { - world.setBlock(position, materials.surface(), 2); - } - if (requireRigidBaseSupport) { - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - } - - private static void ensureRigidBaseTerrain(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int targetY, int worldMinY) { - int supportY = targetY - 1; - boolean targetIsTerrain = isTerrainBlock(world.getBlockState(position.set(x, targetY, z))); - boolean supportIsTerrain = supportY < worldMinY - || isTerrainBlock(world.getBlockState(position.set(x, supportY, z))); - if (targetIsTerrain && supportIsTerrain) { - return; - } - SurfaceMaterials materials = resolveSurfaceMaterials( - world, position, x, z, targetY, worldMinY); - ensureRigidBaseTerrain(world, position, x, z, targetY, worldMinY, materials); - } - - private static void ensureRigidBaseTerrain(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int targetY, int worldMinY, - SurfaceMaterials materials) { - int supportY = targetY - 1; - position.set(x, targetY, z); - if (!isTerrainBlock(world.getBlockState(position))) { - world.setBlock(position, materials.surface(), 2); - } - if (supportY < worldMinY) { - return; - } - position.set(x, supportY, z); - if (!isTerrainBlock(world.getBlockState(position))) { - world.setBlock(position, materials.subsurface(), 2); - } - } - - private static BlockState clearSurfaceDecorationAndResolveFill( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int originalY, int worldMaxY) { - BlockState air = Blocks.AIR.defaultBlockState(); - for (int y = originalY + 1; y <= worldMaxY; y++) { - BlockState state = world.getBlockState(position.set(x, y, z)); - if (state.isAir()) { - return air; - } - if (!state.getFluidState().isEmpty()) { - BlockState fluid = state.getFluidState().createLegacyBlock(); - if (state != fluid) { - world.setBlock(position, fluid, 2); - } - return fluid; - } - if (state.isSolid() && !state.is(Blocks.CACTUS) - || NativeStructureVegetationClearer.isTreeBlock(state)) { - return air; - } - world.setBlock(position, air, 2); - } - return air; - } - - private static SurfaceMaterials resolveSurfaceMaterials(WorldGenLevel world, - BlockPos.MutableBlockPos position, - int x, int z, int originalY, - int worldMinY) { - BlockState surface = world.getBlockState(position.set(x, originalY, z)); - BlockState subsurface = null; - for (int y = originalY - 1; y >= worldMinY; y--) { - BlockState candidate = world.getBlockState(position.set(x, y, z)); - if (isTerrainBlock(candidate)) { - subsurface = candidate; - break; - } - } - if (subsurface == null) { - subsurface = isTerrainBlock(surface) ? surface : Blocks.STONE.defaultBlockState(); - } - if (!isTerrainBlock(surface)) { - surface = subsurface; - } - return new SurfaceMaterials(surface, subsurface); - } - - private static boolean isTerrainBlock(BlockState state) { - return state.isSolid() && !NativeStructureVegetationClearer.isTreeBlock(state); - } - - record SurfaceAnchor(int minX, int maxX, int minZ, int maxZ, int meetY, int strength) { - } - - record FlattenAnchor(SurfaceAnchor source, int radius, int range) { - } - - record FlattenResolution(int targetY, int range) { - } - - private record FlattenFootprint(List anchors, Map projectedSupport) { - } - - private record SurfaceMaterials(BlockState surface, BlockState subsurface) { - } - - private record SurfaceResolution(int targetY, boolean rigidBaseSupport) { - } - - private record VacuumFootprint( - Map anchors, Map columnCaps, - Set foundationBases, Set occupiedCells) { - } - - private record VacuumAnchor(int surfaceY, int strength) { - } - - public record SurfaceTerrainPlan( - List foundationBases, Set occupiedCells, Map flattenedHeights) { - private static final SurfaceTerrainPlan EMPTY = - new SurfaceTerrainPlan(List.of(), Set.of(), Map.of()); - - public SurfaceTerrainPlan { - foundationBases = List.copyOf(foundationBases); - occupiedCells = Set.copyOf(occupiedCells); - flattenedHeights = Map.copyOf(flattenedHeights); - } - - private static SurfaceTerrainPlan empty() { - return EMPTY; - } - - private static SurfaceTerrainPlan create( - VacuumFootprint vacuum, BoundingBox area, Map flattenedHeights) { - Set packedBases = vacuum.foundationBases(); - if (packedBases.isEmpty() && flattenedHeights.isEmpty()) { - return EMPTY; - } - List bases = new ArrayList<>(packedBases.size()); - Set columns = new HashSet<>(); - for (long packedBase : packedBases) { - BlockPos base = BlockPos.of(packedBase); - if (!area.isInside(base)) { - continue; - } - bases.add(packedBase); - columns.add(NativeStructureTemplateOccupancy.columnKey( - base.getX(), base.getZ())); - } - if (bases.isEmpty() && flattenedHeights.isEmpty()) { - return EMPTY; - } - bases.sort((first, second) -> { - int yOrder = Integer.compare( - BlockPos.of(first).getY(), BlockPos.of(second).getY()); - return yOrder == 0 ? Long.compare(first, second) : yOrder; - }); - Set occupancy = new HashSet<>(); - for (long packedCell : vacuum.occupiedCells()) { - BlockPos cell = BlockPos.of(packedCell); - if (columns.contains(NativeStructureTemplateOccupancy.columnKey( - cell.getX(), cell.getZ()))) { - occupancy.add(packedCell); - } - } - return new SurfaceTerrainPlan( - bases, occupancy, flattenedHeights); - } - - public void primeHeightmaps(ChunkAccess chunk) { - if (flattenedHeights.isEmpty()) { - return; - } - Heightmap surface = chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG); - Heightmap floor = chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG); - WorldgenTerrainHeightmaps.primeTerrain(chunk, - (x, z) -> flattenedHeights.getOrDefault(NativeStructureTemplateOccupancy.columnKey(x, z), - surface.getFirstAvailable(x & 15, z & 15)), - (x, z) -> flattenedHeights.getOrDefault(NativeStructureTemplateOccupancy.columnKey(x, z), - floor.getFirstAvailable(x & 15, z & 15))); - } - } - - private static final class TemplateCellBudget { - private final int maximum; - private int consumed; - - private TemplateCellBudget(int maximum) { - if (maximum < 0) { - throw new IllegalArgumentException( - "Native structure surface template budget cannot be negative"); - } - this.maximum = maximum; - } - - private void consume(int amount) { - if (amount < 0 || consumed > maximum - amount) { - throw TemplateBudgetExceeded.INSTANCE; - } - consumed += amount; - } - } - - private static final class TemplateBudgetExceeded extends RuntimeException { - private static final TemplateBudgetExceeded INSTANCE = - new TemplateBudgetExceeded(); - - private TemplateBudgetExceeded() { - super(null, null, false, false); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceSupportBuilder.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceSupportBuilder.java deleted file mode 100644 index 988eed01f..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureSurfaceSupportBuilder.java +++ /dev/null @@ -1,305 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; - -final class NativeStructureSurfaceSupportBuilder { - private static final int MAX_BATCH_CELLS = 524_288; - private static final int MAX_BRIDGE_SPAN = 2; - - private NativeStructureSurfaceSupportBuilder() { - } - - static Set bridgeRigidPieceSupport( - WorldGenLevel world, BoundingBox area, - List targets, - Supplier templates) { - if (targets == null || targets.isEmpty()) { - return Set.of(); - } - Map planned; - try { - BatchCellBudget budget = new BatchCellBudget(); - List requests = new ArrayList<>(); - Set seenStarts = Collections.newSetFromMap( - new IdentityHashMap<>()); - for (NativeStructureTerrainIntegrator.TerrainTarget target : targets) { - if (target == null - || !NativeStructureSurfaceFitter.requiresSurfaceTerrain(target) - || target.terrain().resolvedMode() == IrisStructureTerrainMode.FLATTEN - && !NativeStructureSurfaceFitter.hasExposedFlattenPiece( - target.start(), (x, z) -> world.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - 1, - target.terrain().resolvedFlattenRange()) - || !seenStarts.add(target.start())) { - continue; - } - collectTargetRequests( - world, area, target, templates, budget, requests); - } - planned = planWrites(world, area, requests, budget); - } catch (BatchBudgetExceeded ignored) { - return Set.of(); - } - if (planned.isEmpty()) { - return Set.of(); - } - List> ordered = new ArrayList<>(planned.entrySet()); - ordered.sort(Map.Entry.comparingByKey()); - Set written = new HashSet<>(); - for (Map.Entry entry : ordered) { - BlockPos position = BlockPos.of(entry.getKey()); - BlockState existing = world.getBlockState(position); - if (!existing.isSolid() - && !NativeStructureVegetationClearer.isTreeBlock(existing) - && existing.getFluidState().isEmpty() - && world.setBlock(position, entry.getValue(), 2)) { - written.add(entry.getKey()); - } - } - return Set.copyOf(written); - } - - private static void collectTargetRequests( - WorldGenLevel world, BoundingBox area, - NativeStructureTerrainIntegrator.TerrainTarget target, - Supplier templates, - BatchCellBudget budget, List requests) { - if (!NativeStructureSurfaceFitter.requiresSurfaceTerrain(target)) { - return; - } - List anchors = rigidAnchors(target.start()); - if (anchors.size() < 2) { - return; - } - BlockPos referencePos = referencePosition(target.start()); - for (RigidAnchor upper : anchors) { - Map lowerMeets = candidateLowerMeets( - area, upper, anchors, budget); - if (lowerMeets.isEmpty()) { - continue; - } - NativeStructureTemplateOccupancy.OccupancyResult occupancy = - NativeStructureTemplateOccupancy.resolve( - world, upper.piece(), referencePos, area, templates, - position -> retain(position, area, lowerMeets), budget::consume); - if (!occupancy.resolved()) { - continue; - } - Map lowest = - NativeStructureTemplateOccupancy.lowestCells(occupancy.cells()); - for (Map.Entry entry - : lowest.entrySet()) { - NativeStructureTemplateOccupancy.LowestCell cell = entry.getValue(); - Integer lowerMeet = lowerMeets.get(entry.getKey()); - if (lowerMeet == null || cell.occupancy().blocker() - || !NativeStructureTemplateOccupancy.isSolidBase( - cell.occupancy().state()) - || cell.y() - lowerMeet < 2 - || cell.y() - lowerMeet > MAX_BRIDGE_SPAN) { - continue; - } - budget.consume(1); - requests.add(new SupportRequest( - cell.x(), cell.z(), lowerMeet, cell.y())); - } - } - } - - private static List rigidAnchors(StructureStart start) { - List anchors = new ArrayList<>(); - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() - != StructureTemplatePool.Projection.RIGID) { - continue; - } - BoundingBox bounds = poolPiece.getBoundingBox(); - int meetY = bounds.minY() + poolPiece.getGroundLevelDelta() - 1; - anchors.add(new RigidAnchor(poolPiece, bounds, meetY)); - } - return List.copyOf(anchors); - } - - private static Map candidateLowerMeets( - BoundingBox area, RigidAnchor upper, List anchors, - BatchCellBudget budget) { - Map lowerMeets = new HashMap<>(); - for (RigidAnchor lower : anchors) { - budget.consume(1); - int span = upper.meetY() - lower.meetY(); - if (lower.piece() == upper.piece() || span < 1 - || span > MAX_BRIDGE_SPAN - || lower.meetY() < area.minY() || lower.meetY() > area.maxY()) { - continue; - } - int minimumX = Math.max(area.minX(), - Math.max(upper.bounds().minX(), lower.bounds().minX())); - int maximumX = Math.min(area.maxX(), - Math.min(upper.bounds().maxX(), lower.bounds().maxX())); - int minimumZ = Math.max(area.minZ(), - Math.max(upper.bounds().minZ(), lower.bounds().minZ())); - int maximumZ = Math.min(area.maxZ(), - Math.min(upper.bounds().maxZ(), lower.bounds().maxZ())); - if (minimumX > maximumX || minimumZ > maximumZ) { - continue; - } - for (int x = minimumX; x <= maximumX; x++) { - for (int z = minimumZ; z <= maximumZ; z++) { - budget.consume(1); - lowerMeets.merge(columnKey(x, z), lower.meetY(), Math::max); - } - } - } - return lowerMeets; - } - - private static boolean retain( - BlockPos position, BoundingBox area, Map lowerMeets) { - if (!area.isInside(position)) { - return false; - } - Integer lowerMeet = lowerMeets.get(columnKey(position.getX(), position.getZ())); - return lowerMeet != null && position.getY() <= lowerMeet + MAX_BRIDGE_SPAN; - } - - private static Map planWrites( - WorldGenLevel world, BoundingBox area, - List requests, BatchCellBudget budget) { - List eligible = new ArrayList<>(); - Set materialKeys = new HashSet<>(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (SupportRequest request : requests) { - BlockState lowerTerrain = world.getBlockState(position.set( - request.x(), request.lowerMeetY(), request.z())); - if (!isTerrainSupport(lowerTerrain) - || !gapIsClear(world, area, position, request, budget)) { - continue; - } - eligible.add(request); - materialKeys.add(new MaterialKey( - request.x(), request.z(), request.lowerMeetY())); - } - Map materials = new HashMap<>(); - for (MaterialKey key : materialKeys) { - materials.put(key, resolveSupportState( - world, position, key.x(), key.z(), key.lowerMeetY(), budget)); - } - Map planned = new HashMap<>(); - Set conflicts = new HashSet<>(); - eligible.sort(Comparator.comparingInt(SupportRequest::x) - .thenComparingInt(SupportRequest::z) - .thenComparingInt(SupportRequest::lowerMeetY) - .thenComparingInt(SupportRequest::baseY)); - for (SupportRequest request : eligible) { - MaterialKey materialKey = new MaterialKey( - request.x(), request.z(), request.lowerMeetY()); - BlockState material = materials.get(materialKey); - for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) { - budget.consume(1); - long positionKey = BlockPos.asLong(request.x(), y, request.z()); - if (conflicts.contains(positionKey)) { - continue; - } - BlockState existing = planned.putIfAbsent(positionKey, material); - if (existing != null && !existing.equals(material)) { - planned.remove(positionKey); - conflicts.add(positionKey); - } - } - } - return planned; - } - - private static boolean gapIsClear( - WorldGenLevel world, BoundingBox area, BlockPos.MutableBlockPos position, - SupportRequest request, BatchCellBudget budget) { - for (int y = request.lowerMeetY() + 1; y < request.baseY(); y++) { - budget.consume(1); - if (!area.isInside(position.set(request.x(), y, request.z()))) { - return false; - } - BlockState state = world.getBlockState(position); - if (state.isSolid() || NativeStructureVegetationClearer.isTreeBlock(state) - || !state.getFluidState().isEmpty()) { - return false; - } - } - return true; - } - - private static BlockState resolveSupportState( - WorldGenLevel world, BlockPos.MutableBlockPos position, - int x, int z, int lowerMeetY, BatchCellBudget budget) { - budget.consume(1); - BlockState below = world.getBlockState(position.set(x, lowerMeetY - 1, z)); - if (isTerrainSupport(below)) { - return below; - } - return world.getBlockState(position.set(x, lowerMeetY, z)); - } - - private static boolean isTerrainSupport(BlockState state) { - return state.isSolid() - && !NativeStructureVegetationClearer.isTreeBlock(state) - && state.getFluidState().isEmpty(); - } - - private static BlockPos referencePosition(StructureStart start) { - BoundingBox bounds = start.getPieces().getFirst().getBoundingBox(); - BlockPos center = bounds.getCenter(); - return new BlockPos(center.getX(), bounds.minY(), center.getZ()); - } - - private static long columnKey(int x, int z) { - return (long) x << 32 ^ z & 0xffffffffL; - } - - private record RigidAnchor( - PoolElementStructurePiece piece, BoundingBox bounds, int meetY) { - } - - private record SupportRequest(int x, int z, int lowerMeetY, int baseY) { - } - - private record MaterialKey(int x, int z, int lowerMeetY) { - } - - private static final class BatchCellBudget { - private int consumed; - - private void consume(int amount) { - if (amount < 0 || consumed > MAX_BATCH_CELLS - amount) { - throw BatchBudgetExceeded.INSTANCE; - } - consumed += amount; - } - } - - private static final class BatchBudgetExceeded extends RuntimeException { - private static final BatchBudgetExceeded INSTANCE = new BatchBudgetExceeded(); - - private BatchBudgetExceeded() { - super(null, null, false, false); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancy.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancy.java deleted file mode 100644 index f904a2127..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplateOccupancy.java +++ /dev/null @@ -1,165 +0,0 @@ -package art.arcane.iris.nativegen; - -import net.minecraft.core.BlockPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -final class NativeStructureTemplateOccupancy { - private static final OccupancyCell BLOCKER = new OccupancyCell(null, true); - - private NativeStructureTemplateOccupancy() { - } - - static OccupancyResult resolve( - WorldGenLevel world, PoolElementStructurePiece piece, - BlockPos referencePosition, BoundingBox processingArea, - Supplier templates, - PositionFilter filter, CellBudget budget) { - List leaves = new ArrayList<>(); - if (!flattenSingles(piece.getElement(), leaves)) { - return new OccupancyResult(false, Map.of()); - } - List placements = new ArrayList<>(leaves.size()); - Map occupancy = new HashMap<>(); - for (SinglePoolElement leaf : leaves) { - StructurePlaceSettings settings = NativeStructureReflection.resolvePlacementSettings( - leaf, piece, processingArea); - StructureTemplate template = NativeStructureReflection.resolveTemplate(leaf, templates); - List rawBlocks = - NativeStructureReflection.resolveTemplateBlocks( - template, settings, piece.getPosition()); - budget.consume(rawBlocks.size()); - placements.add(new LeafPlacement(settings, rawBlocks, template)); - for (StructureTemplate.StructureBlockInfo raw : rawBlocks) { - BlockPos position = piece.getPosition().offset( - StructureTemplate.calculateRelativePosition(settings, raw.pos())); - if (filter.retain(position)) { - occupancy.put(position.asLong(), BLOCKER); - } - } - } - for (LeafPlacement placement : placements) { - List processed = - NativeStructureReflection.processTemplateBlocks( - world, piece.getPosition(), referencePosition, - placement.settings(), placement.rawBlocks(), placement.template()); - budget.consume(processed.size()); - for (StructureTemplate.StructureBlockInfo block : processed) { - BlockPos position = block.pos(); - if (!filter.retain(position)) { - continue; - } - BlockState state = block.state() - .mirror(placement.settings().getMirror()) - .rotate(placement.settings().getRotation()); - occupancy.put(position.asLong(), new OccupancyCell(state, false)); - } - } - return new OccupancyResult(true, occupancy); - } - - static Map lowestCells(Map occupancy) { - Map lowest = new HashMap<>(); - for (Map.Entry entry : occupancy.entrySet()) { - OccupancyCell cell = entry.getValue(); - BlockPos position = BlockPos.of(entry.getKey()); - long column = columnKey(position.getX(), position.getZ()); - LowestCell current = lowest.get(column); - if (current == null || position.getY() < current.y()) { - lowest.put(column, new LowestCell( - position.getX(), position.getY(), position.getZ(), cell)); - } - } - return lowest; - } - - static Map lowestProcessedSolidCells(Map occupancy) { - Map lowest = new HashMap<>(); - for (Map.Entry entry : occupancy.entrySet()) { - OccupancyCell cell = entry.getValue(); - if (cell.blocker() || !isSolidBase(cell.state())) { - continue; - } - BlockPos position = BlockPos.of(entry.getKey()); - long column = columnKey(position.getX(), position.getZ()); - LowestCell current = lowest.get(column); - if (current == null || position.getY() < current.y()) { - lowest.put(column, new LowestCell( - position.getX(), position.getY(), position.getZ(), cell)); - } - } - return lowest; - } - - static boolean isSolidBase(BlockState state) { - return state != null && state.isSolid() - && !state.is(Blocks.STRUCTURE_VOID) - && !state.is(Blocks.JIGSAW) - && state.getFluidState().isEmpty(); - } - - static long columnKey(int x, int z) { - return (long) x << 32 ^ z & 0xffffffffL; - } - - private static boolean flattenSingles( - StructurePoolElement element, List leaves) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - if (!flattenSingles(child, leaves)) { - return false; - } - } - return true; - } - if (element == EmptyPoolElement.INSTANCE) { - return true; - } - if (element instanceof SinglePoolElement singleElement) { - leaves.add(singleElement); - return true; - } - return false; - } - - @FunctionalInterface - interface PositionFilter { - boolean retain(BlockPos position); - } - - @FunctionalInterface - interface CellBudget { - void consume(int amount); - } - - record OccupancyResult(boolean resolved, Map cells) { - } - - record OccupancyCell(BlockState state, boolean blocker) { - } - - record LowestCell(int x, int y, int z, OccupancyCell occupancy) { - } - - private record LeafPlacement( - StructurePlaceSettings settings, - List rawBlocks, - StructureTemplate template) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplatePoolBounds.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplatePoolBounds.java deleted file mode 100644 index c5335542a..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTemplatePoolBounds.java +++ /dev/null @@ -1,228 +0,0 @@ -package art.arcane.iris.nativegen; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.pools.EmptyPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.FeaturePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.pools.alias.DirectPoolAlias; -import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasBinding; -import net.minecraft.world.level.levelgen.structure.pools.alias.RandomGroupPoolAlias; -import net.minecraft.world.level.levelgen.structure.pools.alias.RandomPoolAlias; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.Collections; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Set; -import java.util.function.Function; - -public final class NativeStructureTemplatePoolBounds { - private NativeStructureTemplatePoolBounds() { - } - - public static int sourceHorizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source) { - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - return sourceHorizontalSpan( - templateManager, - source.getStartPool(), - source.getPoolAliases(), - key -> pools.getValue(key.identifier())); - } - - public static int horizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - String templatePoolKey) { - Identifier identifier = Identifier.tryParse(templatePoolKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered template pool key: " + templatePoolKey); - } - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - StructureTemplatePool pool = pools.getValue(identifier); - if (pool == null) { - throw new IllegalArgumentException("Registered template pool does not exist: " + templatePoolKey); - } - return horizontalSpan(pool, templateManager); - } - - public static int horizontalSpan(RegistryAccess registryAccess, - StructureTemplateManager templateManager, - JigsawStructure source, - String templatePoolKey) { - Identifier identifier = Identifier.tryParse(templatePoolKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered template pool key: " + templatePoolKey); - } - Registry pools = registryAccess.lookupOrThrow(Registries.TEMPLATE_POOL); - ResourceKey startPoolKey = ResourceKey.create( - Registries.TEMPLATE_POOL, identifier); - StructureTemplatePool startPool = pools.getValue(identifier); - if (startPool == null) { - throw new IllegalArgumentException("Registered template pool does not exist: " + templatePoolKey); - } - return sourceHorizontalSpan( - templateManager, - startPool, - startPoolKey, - source.getPoolAliases(), - key -> pools.getValue(key.identifier())); - } - - static int sourceHorizontalSpan(StructureTemplateManager templateManager, - Holder startPool, - List aliases, - Function, StructureTemplatePool> poolLookup) { - return sourceHorizontalSpan(templateManager, startPool.value(), - startPool.unwrapKey().orElse(null), aliases, poolLookup); - } - - static int sourceHorizontalSpan(StructureTemplateManager templateManager, - StructureTemplatePool startPool, - ResourceKey startPoolKey, - List aliases, - Function, StructureTemplatePool> poolLookup) { - int maximumSpan = directHorizontalSpan(startPool, templateManager); - if (startPoolKey == null) { - return maximumSpan; - } - Set> targets = new HashSet<>(); - for (PoolAliasBinding binding : aliases) { - collectTargets(binding, startPoolKey, targets); - } - for (ResourceKey target : targets) { - StructureTemplatePool targetPool = poolLookup.apply(target); - if (targetPool == null) { - throw new IllegalStateException("Jigsaw start-pool alias target does not exist: " - + target.identifier()); - } - maximumSpan = Math.max(maximumSpan, directHorizontalSpan(targetPool, templateManager)); - } - return maximumSpan; - } - - private static void collectTargets(PoolAliasBinding binding, - ResourceKey startPoolKey, - Set> targets) { - if (binding instanceof DirectPoolAlias direct) { - if (direct.alias().equals(startPoolKey)) { - targets.add(direct.target()); - } - return; - } - if (binding instanceof RandomPoolAlias random) { - if (random.alias().equals(startPoolKey)) { - random.targets().unwrap().forEach(entry -> targets.add(entry.value())); - } - return; - } - if (binding instanceof RandomGroupPoolAlias group) { - group.groups().unwrap().forEach(entry -> entry.value().forEach( - nested -> collectTargets(nested, startPoolKey, targets))); - return; - } - throw new IllegalStateException("Unsupported jigsaw pool alias type: " - + binding.getClass().getName()); - } - - static int horizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager) { - Set visited = Collections.newSetFromMap(new IdentityHashMap<>()); - return horizontalSpan(pool, templateManager, visited); - } - - private static int horizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager, - Set visited) { - if (!visited.add(pool)) { - return 0; - } - int maximumSpan = directHorizontalSpan(pool, templateManager); - Holder fallback = pool.getFallback(); - if (fallback == null || !fallback.isBound()) { - throw new IllegalStateException("Template pool has an unresolved fallback"); - } - return Math.max(maximumSpan, horizontalSpan(fallback.value(), templateManager, visited)); - } - - private static int directHorizontalSpan(StructureTemplatePool pool, - StructureTemplateManager templateManager) { - int maximumSpan = 0; - for (Pair entry : pool.getTemplates()) { - StructurePoolElement element = entry.getFirst(); - validateElement(element, templateManager); - maximumSpan = Math.max(maximumSpan, horizontalSpan(element, templateManager)); - } - return maximumSpan; - } - - private static void validateElement(StructurePoolElement element, - StructureTemplateManager templateManager) { - if (element == EmptyPoolElement.INSTANCE || element instanceof FeaturePoolElement) { - return; - } - if (element instanceof SinglePoolElement single) { - Identifier template; - try { - template = single.getTemplateLocation(); - } catch (RuntimeException inlineTemplate) { - return; - } - if (templateManager.get(template).isEmpty()) { - throw new IllegalStateException("Template pool element does not resolve a structure template: " - + template); - } - return; - } - if (element instanceof ListPoolElement list) { - for (StructurePoolElement child : list.getElements()) { - validateElement(child, templateManager); - } - return; - } - } - - private static int horizontalSpan(StructurePoolElement element, - StructureTemplateManager templateManager) { - if (element == EmptyPoolElement.INSTANCE) { - return 0; - } - long maximumSpan = 0L; - for (Rotation rotation : Rotation.values()) { - BoundingBox box; - try { - box = element.getBoundingBox(templateManager, BlockPos.ZERO, rotation); - } catch (RuntimeException | LinkageError error) { - throw new IllegalStateException("Template pool element " - + element.getClass().getName() - + " could not provide bounded geometry for rotation " + rotation, error); - } - if (box == null) { - throw new IllegalStateException("Template pool element " - + element.getClass().getName() - + " returned no bounded geometry for rotation " + rotation); - } - long xSpan = (long) box.maxX() - box.minX() + 1L; - long zSpan = (long) box.maxZ() - box.minZ() + 1L; - maximumSpan = Math.max(maximumSpan, Math.max(xSpan, zSpan)); - } - if (maximumSpan < 0L || maximumSpan > Integer.MAX_VALUE) { - throw new IllegalStateException("Template pool element has an unbounded horizontal span"); - } - return (int) maximumSpan; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTerrainIntegrator.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTerrainIntegrator.java deleted file mode 100644 index 0509b4b7f..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureTerrainIntegrator.java +++ /dev/null @@ -1,1096 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.generation.mantle.StructureCarveEnvelope; -import art.arcane.iris.generation.mantle.StructureCarvingFootprint; -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; -import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.volmlib.util.noise.CNG; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.Rotation; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.dimension.BuiltinDimensionTypes; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.TerrainAdjustment; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.pools.LegacySinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.ListPoolElement; -import net.minecraft.world.level.levelgen.structure.pools.SinglePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructurePoolElement; -import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; - -public final class NativeStructureTerrainIntegrator { - private static final long CARVE_CEILING_ROLL_SIGNATURE = 0x2A17L; - private static final long CARVE_FLOOR_ROLL_SIGNATURE = 0x5B3DL; - private static final long CARVE_LOBE_SIGNATURE = 0x7C41L; - private static final int MAX_CACHED_CARVE_CELLS = 2_000_000; - private static final int MAX_CARVE_COLUMNS = 2_000_000; - private static final int MAX_TEMPLATE_OCCUPANCY_CELLS = 4_194_304; - private static final int SOURCE_BURY_HORIZONTAL_RADIUS = 6; - private static final int SOURCE_BURY_VERTICAL_RADIUS = 12; - private static final int SOURCE_ENCAPSULATE_RADIUS = 12; - private static final int SOURCE_JUNCTION_RADIUS = 12; - private static final int SOURCE_MATERIAL_SAMPLE_RADIUS = 8; - private static final List TEMPLATE_VOID_BLOCKS = List.of(Blocks.AIR, Blocks.STRUCTURE_VOID); - private static final Set VANILLA_SOURCE_TERRAIN_BLOCKS = Set.of( - Blocks.STONE, Blocks.GRANITE, Blocks.DIORITE, Blocks.ANDESITE, - Blocks.TUFF, Blocks.DEEPSLATE, Blocks.NETHERRACK, Blocks.BASALT, - Blocks.BLACKSTONE, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.ROOTED_DIRT, - Blocks.GRASS_BLOCK, Blocks.PODZOL, Blocks.MYCELIUM, Blocks.MUD, - Blocks.MOSS_BLOCK, Blocks.SAND, Blocks.RED_SAND, Blocks.TERRACOTTA, - Blocks.CRIMSON_NYLIUM, Blocks.WARPED_NYLIUM, Blocks.SNOW_BLOCK, - Blocks.ICE, Blocks.PACKED_ICE, Blocks.BLUE_ICE, Blocks.SOUL_SAND, - Blocks.SOUL_SOIL, Blocks.END_STONE, Blocks.GRAVEL, Blocks.CLAY, - Blocks.CALCITE, Blocks.DRIPSTONE_BLOCK, Blocks.SANDSTONE, - Blocks.RED_SANDSTONE, Blocks.SCULK); - // Weakly keyed on the StructureStart so retired starts (world unload, generation done) - // are collectable instead of pinned forever by a process-wide static; values hold only - // primitives, so there is no value-to-key cycle defeating the weak keys. - private static final Map> CARVE_FOOTPRINTS = - new WeakHashMap<>(16); - private static final ConcurrentHashMap> - CARVE_FOOTPRINT_BUILDS = new ConcurrentHashMap<>(); - private static int cachedCarveCells; - - private NativeStructureTerrainIntegrator() { - } - - public static IrisStructureTerrain resolveNativeTerrain(StructureStart start, - IrisStructureTerrain configuredTerrain) { - return configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - } - - static void integrateTerrain(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain configuredTerrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver) { - SourceTerrainSnapshot sourceTerrain = requiresSourceTerrainFill(start, configuredTerrain) - ? captureSourceTerrain(world, area, start, configuredTerrain) : null; - integrateTerrain(world, area, structureId, start, configuredTerrain, - paletteBlockResolver, sourceTerrain); - } - - static void integrateTerrain(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain configuredTerrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver, - SourceTerrainSnapshot sourceTerrain) { - IrisStructureTerrain terrain = configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - IrisStructureTerrainMode mode = terrain.resolvedMode(); - if (mode == IrisStructureTerrainMode.SOURCE) { - integrateSourceTerrain(world, area, start, sourceTerrain); - return; - } - if (mode == IrisStructureTerrainMode.PRESERVE) { - return; - } - if (mode == IrisStructureTerrainMode.VACUUM || mode == IrisStructureTerrainMode.FLATTEN) { - return; - } - if (mode == IrisStructureTerrainMode.ENCASE) { - encasePieces(world, area, structureId, start, terrain, paletteBlockResolver); - return; - } - if (mode != IrisStructureTerrainMode.BORE && mode != IrisStructureTerrainMode.FORCE_CARVE) { - throw new IllegalStateException("Native structure terrain mode " + mode - + " is not implemented for '" + structureId + "'"); - } - IrisStructureCarveShape shape = mode == IrisStructureTerrainMode.BORE - ? IrisStructureCarveShape.BOX : terrain.resolvedShape(); - if (shape == IrisStructureCarveShape.BOX) { - carvePieceBoxes(world, area, start, terrain); - return; - } - carveOrganicColumns(world, area, organicCarve( - carveFootprint(start, Math.max(0, terrain.getHorizontalPadding()), - () -> world.getLevel().getStructureManager()), - terrain, shape, carveNoiseIdentity(world, structureId, start))); - } - - static SourceTerrainSnapshot captureSourceTerrain( - WorldGenLevel world, BoundingBox area, List targets) { - BitSet requiredLayers = new BitSet(area.getYSpan()); - boolean requiresSnapshot = false; - for (TerrainTarget target : targets) { - if (target != null && requiresSourceTerrainFill(target.start(), target.terrain())) { - requiresSnapshot = true; - markSourceTerrainLayers(area, target.start(), target.terrain(), requiredLayers); - } - } - return requiresSnapshot ? SourceTerrainSnapshot.capture(world, area, requiredLayers) : null; - } - - private static SourceTerrainSnapshot captureSourceTerrain( - WorldGenLevel world, BoundingBox area, StructureStart start, - IrisStructureTerrain configuredTerrain) { - BitSet requiredLayers = new BitSet(area.getYSpan()); - markSourceTerrainLayers(area, start, configuredTerrain, requiredLayers); - return SourceTerrainSnapshot.capture(world, area, requiredLayers); - } - - private static void markSourceTerrainLayers( - BoundingBox area, StructureStart start, IrisStructureTerrain configuredTerrain, - BitSet requiredLayers) { - if (!requiresSourceTerrainFill(start, configuredTerrain)) { - return; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - long horizontalDistanceSquared = minimumHorizontalDistanceSquared(area, bounds); - if (adjustment == TerrainAdjustment.BURY) { - long remainingDistanceSquared = (long) SOURCE_BURY_VERTICAL_RADIUS - * SOURCE_BURY_VERTICAL_RADIUS - 1L - horizontalDistanceSquared * 4L; - if (remainingDistanceSquared < 0L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - long groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - } - markLayers(area, requiredLayers, - groundY - verticalRadius, groundY + verticalRadius); - } else { - long remainingDistanceSquared = (long) SOURCE_ENCAPSULATE_RADIUS - * SOURCE_ENCAPSULATE_RADIUS - 1L - horizontalDistanceSquared; - if (remainingDistanceSquared < 0L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - markLayers(area, requiredLayers, - (long) bounds.minY() - verticalRadius, - (long) bounds.maxY() + verticalRadius); - } - } - markSourceJunctionLayers(area, start, requiredLayers); - } - - private static void markSourceJunctionLayers( - BoundingBox area, StructureStart start, BitSet requiredLayers) { - Set anchors = sourceJunctionAnchors(start); - for (JunctionAnchor anchor : anchors) { - long deltaX = intervalDistance(area.minX(), area.maxX(), anchor.x(), anchor.x()); - long deltaZ = intervalDistance(area.minZ(), area.maxZ(), anchor.z(), anchor.z()); - long horizontalDistanceSquared = deltaX * deltaX + deltaZ * deltaZ; - long remainingDistanceSquared = (long) SOURCE_JUNCTION_RADIUS - * SOURCE_JUNCTION_RADIUS - 1L - horizontalDistanceSquared; - if (remainingDistanceSquared < 1L) { - continue; - } - int verticalRadius = (int) Math.floor(Math.sqrt(remainingDistanceSquared)); - markLayers(area, requiredLayers, - (long) anchor.y() - verticalRadius, (long) anchor.y() - 1L); - } - } - - private static long minimumHorizontalDistanceSquared(BoundingBox area, BoundingBox bounds) { - long deltaX = intervalDistance(area.minX(), area.maxX(), bounds.minX(), bounds.maxX()); - long deltaZ = intervalDistance(area.minZ(), area.maxZ(), bounds.minZ(), bounds.maxZ()); - return deltaX * deltaX + deltaZ * deltaZ; - } - - private static long intervalDistance(int firstMin, int firstMax, int secondMin, int secondMax) { - if (firstMax < secondMin) { - return (long) secondMin - firstMax; - } - if (firstMin > secondMax) { - return (long) firstMin - secondMax; - } - return 0L; - } - - private static void markLayers( - BoundingBox area, BitSet requiredLayers, long minimumY, long maximumY) { - long clippedMinimumY = Math.max(area.minY(), minimumY); - long clippedMaximumY = Math.min(area.maxY(), maximumY); - if (clippedMinimumY > clippedMaximumY) { - return; - } - int fromIndex = Math.toIntExact(clippedMinimumY - area.minY()); - int toIndex = Math.toIntExact(clippedMaximumY - area.minY() + 1L); - requiredLayers.set(fromIndex, toIndex); - } - - private static boolean requiresSourceTerrainFill( - StructureStart start, IrisStructureTerrain configuredTerrain) { - if (start == null || !start.isValid()) { - return false; - } - IrisStructureTerrain terrain = configuredTerrain == null - ? new IrisStructureTerrain().setMode(IrisStructureTerrainMode.SOURCE) - : configuredTerrain; - if (terrain.resolvedMode() != IrisStructureTerrainMode.SOURCE) { - return false; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - return adjustment == TerrainAdjustment.BURY - || adjustment == TerrainAdjustment.ENCAPSULATE; - } - - static boolean clearsLegacyTemplateAir(StructureStart start, IrisStructureTerrain terrain) { - if (start == null || !start.isValid() || terrain == null) { - return false; - } - IrisStructureTerrainMode mode = terrain.resolvedMode(); - return mode == IrisStructureTerrainMode.ENCASE - || mode == IrisStructureTerrainMode.SOURCE - && start.getStructure().terrainAdaptation() != TerrainAdjustment.NONE; - } - - private static void integrateSourceTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - if (start == null || !start.isValid()) { - return; - } - TerrainAdjustment adjustment = start.getStructure().terrainAdaptation(); - if (adjustment == TerrainAdjustment.BURY) { - fillBuriedTerrain(world, area, start, requireSourceTerrain( - world, area, start, sourceTerrain)); - } else if (adjustment == TerrainAdjustment.ENCAPSULATE) { - fillEncapsulatedTerrain(world, area, start, requireSourceTerrain( - world, area, start, sourceTerrain)); - } - } - - private static SourceTerrainSnapshot requireSourceTerrain( - WorldGenLevel world, BoundingBox area, StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - return sourceTerrain == null - ? captureSourceTerrain(world, area, start, null) : sourceTerrain; - } - - private static void fillBuriedTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int groundY = bounds.minY(); - if (piece instanceof PoolElementStructurePiece poolPiece) { - groundY += poolPiece.getGroundLevelDelta(); - } - int minX = Math.max(area.minX(), bounds.minX() - SOURCE_BURY_HORIZONTAL_RADIUS); - int maxX = Math.min(area.maxX(), bounds.maxX() + SOURCE_BURY_HORIZONTAL_RADIUS); - int minY = Math.max(area.minY(), groundY - SOURCE_BURY_VERTICAL_RADIUS); - int maxY = Math.min(area.maxY(), groundY + SOURCE_BURY_VERTICAL_RADIUS); - int minZ = Math.max(area.minZ(), bounds.minZ() - SOURCE_BURY_HORIZONTAL_RADIUS); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + SOURCE_BURY_HORIZONTAL_RADIUS); - for (int x = minX; x <= maxX; x++) { - int outX = outset(x, bounds.minX(), bounds.maxX()); - for (int z = minZ; z <= maxZ; z++) { - int outZ = outset(z, bounds.minZ(), bounds.maxZ()); - for (int y = minY; y <= maxY; y++) { - int vertical = Math.abs(y - groundY); - if (!insideBurialEnvelope(outX, vertical, outZ)) { - continue; - } - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - fillSourceJunctionTerrain(world, area, start, position, sourceTerrain); - } - - private static void fillEncapsulatedTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (StructurePiece piece : start.getPieces()) { - if (!isSourceRigidPiece(piece)) { - continue; - } - BoundingBox bounds = piece.getBoundingBox(); - int minX = Math.max(area.minX(), bounds.minX() - SOURCE_ENCAPSULATE_RADIUS); - int maxX = Math.min(area.maxX(), bounds.maxX() + SOURCE_ENCAPSULATE_RADIUS); - int minY = Math.max(area.minY(), bounds.minY() - SOURCE_ENCAPSULATE_RADIUS); - int maxY = Math.min(area.maxY(), bounds.maxY() + SOURCE_ENCAPSULATE_RADIUS); - int minZ = Math.max(area.minZ(), bounds.minZ() - SOURCE_ENCAPSULATE_RADIUS); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + SOURCE_ENCAPSULATE_RADIUS); - for (int x = minX; x <= maxX; x++) { - int outX = outset(x, bounds.minX(), bounds.maxX()); - for (int z = minZ; z <= maxZ; z++) { - int outZ = outset(z, bounds.minZ(), bounds.maxZ()); - for (int y = minY; y <= maxY; y++) { - int outY = outset(y, bounds.minY(), bounds.maxY()); - if (!insideEncapsulationEnvelope(outX, outY, outZ)) { - continue; - } - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - fillSourceJunctionTerrain(world, area, start, position, sourceTerrain); - } - - static boolean insideBurialEnvelope(int outX, int verticalDistance, int outZ) { - long horizontalSquared = (long) outX * outX + (long) outZ * outZ; - long verticalSquared = (long) verticalDistance * verticalDistance; - return horizontalSquared * 4L + verticalSquared - < (long) SOURCE_BURY_VERTICAL_RADIUS * SOURCE_BURY_VERTICAL_RADIUS; - } - - static boolean insideEncapsulationEnvelope(int outX, int outY, int outZ) { - return (long) outX * outX + (long) outY * outY + (long) outZ * outZ - < (long) SOURCE_ENCAPSULATE_RADIUS * SOURCE_ENCAPSULATE_RADIUS; - } - - static boolean isSourceRigidPiece(StructurePiece piece) { - if (piece == null) { - return false; - } - return !(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() == StructureTemplatePool.Projection.RIGID; - } - - static boolean insideSourceJunctionEnvelope(int deltaX, int deltaY, int deltaZ) { - if (deltaY >= 0) { - return false; - } - return (long) deltaX * deltaX + (long) deltaY * deltaY + (long) deltaZ * deltaZ - < (long) SOURCE_JUNCTION_RADIUS * SOURCE_JUNCTION_RADIUS; - } - - private static void fillSourceJunctionTerrain(WorldGenLevel world, BoundingBox area, - StructureStart start, - BlockPos.MutableBlockPos position, - SourceTerrainSnapshot sourceTerrain) { - Set anchors = sourceJunctionAnchors(start); - for (JunctionAnchor anchor : anchors) { - int minX = Math.max(area.minX(), anchor.x() - SOURCE_JUNCTION_RADIUS + 1); - int maxX = Math.min(area.maxX(), anchor.x() + SOURCE_JUNCTION_RADIUS - 1); - int minY = Math.max(area.minY(), anchor.y() - SOURCE_JUNCTION_RADIUS + 1); - int maxY = Math.min(area.maxY(), anchor.y() - 1); - int minZ = Math.max(area.minZ(), anchor.z() - SOURCE_JUNCTION_RADIUS + 1); - int maxZ = Math.min(area.maxZ(), anchor.z() + SOURCE_JUNCTION_RADIUS - 1); - for (int x = minX; x <= maxX; x++) { - int deltaX = x - anchor.x(); - for (int z = minZ; z <= maxZ; z++) { - int deltaZ = z - anchor.z(); - for (int y = minY; y <= maxY; y++) { - if (insideSourceJunctionEnvelope(deltaX, y - anchor.y(), deltaZ)) { - fillEncaseable(world, position.set(x, y, z), sourceTerrain); - } - } - } - } - } - } - - private static Set sourceJunctionAnchors(StructureStart start) { - Set anchors = new HashSet<>(); - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece)) { - continue; - } - for (JigsawJunction junction : poolPiece.getJunctions()) { - anchors.add(new JunctionAnchor( - junction.getSourceX(), junction.getSourceGroundY(), junction.getSourceZ())); - } - } - return anchors; - } - - private static int outset(int value, int minimum, int maximum) { - if (value < minimum) { - return minimum - value; - } - return Math.max(0, value - maximum); - } - - private static void fillEncaseable(WorldGenLevel world, BlockPos position, - SourceTerrainSnapshot sourceTerrain) { - if (isEncaseable(world.getBlockState(position))) { - world.setBlock(position, sourceEncaseBlock(world, position, sourceTerrain), 2); - } - } - - static BlockState sourceEncaseBlock(WorldGenLevel world, BlockPos position) { - int minX = Math.subtractExact(position.getX(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int minZ = Math.subtractExact(position.getZ(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int maxX = Math.addExact(position.getX(), SOURCE_MATERIAL_SAMPLE_RADIUS); - int maxZ = Math.addExact(position.getZ(), SOURCE_MATERIAL_SAMPLE_RADIUS); - BoundingBox sampleArea = new BoundingBox(minX, position.getY(), minZ, - maxX, position.getY(), maxZ); - BitSet requiredLayers = new BitSet(1); - requiredLayers.set(0); - SourceTerrainSnapshot sourceTerrain = SourceTerrainSnapshot.capture( - world, sampleArea, requiredLayers); - return sourceEncaseBlock(world, position, sourceTerrain); - } - - static BlockState sourceEncaseBlock(WorldGenLevel world, BlockPos position, - SourceTerrainSnapshot sourceTerrain) { - BlockPos.MutableBlockPos probe = new BlockPos.MutableBlockPos(); - for (int distance = 1; distance <= SOURCE_MATERIAL_SAMPLE_RADIUS; distance++) { - BlockState sampled = sourceTerrain.stateAt(probe.set( - position.getX() - distance, position.getY(), position.getZ())); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX() + distance, position.getY(), position.getZ())); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX(), position.getY(), position.getZ() - distance)); - if (sampled != null) { - return sampled; - } - sampled = sourceTerrain.stateAt(probe.set( - position.getX(), position.getY(), position.getZ() + distance)); - if (sampled != null) { - return sampled; - } - } - return defaultEncaseBlock(world, position.getY()); - } - - private static BlockState sourceTerrainBlock(BlockState state) { - if (!state.isSolid() || NativeStructureVegetationClearer.isTreeBlock(state)) { - return null; - } - return VANILLA_SOURCE_TERRAIN_BLOCKS.contains(state.getBlock()) - || state.is(BlockTags.BASE_STONE_OVERWORLD) - || state.is(BlockTags.BASE_STONE_NETHER) - || state.is(BlockTags.SUBSTRATE_OVERWORLD) - || state.is(BlockTags.DIRT) - || state.is(BlockTags.SAND) - || state.is(BlockTags.TERRACOTTA) - || state.is(BlockTags.MUD) - || state.is(BlockTags.MOSS_BLOCKS) - || state.is(BlockTags.GRASS_BLOCKS) - || state.is(BlockTags.NYLIUM) - || state.is(BlockTags.SNOW) - || state.is(BlockTags.ICE) - || state.is(BlockTags.CORAL_BLOCKS) - || state.is(BlockTags.SOUL_FIRE_BASE_BLOCKS) - ? state : null; - } - - static List contentPieceBounds(StructureStart start) { - List bounds = new ArrayList<>(start.getPieces().size()); - for (StructurePiece piece : start.getPieces()) { - bounds.add(piece.getBoundingBox()); - } - return List.copyOf(bounds); - } - - /** - * Per-column occupancy of the whole start, cached because a single structure spans many chunks and - * every one of them carves against the same shrinkwrapped footprint. - */ - static StructureCarvingFootprint carveFootprint(StructureStart start, int horizontalPadding, - Supplier templates) { - CarveFootprintKey key = new CarveFootprintKey(start, horizontalPadding); - StructureCarvingFootprint cached = cachedCarveFootprint(key); - if (cached != null) { - return cached; - } - CompletableFuture build = new CompletableFuture<>(); - CompletableFuture active = CARVE_FOOTPRINT_BUILDS.putIfAbsent(key, build); - if (active != null) { - return awaitCarveFootprint(active); - } - // Close the check-then-claim window: a racer whose cache check missed before the - // previous builder cached can claim the build slot after that builder retired it, - // and would rebuild a second instance without this re-check. - StructureCarvingFootprint published = cachedCarveFootprint(key); - if (published != null) { - build.complete(published); - CARVE_FOOTPRINT_BUILDS.remove(key, build); - return published; - } - try { - StructureCarvingFootprint footprint = StructureCarvingFootprint.fromColumns( - sink -> emitCarveColumns(start, templates, sink), horizontalPadding, MAX_CARVE_COLUMNS); - if (footprint == null) { - throw new IllegalStateException("Native structure carve footprint is empty or exceeds " - + MAX_CARVE_COLUMNS + " columns"); - } - cacheCarveFootprint(key, footprint); - build.complete(footprint); - return footprint; - } catch (RuntimeException | Error error) { - build.completeExceptionally(error); - throw error; - } finally { - CARVE_FOOTPRINT_BUILDS.remove(key, build); - } - } - - static int cachedCarveFootprintCells() { - synchronized (CARVE_FOOTPRINTS) { - cachedCarveCells = liveCarveCells(); - return cachedCarveCells; - } - } - - private static int liveCarveCells() { - int total = 0; - for (Map perStart : CARVE_FOOTPRINTS.values()) { - for (CachedCarveFootprint cached : perStart.values()) { - total += cached.cells(); - } - } - return total; - } - - static int maximumCachedCarveFootprintCells() { - return MAX_CACHED_CARVE_CELLS; - } - - private static StructureCarvingFootprint cachedCarveFootprint(CarveFootprintKey key) { - synchronized (CARVE_FOOTPRINTS) { - Map perStart = CARVE_FOOTPRINTS.get(key.start()); - CachedCarveFootprint cached = perStart == null ? null : perStart.get(key.padding()); - return cached == null ? null : cached.footprint(); - } - } - - private static StructureCarvingFootprint awaitCarveFootprint( - CompletableFuture future) { - return NativeBuildFutures.awaitBuild(future, "Native structure carve footprint build"); - } - - private static void cacheCarveFootprint(CarveFootprintKey key, - StructureCarvingFootprint footprint) { - int cells = Math.multiplyExact(footprint.width(), footprint.depth()); - synchronized (CARVE_FOOTPRINTS) { - // Weak keys expunge silently as the GC drops retired starts; re-derive the live - // total so the cell budget tracks reality instead of drifting. - cachedCarveCells = liveCarveCells(); - Map perStart = - CARVE_FOOTPRINTS.computeIfAbsent(key.start(), ignored -> new LinkedHashMap<>(4)); - CachedCarveFootprint previous = perStart.remove(key.padding()); - if (previous != null) { - cachedCarveCells -= previous.cells(); - } - Iterator>> eviction = - CARVE_FOOTPRINTS.entrySet().iterator(); - while (cachedCarveCells + cells > MAX_CACHED_CARVE_CELLS && eviction.hasNext()) { - Map.Entry> victim = eviction.next(); - if (victim.getKey() == key.start()) { - continue; - } - for (CachedCarveFootprint evicted : victim.getValue().values()) { - cachedCarveCells -= evicted.cells(); - } - eviction.remove(); - } - perStart.put(key.padding(), new CachedCarveFootprint(footprint, cells)); - cachedCarveCells += cells; - } - } - - static OrganicCarve organicCarve(StructureCarvingFootprint footprint, IrisStructureTerrain terrain, - IrisStructureCarveShape shape, long identity) { - double strength = terrain.resolvedErosionStrength(); - double lobeStrength = terrain.resolvedLobeStrength(); - RNG noiseRng = new RNG(identity); - CNG blob = null; - CNG ceilingRoll = null; - CNG floorRoll = null; - CNG lobe = null; - if (shape == IrisStructureCarveShape.ERODED && strength > 0D) { - blob = CNG.signature(noiseRng); - ceilingRoll = CNG.signature(noiseRng.nextParallelRNG(CARVE_CEILING_ROLL_SIGNATURE)); - floorRoll = CNG.signature(noiseRng.nextParallelRNG(CARVE_FLOOR_ROLL_SIGNATURE)); - } - if (shape == IrisStructureCarveShape.ERODED && lobeStrength > 0D) { - // A plain single octave channel: the fractured signature noise has no usable low frequency band. - lobe = new CNG(noiseRng.nextParallelRNG(CARVE_LOBE_SIGNATURE), 1D, 1); - } - return new OrganicCarve(footprint, shape, - Math.max(0, terrain.getHorizontalPadding()), - Math.max(0, terrain.getCeilingPadding()), - Math.max(0, terrain.getFloorPadding()), - strength, terrain.resolvedErosionFrequency(), blob, ceilingRoll, floorRoll, - lobe, terrain.resolvedLobeFrequency(), lobeStrength); - } - - static void carveOrganicColumns(WorldGenLevel world, BoundingBox area, OrganicCarve carve) { - StructureCarvingFootprint footprint = carve.footprint(); - int minX = Math.max(area.minX(), footprint.minX()); - int maxX = Math.min(area.maxX(), footprint.maxX()); - int minZ = Math.max(area.minZ(), footprint.minZ()); - int maxZ = Math.min(area.maxZ(), footprint.maxZ()); - if (minX > maxX || minZ > maxZ) { - return; - } - boolean eroded = carve.blob() != null; - BlockState air = Blocks.AIR.defaultBlockState(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - int index = footprint.indexAt(x, z); - long horizontalDistanceSquared = footprint.distanceSquaredAt(index); - double sideReach = StructureCarveEnvelope.lobedSideReach(carve.lobe(), - carve.lobeFrequency(), carve.lobeStrength(), x, z, carve.horizontalPadding()); - double sideReachSquared = sideReach * sideReach; - if (horizontalDistanceSquared > sideReachSquared) { - continue; - } - double normalizedHorizontal = sideReachSquared == 0D - ? 0D : horizontalDistanceSquared / sideReachSquared; - int sourceMinY = footprint.sourceMinYAt(index); - int sourceMaxY = footprint.sourceMaxYAt(index); - double upReach = eroded - ? StructureCarveEnvelope.lobedUpReach(carve.lobe(), carve.lobeFrequency(), - carve.lobeStrength(), x, z, - StructureCarveEnvelope.erodedUpReach(carve.ceilingRoll(), - carve.frequency(), carve.strength(), x, z, - carve.ceilingPadding())) - : Math.max(1D, carve.ceilingPadding()); - double floorReach = eroded - ? StructureCarveEnvelope.erodedDownReach(carve.floorRoll(), carve.frequency(), - carve.strength(), x, z, carve.floorPadding()) - : carve.floorPadding(); - int columnMinY = Math.max(area.minY(), sourceMinY - (int) Math.floor(floorReach)); - int columnMaxY = Math.min(area.maxY(), - sourceMaxY + (eroded ? (int) Math.ceil(upReach) : carve.ceilingPadding())); - double downReach = Math.max(1D, floorReach); - for (int y = columnMinY; y <= columnMaxY; y++) { - double normalizedVertical = StructureCarveEnvelope.normalizedVerticalDistance( - y, sourceMinY, sourceMaxY, upReach, downReach); - double distanceSquared = normalizedHorizontal - + normalizedVertical * normalizedVertical; - if (distanceSquared > 1D) { - continue; - } - if (distanceSquared > 0D && eroded) { - double noise = carve.blob().fitDouble(0D, 1D, - x * carve.frequency(), y * carve.frequency(), z * carve.frequency()); - if (!StructureCarveEnvelope.shouldCarveOverboreCell( - carve.shape(), distanceSquared, noise, carve.strength())) { - continue; - } - } - world.setBlock(position.set(x, y, z), air, 2); - } - } - } - } - - private static boolean emitCarveColumns(StructureStart start, - Supplier templates, - StructureCarvingFootprint.ColumnSink sink) { - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || !emitTemplateColumns(pieceTemplates(poolPiece, templates), - poolPiece.getPosition(), poolPiece.getRotation(), bounds, sink)) { - emitBoxColumns(bounds, sink); - } - } - return true; - } - - /** - * Derives per-column vertical extents from the complement of the template's air and structure-void - * cells, so a carve tracks the actual silhouette instead of the piece's rectangular bounding box. - */ - static boolean emitTemplateColumns(List templates, BlockPos position, - Rotation rotation, BoundingBox bounds, - StructureCarvingFootprint.ColumnSink sink) { - if (templates.isEmpty()) { - return false; - } - int width = bounds.getXSpan(); - int depth = bounds.getZSpan(); - long cells = (long) width * depth * bounds.getYSpan(); - if (cells < 1L || cells > MAX_TEMPLATE_OCCUPANCY_CELLS) { - return false; - } - StructurePlaceSettings settings = new StructurePlaceSettings().setRotation(rotation); - boolean[] voidCells = null; - for (StructureTemplate template : templates) { - boolean[] templateVoid = new boolean[(int) cells]; - for (Block ignored : TEMPLATE_VOID_BLOCKS) { - for (StructureTemplate.StructureBlockInfo info - : template.filterBlocks(position, settings, ignored)) { - BlockPos voidPosition = info.pos(); - if (bounds.isInside(voidPosition)) { - templateVoid[templateCellIndex(bounds, width, depth, - voidPosition.getX(), voidPosition.getY(), voidPosition.getZ())] = true; - } - } - } - if (voidCells == null) { - voidCells = templateVoid; - continue; - } - for (int cell = 0; cell < voidCells.length; cell++) { - voidCells[cell] &= templateVoid[cell]; - } - } - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - int minY = Integer.MAX_VALUE; - int maxY = Integer.MIN_VALUE; - for (int y = bounds.minY(); y <= bounds.maxY(); y++) { - if (voidCells[templateCellIndex(bounds, width, depth, x, y, z)]) { - continue; - } - if (minY == Integer.MAX_VALUE) { - minY = y; - } - maxY = y; - } - if (minY != Integer.MAX_VALUE) { - sink.column(x, z, minY, maxY); - } - } - } - return true; - } - - private static void emitBoxColumns(BoundingBox bounds, StructureCarvingFootprint.ColumnSink sink) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - sink.column(x, z, bounds.minY(), bounds.maxY()); - } - } - } - - private static int templateCellIndex(BoundingBox bounds, int width, int depth, - int x, int y, int z) { - return ((y - bounds.minY()) * depth + z - bounds.minZ()) * width + x - bounds.minX(); - } - - private static List pieceTemplates(PoolElementStructurePiece poolPiece, - Supplier templates) { - List resolved = new ArrayList<>(1); - collectElementTemplates(poolPiece.getElement(), templates, resolved); - return resolved; - } - - private static void collectElementTemplates(StructurePoolElement element, - Supplier templates, - List resolved) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - collectElementTemplates(child, templates, resolved); - } - return; - } - if (element instanceof SinglePoolElement singleElement) { - resolved.add(NativeStructureReflection.resolveTemplate(singleElement, templates)); - } - } - - private static long carveNoiseIdentity(WorldGenLevel world, String structureId, - StructureStart start) { - return world.getSeed() - ^ ((long) start.getChunkPos().x() * 341873128712L) - ^ ((long) start.getChunkPos().z() * 132897987541L) - ^ (structureId == null ? 0 : structureId.hashCode()); - } - - private static void encasePieces(WorldGenLevel world, BoundingBox area, String structureId, - StructureStart start, IrisStructureTerrain terrain, - NativeStructurePostProcessor.PaletteBlockResolver paletteBlockResolver) { - IrisMaterialPalette palette = terrain.getEncasePalette(); - RNG rng = null; - if (palette != null) { - Objects.requireNonNull(paletteBlockResolver, - "Native structure encase palette requires a platform block resolver"); - rng = new RNG(world.getSeed() ^ (structureId == null ? 0 : structureId.hashCode())); - } - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (BoundingBox bounds : contentPieceBounds(start)) { - BoundingBox shell = paddedPieceArea(area, bounds, terrain); - if (shell == null) { - continue; - } - for (int x = shell.minX(); x <= shell.maxX(); x++) { - for (int z = shell.minZ(); z <= shell.maxZ(); z++) { - for (int y = shell.minY(); y <= shell.maxY(); y++) { - BlockState existing = world.getBlockState(position.set(x, y, z)); - if (!isEncaseable(existing)) { - continue; - } - BlockState fill = palette == null - ? defaultEncaseBlock(world, y) - : Objects.requireNonNull( - paletteBlockResolver.resolve(palette, rng, x, y, z), - "Encase palette returned no block for " + structureId + " at " - + x + "," + y + "," + z); - world.setBlock(position, fill, 2); - } - } - } - } - } - - static boolean isEncaseable(BlockState state) { - return state.isAir() || !state.getFluidState().isEmpty(); - } - - static BlockState defaultEncaseBlock(int y) { - return y < 0 ? Blocks.DEEPSLATE.defaultBlockState() : Blocks.STONE.defaultBlockState(); - } - - static BlockState defaultEncaseBlock(WorldGenLevel world, int y) { - ServerLevel level = world == null ? null : world.getLevel(); - if (level == null) { - return defaultEncaseBlock(y); - } - Holder dimensionType = level.dimensionTypeRegistration(); - ResourceKey dimensionTypeKey = dimensionType.unwrapKey().orElse(null); - return defaultEncaseBlock(level.dimension(), dimensionTypeKey, dimensionType.value(), y); - } - - static BlockState defaultEncaseBlock(ResourceKey dimension, int y) { - return defaultEncaseBlock(dimension, null, null, y); - } - - static BlockState defaultEncaseBlock(ResourceKey dimension, - ResourceKey dimensionTypeKey, - DimensionType dimensionType, int y) { - if (Level.NETHER.equals(dimension)) { - return Blocks.NETHERRACK.defaultBlockState(); - } - if (Level.END.equals(dimension)) { - return Blocks.END_STONE.defaultBlockState(); - } - if (BuiltinDimensionTypes.NETHER.equals(dimensionTypeKey) - || dimensionType != null && dimensionType.hasCeiling() && !dimensionType.hasSkyLight()) { - return Blocks.NETHERRACK.defaultBlockState(); - } - if (BuiltinDimensionTypes.END.equals(dimensionTypeKey) - || dimensionType != null && (dimensionType.hasEnderDragonFight() - || dimensionType.hasEndFlashes())) { - return Blocks.END_STONE.defaultBlockState(); - } - return defaultEncaseBlock(y); - } - - private static void carvePieceBoxes(WorldGenLevel world, BoundingBox area, StructureStart start, - IrisStructureTerrain terrain) { - BlockState air = Blocks.AIR.defaultBlockState(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (BoundingBox bounds : contentPieceBounds(start)) { - BoundingBox carve = paddedPieceArea(area, bounds, terrain); - if (carve == null) { - continue; - } - for (int x = carve.minX(); x <= carve.maxX(); x++) { - for (int z = carve.minZ(); z <= carve.maxZ(); z++) { - for (int y = carve.minY(); y <= carve.maxY(); y++) { - world.setBlock(position.set(x, y, z), air, 2); - } - } - } - } - } - - private static BoundingBox paddedPieceArea(BoundingBox area, BoundingBox bounds, - IrisStructureTerrain terrain) { - int horizontalPadding = Math.max(0, terrain.getHorizontalPadding()); - int minX = Math.max(area.minX(), bounds.minX() - horizontalPadding); - int minY = Math.max(area.minY(), bounds.minY() - Math.max(0, terrain.getFloorPadding())); - int minZ = Math.max(area.minZ(), bounds.minZ() - horizontalPadding); - int maxX = Math.min(area.maxX(), bounds.maxX() + horizontalPadding); - int maxY = Math.min(area.maxY(), bounds.maxY() + Math.max(0, terrain.getCeilingPadding())); - int maxZ = Math.min(area.maxZ(), bounds.maxZ() + horizontalPadding); - if (minX > maxX || minY > maxY || minZ > maxZ) { - return null; - } - return new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ); - } - - static void clearLegacyTemplateAir(WorldGenLevel world, BoundingBox area, - StructureStart start, - Supplier templates) { - for (StructurePiece piece : start.getPieces()) { - if (!(piece instanceof PoolElementStructurePiece poolPiece) - || poolPiece.getElement().getProjection() != StructureTemplatePool.Projection.RIGID - || !intersects(poolPiece.getBoundingBox(), area)) { - continue; - } - int groundY = poolPiece.getBoundingBox().minY() + poolPiece.getGroundLevelDelta(); - StructurePlaceSettings settings = new StructurePlaceSettings() - .setRotation(poolPiece.getRotation()) - .setBoundingBox(area); - clearLegacyTemplateAir(world, poolPiece.getElement(), poolPiece.getPosition(), - groundY, settings, templates); - } - } - - private static void clearLegacyTemplateAir(WorldGenLevel world, StructurePoolElement element, - BlockPos position, int groundY, - StructurePlaceSettings settings, - Supplier templates) { - if (element instanceof ListPoolElement listElement) { - for (StructurePoolElement child : listElement.getElements()) { - clearLegacyTemplateAir(world, child, position, groundY, settings, templates); - } - return; - } - if (!(element instanceof LegacySinglePoolElement legacyElement)) { - return; - } - StructureTemplate template = NativeStructureReflection.resolveTemplate(legacyElement, templates); - clearTemplateAir(world, template, position, groundY, settings); - } - - static void clearTemplateAir(WorldGenLevel world, StructureTemplate template, - BlockPos position, int groundY, - StructurePlaceSettings settings) { - List airBlocks = template.filterBlocks( - position, settings, Blocks.AIR); - BlockState air = Blocks.AIR.defaultBlockState(); - for (StructureTemplate.StructureBlockInfo airBlock : airBlocks) { - BlockPos airPosition = airBlock.pos(); - BlockState existingState = world.getBlockState(airPosition); - if (shouldClearLegacyAir( - airPosition.getY(), groundY, existingState.isAir()) - && !NativeStructureVegetationClearer.isTreeBlock(existingState)) { - world.setBlock(airPosition, air, 2); - } - } - } - - static boolean shouldClearLegacyAir(int airY, int groundY, boolean existingAir) { - return airY >= groundY && !existingAir; - } - - static boolean intersects(BoundingBox first, BoundingBox second) { - return first.maxX() >= second.minX() && first.minX() <= second.maxX() - && first.maxY() >= second.minY() && first.minY() <= second.maxY() - && first.maxZ() >= second.minZ() && first.minZ() <= second.maxZ(); - } - - // StructureStart has no value equality, so the key pins the exact start instance a chunk carves against. - private record CarveFootprintKey(StructureStart start, int padding) { - } - - private record CachedCarveFootprint(StructureCarvingFootprint footprint, int cells) { - } - - private record JunctionAnchor(int x, int y, int z) { - } - - static final class SourceTerrainSnapshot { - private final BoundingBox area; - private final int width; - private final BlockState[][] statesByLayer; - private final int sampledCells; - - private SourceTerrainSnapshot(BoundingBox area, int width, - BlockState[][] statesByLayer, int sampledCells) { - this.area = area; - this.width = width; - this.statesByLayer = statesByLayer; - this.sampledCells = sampledCells; - } - - static SourceTerrainSnapshot capture( - WorldGenLevel world, BoundingBox area, BitSet requiredLayers) { - Objects.requireNonNull(world, "Source terrain snapshot requires a generation level"); - Objects.requireNonNull(area, "Source terrain snapshot requires writable bounds"); - Objects.requireNonNull(requiredLayers, "Source terrain snapshot requires sampled layers"); - int width = area.getXSpan(); - int depth = area.getZSpan(); - int height = area.getYSpan(); - int horizontalCells = Math.multiplyExact(width, depth); - BlockState[][] statesByLayer = new BlockState[height][]; - int sampledCells = 0; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (int layer = requiredLayers.nextSetBit(0); - layer >= 0; layer = requiredLayers.nextSetBit(layer + 1)) { - if (layer >= height) { - throw new IllegalArgumentException( - "Source terrain snapshot layer exceeds writable bounds: " + layer); - } - BlockState[] states = new BlockState[horizontalCells]; - statesByLayer[layer] = states; - sampledCells = Math.addExact(sampledCells, horizontalCells); - int y = Math.addExact(area.minY(), layer); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int index = (z - area.minZ()) * width + x - area.minX(); - states[index] = sourceTerrainBlock( - world.getBlockState(position.set(x, y, z))); - } - } - } - return new SourceTerrainSnapshot(new BoundingBox( - area.minX(), area.minY(), area.minZ(), - area.maxX(), area.maxY(), area.maxZ()), width, - statesByLayer, sampledCells); - } - - BlockState stateAt(BlockPos position) { - if (!area.isInside(position)) { - return null; - } - BlockState[] states = statesByLayer[position.getY() - area.minY()]; - if (states == null) { - return null; - } - int index = (position.getZ() - area.minZ()) * width - + position.getX() - area.minX(); - return states[index]; - } - - int sampledCells() { - return sampledCells; - } - } - - record OrganicCarve(StructureCarvingFootprint footprint, IrisStructureCarveShape shape, - int horizontalPadding, int ceilingPadding, int floorPadding, - double strength, double frequency, CNG blob, CNG ceilingRoll, CNG floorRoll, - CNG lobe, double lobeFrequency, double lobeStrength) { - } - - public record TerrainTarget(String structureId, StructureStart start, - IrisStructureTerrain terrain) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVanillaLocator.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVanillaLocator.java deleted file mode 100644 index b04eaf2f1..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVanillaLocator.java +++ /dev/null @@ -1,224 +0,0 @@ -package art.arcane.iris.nativegen; - -import com.mojang.datafixers.util.Pair; -import net.minecraft.SharedConstants; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.SectionPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureCheckResult; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -public final class NativeStructureVanillaLocator { - private NativeStructureVanillaLocator() { - } - - public static Candidate predict(ServerLevel level, HolderSet holders, - BlockPos origin, int radius, boolean findUnexplored) { - if (SharedConstants.DEBUG_DISABLE_FEATURES - || !level.getServer().getWorldGenSettings().options().generateStructures()) { - return null; - } - ChunkGeneratorStructureState state = level.getChunkSource().getGeneratorState(); - Map>> byPlacement = new LinkedHashMap<>(); - for (Holder holder : holders) { - for (StructurePlacement placement : state.getPlacementsForStructure(holder)) { - byPlacement.computeIfAbsent(placement, ignored -> new LinkedHashSet<>()).add(holder); - } - } - if (byPlacement.isEmpty()) { - return null; - } - - StructureManager structureManager = level.structureManager(); - Candidate best = null; - double bestDistance = Double.MAX_VALUE; - List>>> randomPlacements = - new ArrayList<>(byPlacement.size()); - for (Map.Entry>> entry : byPlacement.entrySet()) { - StructurePlacement placement = entry.getKey(); - if (placement instanceof ConcentricRingsStructurePlacement concentric) { - Candidate candidate = predictConcentric( - entry.getValue(), level, structureManager, origin, findUnexplored, - state, concentric); - if (candidate != null) { - double distance = origin.distSqr(candidate.result().getFirst()); - if (distance < bestDistance) { - best = candidate; - bestDistance = distance; - } - } - } else if (placement instanceof RandomSpreadStructurePlacement randomSpread) { - randomPlacements.add(Map.entry(randomSpread, entry.getValue())); - } - } - - int centerChunkX = SectionPos.blockToSectionCoord(origin.getX()); - int centerChunkZ = SectionPos.blockToSectionCoord(origin.getZ()); - int searchRadius = Math.max(0, radius); - for (int ring = 0; ring <= searchRadius; ring++) { - boolean foundInRing = false; - for (Map.Entry>> entry - : randomPlacements) { - Candidate candidate = predictRandomSpread( - entry.getValue(), level, structureManager, - centerChunkX, centerChunkZ, ring, findUnexplored, - state.getLevelSeed(), entry.getKey()); - if (candidate == null) { - continue; - } - foundInRing = true; - double distance = origin.distSqr(candidate.result().getFirst()); - if (distance < bestDistance) { - best = candidate; - bestDistance = distance; - } - } - if (foundInRing) { - return best; - } - } - return best; - } - - private static Candidate predictConcentric(Set> holders, - ServerLevel level, - StructureManager structureManager, - BlockPos origin, - boolean findUnexplored, - ChunkGeneratorStructureState state, - ConcentricRingsStructurePlacement placement) { - List positions = state.getRingPositionsFor(placement); - if (positions == null) { - throw new IllegalStateException( - "Tried to locate structures for an unavailable concentric placement"); - } - Candidate best = null; - double bestDistance = Double.MAX_VALUE; - for (ChunkPos position : positions) { - BlockPos locatePosition = new BlockPos( - SectionPos.sectionToBlockCoord(position.x(), 8), - 32, - SectionPos.sectionToBlockCoord(position.z(), 8)); - double distance = locatePosition.distSqr(origin); - if (best != null && distance >= bestDistance) { - continue; - } - Candidate candidate = predictAt( - holders, level, structureManager, findUnexplored, placement, position); - if (candidate != null) { - best = candidate; - bestDistance = distance; - } - } - return best; - } - - private static Candidate predictRandomSpread(Set> holders, - ServerLevel level, - StructureManager structureManager, - int centerChunkX, int centerChunkZ, - int ring, boolean findUnexplored, - long seed, - RandomSpreadStructurePlacement placement) { - int spacing = placement.spacing(); - for (int offsetX = -ring; offsetX <= ring; offsetX++) { - boolean edgeX = offsetX == -ring || offsetX == ring; - for (int offsetZ = -ring; offsetZ <= ring; offsetZ++) { - boolean edgeZ = offsetZ == -ring || offsetZ == ring; - if (!edgeX && !edgeZ) { - continue; - } - int gridChunkX = centerChunkX + spacing * offsetX; - int gridChunkZ = centerChunkZ + spacing * offsetZ; - ChunkPos candidatePosition = placement.getPotentialStructureChunk( - seed, gridChunkX, gridChunkZ); - Candidate candidate = predictAt( - holders, level, structureManager, - findUnexplored, placement, candidatePosition); - if (candidate != null) { - return candidate; - } - } - } - return null; - } - - private static Candidate predictAt(Set> holders, - ServerLevel level, - StructureManager structureManager, - boolean findUnexplored, - StructurePlacement placement, - ChunkPos candidatePosition) { - for (Holder holder : holders) { - Structure structure = holder.value(); - StructureCheckResult result = structureManager.checkStructurePresence( - candidatePosition, structure, placement, findUnexplored); - if (result == StructureCheckResult.START_NOT_PRESENT) { - continue; - } - if (!findUnexplored && result == StructureCheckResult.START_PRESENT) { - return new Candidate( - Pair.of(placement.getLocatePos(candidatePosition), holder), null); - } - ChunkAccess chunk = level.getChunk( - candidatePosition.x(), candidatePosition.z(), ChunkStatus.STRUCTURE_STARTS); - StructureStart start = structureManager.getStartForStructure( - SectionPos.bottomOf(chunk), structure, chunk); - if (start == null || !start.isValid() - || findUnexplored && !start.canBeReferenced()) { - continue; - } - return new Candidate( - Pair.of(placement.getLocatePos(start.getChunkPos()), holder), - findUnexplored ? start : null); - } - return null; - } - - public static final class Candidate { - private final Pair> result; - private final StructureStart referenceStart; - private final AtomicBoolean committed; - - private Candidate(Pair> result, - StructureStart referenceStart) { - this.result = result; - this.referenceStart = referenceStart; - this.committed = new AtomicBoolean(); - } - - public Pair> result() { - return result; - } - - public boolean reference(StructureManager structureManager) { - if (referenceStart == null || !committed.compareAndSet(false, true)) { - return false; - } - if (!referenceStart.canBeReferenced()) { - return false; - } - structureManager.addReference(referenceStart); - return true; - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVegetationClearer.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVegetationClearer.java deleted file mode 100644 index 88674aacf..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVegetationClearer.java +++ /dev/null @@ -1,174 +0,0 @@ -package art.arcane.iris.nativegen; - -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.Arrays; -import java.util.BitSet; -import java.util.List; - -public final class NativeStructureVegetationClearer { - private NativeStructureVegetationClearer() { - } - - public static boolean isUndergroundStep(GenerationStep.Decoration step) { - return step == GenerationStep.Decoration.UNDERGROUND_STRUCTURES - || step == GenerationStep.Decoration.UNDERGROUND_DECORATION - || step == GenerationStep.Decoration.STRONGHOLDS; - } - - public static void clearIntersectingVegetation(WorldGenLevel world, ChunkAccess chunk, BoundingBox area, - List starts) { - if (starts == null || starts.isEmpty()) { - return; - } - VegetationSnapshot snapshot = captureVegetation(chunk, area); - if (snapshot.treeBlockCount() == 0) { - return; - } - int columns = area.getXSpan() * area.getZSpan(); - int[] clearMinY = new int[columns]; - int[] clearMaxY = new int[columns]; - Arrays.fill(clearMinY, Integer.MAX_VALUE); - Arrays.fill(clearMaxY, Integer.MIN_VALUE); - for (StructureStart start : starts) { - if (shouldProcessStart(start)) { - markVegetationColumns(area, snapshot, start, clearMinY, clearMaxY); - } - } - clearVegetationColumns(world, area, snapshot, clearMinY, clearMaxY); - } - - static boolean shouldProcessStart(StructureStart start) { - return start != null && start.isValid(); - } - - private static VegetationSnapshot captureVegetation(ChunkAccess chunk, BoundingBox area) { - int width = area.getXSpan(); - int depth = area.getZSpan(); - BitSet[] columns = new BitSet[width * depth]; - int treeBlockCount = 0; - LevelChunkSection[] sections = chunk.getSections(); - int chunkMinX = chunk.getPos().getMinBlockX(); - int chunkMinZ = chunk.getPos().getMinBlockZ(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - int sectionMinY = chunk.getSectionYFromSectionIndex(sectionIndex) << 4; - int minY = Math.max(area.minY(), sectionMinY); - int maxY = Math.min(area.maxY(), sectionMinY + 15); - if (minY > maxY || section.hasOnlyAir() - || !section.maybeHas(NativeStructureVegetationClearer::isTreeBlock)) { - continue; - } - for (int y = minY; y <= maxY; y++) { - int localY = y - sectionMinY; - for (int z = area.minZ(); z <= area.maxZ(); z++) { - int localZ = z - chunkMinZ; - for (int x = area.minX(); x <= area.maxX(); x++) { - int localX = x - chunkMinX; - if (!isTreeBlock(section.getBlockState(localX, localY, localZ))) { - continue; - } - int column = (z - area.minZ()) * width + x - area.minX(); - BitSet treeBlocks = columns[column]; - if (treeBlocks == null) { - treeBlocks = new BitSet(area.getYSpan()); - columns[column] = treeBlocks; - } - treeBlocks.set(y - area.minY()); - treeBlockCount++; - } - } - } - } - return new VegetationSnapshot(columns, treeBlockCount); - } - - private static void markVegetationColumns(BoundingBox area, VegetationSnapshot snapshot, - StructureStart start, int[] clearMinY, int[] clearMaxY) { - int width = area.getXSpan(); - int[] pieceTops = new int[clearMinY.length]; - int[] pieceBottoms = new int[clearMinY.length]; - Arrays.fill(pieceTops, Integer.MIN_VALUE); - Arrays.fill(pieceBottoms, Integer.MAX_VALUE); - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - int minX = Math.max(area.minX(), bounds.minX()); - int maxX = Math.min(area.maxX(), bounds.maxX()); - int minZ = Math.max(area.minZ(), bounds.minZ()); - int maxZ = Math.min(area.maxZ(), bounds.maxZ()); - if (minX > maxX || minZ > maxZ) { - continue; - } - for (int z = minZ; z <= maxZ; z++) { - for (int x = minX; x <= maxX; x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - pieceTops[column] = Math.max(pieceTops[column], bounds.maxY()); - pieceBottoms[column] = Math.min(pieceBottoms[column], bounds.minY()); - } - } - } - for (int column = 0; column < pieceTops.length; column++) { - if (snapshot.columns()[column] == null || pieceTops[column] == Integer.MIN_VALUE) { - continue; - } - clearMinY[column] = Math.min(clearMinY[column], pieceBottoms[column] - 1); - clearMaxY[column] = Math.max(clearMaxY[column], pieceTops[column]); - } - } - - private static void clearVegetationColumns(WorldGenLevel world, BoundingBox area, - VegetationSnapshot snapshot, int[] clearMinY, int[] clearMaxY) { - int width = area.getXSpan(); - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - BlockState air = Blocks.AIR.defaultBlockState(); - for (int z = area.minZ(); z <= area.maxZ(); z++) { - for (int x = area.minX(); x <= area.maxX(); x++) { - int column = (z - area.minZ()) * width + x - area.minX(); - BitSet treeBlocks = snapshot.columns()[column]; - if (treeBlocks == null || clearMaxY[column] < clearMinY[column]) { - continue; - } - int firstBit = Math.max(0, clearMinY[column] - area.minY()); - int lastBit = Math.min(area.getYSpan() - 1, clearMaxY[column] - area.minY()); - if (firstBit > lastBit) { - continue; - } - for (int bit = treeBlocks.nextSetBit(firstBit); bit >= 0 && bit <= lastBit; - bit = treeBlocks.nextSetBit(bit + 1)) { - int y = area.minY() + bit; - position.set(x, y, z); - BlockState state = world.getBlockState(position); - if (isTreeBlock(state)) { - world.setBlock(position, air, 2); - } - } - } - } - } - - static boolean isTreeBlock(BlockState state) { - if (state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES)) { - return true; - } - // Field reads instead of a registry byValue map hit — this predicate runs per block - // probe on the hottest native-structure scans. - String path = state.getBlock().builtInRegistryHolder().key().identifier().getPath(); - return path.endsWith("_log") || path.endsWith("_wood") - || path.endsWith("_stem") || path.endsWith("_hyphae") - || path.endsWith("_leaves"); - } - - private record VegetationSnapshot(BitSet[] columns, int treeBlockCount) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVerticalPlacer.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVerticalPlacer.java deleted file mode 100644 index aaf555edc..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVerticalPlacer.java +++ /dev/null @@ -1,384 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.structure.placement.StructureVerticalBounds; -import art.arcane.iris.structure.placement.IrisStructureYBand; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.volmlib.util.math.RNG; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece; -import net.minecraft.world.level.levelgen.structure.ScatteredFeaturePiece; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer; -import net.minecraft.world.level.levelgen.structure.pools.JigsawJunction; -import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidPiece; -import net.minecraft.world.level.levelgen.structure.structures.DesertPyramidStructure; -import net.minecraft.world.level.levelgen.structure.structures.JungleTemplePiece; -import net.minecraft.world.level.levelgen.structure.structures.JungleTempleStructure; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentPieces; -import net.minecraft.world.level.levelgen.structure.structures.OceanMonumentStructure; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.function.IntBinaryOperator; - -public final class NativeStructureVerticalPlacer { - private static final String DESERT_PYRAMID_ID = "minecraft:desert_pyramid"; - private static final String JUNGLE_PYRAMID_ID = "minecraft:jungle_pyramid"; - private static final int MAX_BURIAL_COLUMNS = 2_000_000; - private static final int MONUMENT_BASE_BELOW_SEA_LEVEL = 24; - private static final String OCEAN_MONUMENT_ID = "minecraft:monument"; - private static final int UNDERGROUND_SURFACE_CLEARANCE = 1; - - private NativeStructureVerticalPlacer() { - } - - public static int applyVerticalPlacement(StructureStart start, String structureId, int requestedOffset, - int seaLevel, int worldMinY, int worldMaxYExclusive, - boolean underground, boolean preserveSourceY, - IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - if (isOceanMonument(start, structureId)) { - return alignOceanMonumentToSeaLevel( - start, requestedOffset, seaLevel, worldMinY, worldMaxYExclusive); - } - if (isAdjustedScatteredStructure(start, structureId)) { - return alignScatteredStructureToSurface( - start, structureId, requestedOffset, worldMinY, worldMaxYExclusive, surfaceHeight); - } - return applyVerticalShift(start, requestedOffset, worldMinY, worldMaxYExclusive, - underground, preserveSourceY, yBand, surfaceHeight); - } - - public static StructureStart relocateToMinY(StructureStart start, Structure source, int targetMinY, - LevelHeightAccessor heightAccessor) { - Objects.requireNonNull(start, "Native structure start must not be null"); - Objects.requireNonNull(source, "Native structure source must not be null"); - Objects.requireNonNull(heightAccessor, "Native structure height accessor must not be null"); - if (!start.isValid()) { - return StructureStart.INVALID_START; - } - List pieces = start.getPieces(); - int minY = Integer.MAX_VALUE; - for (StructurePiece piece : pieces) { - minY = Math.min(minY, piece.getBoundingBox().minY()); - } - if (minY == Integer.MAX_VALUE) { - return StructureStart.INVALID_START; - } - int offsetY = Math.subtractExact(targetMinY, minY); - if (offsetY != 0) { - for (StructurePiece piece : pieces) { - moveStructurePiece(piece, offsetY); - } - } - int worldMinY = heightAccessor.getMinY() + 1; - int worldMaxYExclusive = Math.addExact( - heightAccessor.getMinY(), heightAccessor.getHeight()); - for (StructurePiece piece : pieces) { - BoundingBox bounds = piece.getBoundingBox(); - if (bounds.minY() < worldMinY || bounds.maxY() >= worldMaxYExclusive) { - throw new IllegalStateException("Native structure cannot fit target minimum Y " - + targetMinY + " inside world bounds [" + worldMinY + "," - + worldMaxYExclusive + ")"); - } - } - return new StructureStart( - source, - start.getChunkPos(), - start.getReferences(), - new PiecesContainer(List.copyOf(pieces)) - ); - } - - static int alignScatteredStructureToSurface(StructureStart start, String structureId, - int configuredOffset, int worldMinY, - int worldMaxYExclusive, - IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(surfaceHeight, "Scattered native structure requires a terrain height resolver"); - ScatteredFeaturePiece piece = requireAdjustedScatteredPiece(start, structureId); - BoundingBox bounds = start.getBoundingBox(); - BoundingBox pieceBounds = piece.getBoundingBox(); - int surfaceY = representativeScatteredSurfaceY(structureId, pieceBounds, surfaceHeight); - int targetMinY = Math.addExact(Math.addExact(surfaceY, 1), configuredOffset); - int requestedMove = Math.subtractExact(targetMinY, pieceBounds.minY()); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), requestedMove, worldMinY, worldMaxYExclusive); - if (offsetY != 0) { - moveStructureStart(start, bounds, offsetY); - } - setScatteredHeightPosition(piece, Math.max(0, piece.getBoundingBox().minY())); - return offsetY; - } - - public static int applyVerticalShift(StructureStart start, int requestedOffset, int worldMinY, - int worldMaxYExclusive, boolean underground, - boolean preserveSourceY, IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - BoundingBox bounds = start.getBoundingBox(); - int resolvedOffset = resolveShiftOffset(start, bounds, requestedOffset, worldMinY, - worldMaxYExclusive, underground, preserveSourceY, yBand, surfaceHeight); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), resolvedOffset, worldMinY, worldMaxYExclusive); - if (offsetY == 0) { - return 0; - } - moveStructureStart(start, bounds, offsetY); - return offsetY; - } - - private static int resolveShiftOffset(StructureStart start, BoundingBox bounds, int requestedOffset, - int worldMinY, int worldMaxYExclusive, boolean underground, - boolean preserveSourceY, IrisStructureYBand yBand, - IntBinaryOperator surfaceHeight) { - if (preserveSourceY) { - return requestedOffset; - } - if (yBand != null) { - return resolveYBandOffset(bounds, yBand, start.getChunkPos()); - } - if (underground) { - return resolveBuriedOffset( - bounds, requestedOffset, worldMinY, worldMaxYExclusive, surfaceHeight); - } - return requestedOffset; - } - - static int alignOceanMonumentToSeaLevel(StructureStart start, int configuredOffset, int seaLevel, - int worldMinY, int worldMaxYExclusive) { - OceanMonumentPieces.MonumentBuilding building = requireOceanMonumentBuilding(start); - BoundingBox bounds = start.getBoundingBox(); - int targetMinY = Math.addExact( - Math.subtractExact(seaLevel, MONUMENT_BASE_BELOW_SEA_LEVEL), configuredOffset); - int requestedOffset = Math.subtractExact(targetMinY, building.getBoundingBox().minY()); - int offsetY = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), requestedOffset, worldMinY, worldMaxYExclusive); - if (offsetY != requestedOffset) { - throw new IllegalStateException("Ocean monument cannot align to sea level " + seaLevel - + " with configured offset " + configuredOffset + " inside world bounds [" - + worldMinY + "," + worldMaxYExclusive + ")"); - } - if (offsetY == 0) { - return 0; - } - moveStructureStart(start, bounds, offsetY); - return offsetY; - } - - static void ensureMonumentSeaLevelAlignment(StructureStart start, String structureId, - int configuredOffset, int seaLevel, - int worldMinY, int worldMaxYExclusive) { - if (!isOceanMonument(start, structureId)) { - return; - } - synchronized (start) { - alignOceanMonumentToSeaLevel( - start, configuredOffset, seaLevel, worldMinY, worldMaxYExclusive); - } - } - - private static boolean isOceanMonument(StructureStart start, String structureId) { - return OCEAN_MONUMENT_ID.equals(structureId) - && start != null - && start.getStructure() instanceof OceanMonumentStructure; - } - - private static boolean isAdjustedScatteredStructure(StructureStart start, String structureId) { - if (start == null) { - return false; - } - Structure source = start.getStructure(); - return (DESERT_PYRAMID_ID.equals(structureId) && source instanceof DesertPyramidStructure) - || (JUNGLE_PYRAMID_ID.equals(structureId) && source instanceof JungleTempleStructure); - } - - private static ScatteredFeaturePiece requireAdjustedScatteredPiece(StructureStart start, - String structureId) { - Objects.requireNonNull(start, "Scattered native structure start must not be null"); - List pieces = start.getPieces(); - if (pieces.size() != 1) { - throw new IllegalStateException(structureId + " must contain exactly one scattered piece, found " - + pieces.size()); - } - StructurePiece piece = pieces.get(0); - if (DESERT_PYRAMID_ID.equals(structureId) && piece instanceof DesertPyramidPiece desertPyramid) { - return desertPyramid; - } - if (JUNGLE_PYRAMID_ID.equals(structureId) && piece instanceof JungleTemplePiece jungleTemple) { - return jungleTemple; - } - throw new IllegalStateException(structureId + " contains unexpected piece " - + piece.getClass().getName()); - } - - private static int representativeScatteredSurfaceY(String structureId, BoundingBox bounds, - IntBinaryOperator surfaceHeight) { - if (DESERT_PYRAMID_ID.equals(structureId)) { - return lowestSurfaceY(bounds, surfaceHeight); - } - if (JUNGLE_PYRAMID_ID.equals(structureId)) { - return averageSurfaceY(bounds, surfaceHeight); - } - throw new IllegalStateException("Unsupported scattered native structure " + structureId); - } - - private static int lowestSurfaceY(BoundingBox bounds, IntBinaryOperator surfaceHeight) { - int lowestY = Integer.MAX_VALUE; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - lowestY = Math.min(lowestY, surfaceHeight.applyAsInt(x, z)); - } - } - if (lowestY == Integer.MAX_VALUE) { - throw new IllegalStateException("Scattered native structure has an empty terrain footprint"); - } - return lowestY; - } - - private static int averageSurfaceY(BoundingBox bounds, IntBinaryOperator surfaceHeight) { - long totalY = 0L; - long columns = 0L; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - totalY += surfaceHeight.applyAsInt(x, z); - columns++; - } - } - if (columns == 0L) { - throw new IllegalStateException("Scattered native structure has an empty terrain footprint"); - } - return Math.toIntExact(totalY / columns); - } - - private static void setScatteredHeightPosition(ScatteredFeaturePiece piece, int heightPosition) { - try { - NativeStructureReflection.ScatteredHeightPositionAccess.FIELD.setInt(piece, heightPosition); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot lock scattered native structure height", error); - } - } - - private static OceanMonumentPieces.MonumentBuilding requireOceanMonumentBuilding(StructureStart start) { - Objects.requireNonNull(start, "Ocean monument start must not be null"); - List pieces = start.getPieces(); - OceanMonumentPieces.MonumentBuilding building = null; - for (StructurePiece piece : pieces) { - if (piece instanceof OceanMonumentPieces.MonumentBuilding monumentBuilding) { - building = monumentBuilding; - } - } - if (pieces.size() != 1 || building == null) { - throw new IllegalStateException("minecraft:monument must contain exactly one MonumentBuilding, found " - + pieces.size() + " pieces"); - } - return building; - } - - private static void moveStructureStart(StructureStart start, BoundingBox cachedBounds, int offsetY) { - for (StructurePiece piece : start.getPieces()) { - moveStructurePiece(piece, offsetY); - } - cachedBounds.move(0, offsetY, 0); - } - - private static void moveStructurePiece(StructurePiece piece, int offsetY) { - piece.move(0, offsetY, 0); - if (piece instanceof OceanMonumentPieces.MonumentBuilding building) { - for (StructurePiece child : monumentChildPieces(building)) { - child.move(0, offsetY, 0); - } - } - if (piece instanceof PoolElementStructurePiece poolPiece) { - List junctions = poolPiece.getJunctions(); - for (int i = 0; i < junctions.size(); i++) { - JigsawJunction junction = junctions.get(i); - junctions.set(i, new JigsawJunction( - junction.getSourceX(), - junction.getSourceGroundY() + offsetY, - junction.getSourceZ(), - junction.getDeltaY(), - junction.getDestProjection())); - } - } - } - - static List monumentChildPieces(OceanMonumentPieces.MonumentBuilding building) { - Object value; - try { - value = NativeStructureReflection.MonumentChildPiecesAccess.FIELD.get(building); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Cannot read Ocean Monument child pieces", error); - } - if (!(value instanceof List children)) { - throw new IllegalStateException("Ocean Monument child-pieces field is not a list"); - } - List pieces = new ArrayList<>(children.size()); - for (Object child : children) { - if (!(child instanceof StructurePiece monumentPiece) - || child.getClass().getEnclosingClass() != OceanMonumentPieces.class) { - throw new IllegalStateException("Ocean Monument child-pieces list contains " - + (child == null ? "null" : child.getClass().getName())); - } - pieces.add(monumentPiece); - } - return List.copyOf(pieces); - } - - static int resolveYBandOffset(BoundingBox bounds, IrisStructureYBand yBand, ChunkPos startChunk) { - Objects.requireNonNull(bounds, "Native structure bounds must not be null"); - Objects.requireNonNull(startChunk, "Native structure Y band requires a start chunk"); - int target = yBandTargetMidpointY( - bounds.minY(), bounds.maxY(), yBand.resolvedMin(), yBand.resolvedMax(), startChunk); - return Math.subtractExact(target, midpointY(bounds.minY(), bounds.maxY())); - } - - static int yBandTargetMidpointY(int minY, int maxY, int bandMin, int bandMax, ChunkPos startChunk) { - int height = Math.subtractExact(maxY, minY); - int lowest = Math.addExact(bandMin, height / 2); - int highest = Math.subtractExact(bandMax, height - height / 2); - if (lowest > highest) { - // The band is shorter than the structure, so only its midpoint can be honoured. - return Math.floorDiv(Math.addExact(bandMin, bandMax), 2); - } - int span = highest - lowest + 1; - if (span == 1) { - return lowest; - } - long identity = (long) startChunk.x() * 341873128712L ^ (long) startChunk.z() * 132897987541L; - return lowest + new RNG(identity).nextInt(span); - } - - private static int midpointY(int minY, int maxY) { - return minY + (maxY - minY) / 2; - } - - static int resolveBuriedOffset(BoundingBox bounds, int requestedOffset, int worldMinY, - int worldMaxYExclusive, IntBinaryOperator surfaceHeight) { - Objects.requireNonNull(bounds, "Native structure bounds must not be null"); - Objects.requireNonNull(surfaceHeight, "Underground native structure requires a terrain height resolver"); - long width = (long) bounds.maxX() - bounds.minX() + 1L; - long depth = (long) bounds.maxZ() - bounds.minZ() + 1L; - if (width <= 0L || depth <= 0L || width > MAX_BURIAL_COLUMNS / depth) { - throw new IllegalStateException("Underground native structure burial footprint is invalid or exceeds " - + MAX_BURIAL_COLUMNS + " columns"); - } - int maximumOffset = requestedOffset; - for (int x = bounds.minX(); x <= bounds.maxX(); x++) { - for (int z = bounds.minZ(); z <= bounds.maxZ(); z++) { - int allowedTopY = surfaceHeight.applyAsInt(x, z) - UNDERGROUND_SURFACE_CLEARANCE; - maximumOffset = Math.min(maximumOffset, allowedTopY - bounds.maxY()); - } - } - int clampedOffset = StructureVerticalBounds.clampOffset( - bounds.minY(), bounds.maxY(), maximumOffset, worldMinY, worldMaxYExclusive); - if (clampedOffset > maximumOffset) { - IrisLogging.warn("Native structure burial at " + bounds.minX() + "," + bounds.minZ() - + " clamped to world floor: wanted " + maximumOffset + ", used " + clampedOffset); - } - return clampedOffset; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTerrainHeightCache.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTerrainHeightCache.java deleted file mode 100644 index 01a9e6e4b..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTerrainHeightCache.java +++ /dev/null @@ -1,42 +0,0 @@ -package art.arcane.iris.nativegen; - -import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; -import net.minecraft.world.level.levelgen.Heightmap; - -import java.util.OptionalInt; -import java.util.function.Supplier; - -public final class NativeTerrainHeightCache { - private static final int MAXIMUM_CACHED_QUERIES = 65_536; - - private final Object2IntLinkedOpenHashMap heights = new Object2IntLinkedOpenHashMap<>(); - - public OptionalInt resolvedHeight(Query query, Supplier resolver) { - synchronized (heights) { - if (heights.containsKey(query)) { - return OptionalInt.of(heights.getAndMoveToFirst(query)); - } - } - OptionalInt resolved = resolver.get(); - if (resolved.isEmpty()) { - return resolved; - } - int height = resolved.getAsInt(); - synchronized (heights) { - heights.putAndMoveToFirst(query, height); - while (heights.size() > MAXIMUM_CACHED_QUERIES) { - heights.removeLastInt(); - } - } - return resolved; - } - - public void evictRuntime(int runtimeId) { - synchronized (heights) { - heights.keySet().removeIf(query -> query.runtimeId() == runtimeId); - } - } - - public record Query(int runtimeId, int x, int z, Heightmap.Types type, int minimumY, int height) { - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTransitionColumn.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTransitionColumn.java deleted file mode 100644 index d0957441d..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeTransitionColumn.java +++ /dev/null @@ -1,63 +0,0 @@ -package art.arcane.iris.nativegen; - -import art.arcane.iris.world.history.TerrainBoundarySignature; -import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.commands.arguments.blocks.BlockStateParser; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap; - -import java.util.HashMap; -import java.util.Map; - -public final class NativeTransitionColumn { - private NativeTransitionColumn() { - } - - public static NoiseColumn column(TerrainBoundarySignature signature, LevelHeightAccessor height) { - BlockState[] states = new BlockState[height.getHeight()]; - Map palette = new HashMap<>(); - for (int offset = 0; offset < states.length; offset++) { - String state = signature.geometry().voxelAt(height.getMinY() + offset).stateKey(); - states[offset] = palette.computeIfAbsent(state, NativeTransitionColumn::parse); - } - return new NoiseColumn(height.getMinY(), states); - } - - public static int height(TerrainBoundarySignature signature, Heightmap.Types type, LevelHeightAccessor height) { - Map palette = new HashMap<>(); - for (int offset = height.getHeight() - 1; offset >= 0; offset--) { - String state = signature.geometry().voxelAt(height.getMinY() + offset).stateKey(); - if (type.isOpaque().test(palette.computeIfAbsent(state, NativeTransitionColumn::parse))) { - return height.getMinY() + offset + 1; - } - } - return height.getMinY(); - } - - private static BlockState parse(String key) { - String nativeKey = key; - if (!key.startsWith("minecraft:") && IrisPlatforms.isBound()) { - PlatformBlockState logicalState = IrisPlatforms.get().registries().blockOrNull(key); - PlatformBlockState baseState = logicalState == null ? null : logicalState.placementBaseState(); - if (baseState != null) { - nativeKey = baseState.key(); - } - } - StringReader reader = new StringReader(nativeKey); - try { - BlockState state = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK, reader, false).blockState(); - if (reader.canRead()) { - throw new IllegalArgumentException("Unexpected terrain block state suffix: " + reader.getRemaining()); - } - return state; - } catch (CommandSyntaxException failure) { - throw new IllegalArgumentException("Saved terrain block state cannot be resolved: " + key, failure); - } - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/WorldgenTerrainHeightmaps.java b/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/WorldgenTerrainHeightmaps.java deleted file mode 100644 index 6bd3e81e7..000000000 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/WorldgenTerrainHeightmaps.java +++ /dev/null @@ -1,113 +0,0 @@ -package art.arcane.iris.nativegen; - -import net.minecraft.core.SectionPos; -import net.minecraft.util.Mth; -import net.minecraft.util.SimpleBitStorage; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.StructureStart; - -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.IntBinaryOperator; - -/** - * Writes WORLD_SURFACE_WG and OCEAN_FLOOR_WG from Iris engine heights. - * - * Iris writes its terrain straight into LevelChunkSection, so neither vanilla's noise fill nor - * ProtoChunk.setBlockState ever updates the two worldgen heightmaps. Without these writes - * ChunkAccess.getHeight lazily primes the map from finished blocks - Iris trees, leaves and snow - * included - and vanilla pieces that self-snap against WORLD_SURFACE_WG (igloo) or OCEAN_FLOOR_WG - * (ocean ruins, buried treasure, mineshaft interiors) anchor to the canopy instead of the terrain. - * - * Both resolvers take world block coordinates and return the absolute first free Y, matching - * Heightmap's internal "first available" semantics: WORLD_SURFACE_WG counts fluid, OCEAN_FLOOR_WG - * does not. - */ -public final class WorldgenTerrainHeightmaps { - private static final int COLUMNS = 256; - private static final int PLACEMENT_CHUNK_MARGIN = 1; - - private WorldgenTerrainHeightmaps() { - } - - public static void primeTerrain(ChunkAccess chunk, IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY) { - Objects.requireNonNull(chunk, "Iris worldgen heightmap priming requires a chunk"); - Objects.requireNonNull(surfaceFirstFreeY, - "Iris worldgen heightmap priming requires a surface height resolver"); - Objects.requireNonNull(floorFirstFreeY, - "Iris worldgen heightmap priming requires an ocean floor height resolver"); - write(chunk, Heightmap.Types.WORLD_SURFACE_WG, surfaceFirstFreeY); - write(chunk, Heightmap.Types.OCEAN_FLOOR_WG, floorFirstFreeY); - } - - public static void primeStructurePlacement(WorldGenLevel world, ChunkPos generationCenter, - List starts, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY) { - Objects.requireNonNull(world, - "Iris worldgen heightmap priming requires a generation level"); - Objects.requireNonNull(generationCenter, - "Iris worldgen heightmap priming requires a generation center"); - if (starts == null || starts.isEmpty()) { - return; - } - Set primed = new HashSet<>(); - for (StructureStart start : starts) { - if (start == null || !start.isValid()) { - continue; - } - BoundingBox bounds = start.getBoundingBox(); - int minChunkX = SectionPos.blockToSectionCoord(bounds.minX()) - PLACEMENT_CHUNK_MARGIN; - int maxChunkX = SectionPos.blockToSectionCoord(bounds.maxX()) + PLACEMENT_CHUNK_MARGIN; - int minChunkZ = SectionPos.blockToSectionCoord(bounds.minZ()) - PLACEMENT_CHUNK_MARGIN; - int maxChunkZ = SectionPos.blockToSectionCoord(bounds.maxZ()) + PLACEMENT_CHUNK_MARGIN; - for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { - for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { - if (Math.abs(chunkX - generationCenter.x()) > PLACEMENT_CHUNK_MARGIN - || Math.abs(chunkZ - generationCenter.z()) > PLACEMENT_CHUNK_MARGIN) { - continue; - } - if (!primed.add(ChunkPos.pack(chunkX, chunkZ))) { - continue; - } - if (!world.hasChunk(chunkX, chunkZ)) { - continue; - } - primeTerrain(world.getChunk(chunkX, chunkZ), surfaceFirstFreeY, floorFirstFreeY); - } - } - } - } - - private static void write(ChunkAccess chunk, Heightmap.Types type, IntBinaryOperator firstFreeY) { - ChunkPos pos = chunk.getPos(); - int minY = chunk.getMinY(); - int height = chunk.getHeight(); - int baseX = pos.getMinBlockX(); - int baseZ = pos.getMinBlockZ(); - SimpleBitStorage storage = new SimpleBitStorage(Mth.ceillog2(height + 1), COLUMNS); - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - int firstFree = firstFreeY.applyAsInt(baseX + x, baseZ + z) - minY; - storage.set(x + z * 16, Mth.clamp(firstFree, 0, height)); - } - } - Heightmap heightmap = chunk.getOrCreateHeightmapUnprimed(type); - long[] packed = storage.getRaw(); - int expected = heightmap.getRawData().length; - if (expected != packed.length) { - throw new IllegalStateException("Iris cannot prime " + type + " for chunk " - + pos.x() + "," + pos.z() + ": packed " + packed.length - + " words for a heightmap storing " + expected - + "; Heightmap.setRawData would silently fall back to block scanning"); - } - heightmap.setRawData(chunk, type, packed); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisItemRecipe.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisItemRecipe.java new file mode 100644 index 000000000..b96795890 --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisItemRecipe.java @@ -0,0 +1,116 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.structure.placement.LootResolver; +import art.arcane.iris.world.entity.IrisAttributeModifier; +import art.arcane.iris.world.entity.IrisEnchantment; +import art.arcane.iris.world.loot.IrisLoot; +import art.arcane.volmlib.nativelib.item.NativeItemRecipe; +import art.arcane.volmlib.util.format.Form; +import art.arcane.volmlib.util.math.RNG; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +record IrisItemRecipe(IrisLoot loot, RNG random) implements NativeItemRecipe { + private static final String COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx"; + + @Override + public String typeKey() { return loot.getTypeKey(); } + @Override + public int amount() { return LootResolver.inclusive(random, loot.getMinAmount(), loot.getMaxAmount()); } + @Override + public double chance() { return random.nextDouble(); } + @Override + public boolean unbreakable() { return loot.isUnbreakable(); } + @Override + public List itemFlags() { return loot.getItemFlags(); } + @Override + public Integer customModel() { return loot.getCustomModel(); } + @Override + public double durability() { return random.d(loot.getMinDurability(), loot.getMaxDurability()); } + @Override + public String leatherColor() { return loot.getLeatherColor(); } + @Override + public String dyeColor() { return loot.getDyeColorKey(); } + @Override + public String displayName() { return loot.getDisplayName() == null ? null : colorize(loot.getDisplayName()); } + @Override + public Map customNbt() { return loot.getCustomNbt(); } + + @Override + public List enchantments() { + if (loot.getEnchantments().isEmpty()) { + return List.of(); + } + List entries = new ArrayList<>(loot.getEnchantments().size()); + for (IrisEnchantment enchantment : loot.getEnchantments()) { + entries.add(new EnchantmentRecipe(enchantment, random)); + } + return entries; + } + + @Override + public List attributes() { + if (loot.getAttributes().isEmpty()) { + return List.of(); + } + List entries = new ArrayList<>(loot.getAttributes().size()); + for (IrisAttributeModifier attribute : loot.getAttributes()) { + entries.add(new AttributeRecipe(attribute, random)); + } + return entries; + } + + @Override + public List lore() { + if (loot.getLore().isEmpty()) { + return List.of(); + } + List lines = new ArrayList<>(); + for (String line : loot.getLore()) { + String colored = colorize(line); + if (colored.length() > 24) { + for (String wrapped : Form.wrapWords(colored, 24).split("\\Q\n\\E")) { + lines.add(wrapped.trim()); + } + } else { + lines.add(colored); + } + } + return lines; + } + + private static String colorize(String text) { + char[] chars = text.toCharArray(); + for (int i = 0; i < chars.length - 1; i++) { + if (chars[i] == '&' && COLOR_CODES.indexOf(chars[i + 1]) > -1) { + chars[i] = '§'; + chars[i + 1] = Character.toLowerCase(chars[i + 1]); + } + } + return new String(chars); + } + + private record EnchantmentRecipe(IrisEnchantment enchantment, RNG random) implements Enchantment { + @Override + public String name() { return enchantment.getEnchantment(); } + @Override + public double chance() { return enchantment.getChance(); } + @Override + public int level() { return enchantment.getLevel(random); } + } + + private record AttributeRecipe(IrisAttributeModifier modifier, RNG random) implements Attribute { + @Override + public String attribute() { return modifier.getAttribute(); } + @Override + public String name() { return modifier.getName(); } + @Override + public String operation() { return modifier.getOperation(); } + @Override + public double chance() { return modifier.getChance(); } + @Override + public double amount() { return modifier.getAmount(random); } + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java index 877ed494f..0c9248e89 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java @@ -18,12 +18,56 @@ package art.arcane.iris.modded; -import art.arcane.iris.nativegen.NativeTransitionColumn; -import art.arcane.iris.nativegen.NativeTerrainHeightCache; +import art.arcane.volmlib.nativelib.terrain.structure.StructureInjectionPolicy; +import art.arcane.volmlib.nativelib.terrain.StructureFrequencyControl; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferencePolicy; +import java.util.concurrent.Executor; + +import art.arcane.volmlib.nativelib.terrain.NativeSpawnBiomePolicy; + +import art.arcane.volmlib.nativelib.terrain.NativeSpawnSelection; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockColumn; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationLease; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRoute; + +import art.arcane.volmlib.nativelib.terrain.NativeModdedBiomePolicy; + +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeFeatureBiomeSource; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDimensionRuntime; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorOwner; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorHandle; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkGeneratorDefinition; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedGeneratorPolicy; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorContext; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedStructureStage; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; + +import art.arcane.iris.structure.nativegen.IrisStructurePolicy; + +import art.arcane.iris.world.history.TerrainNativeBlockKeys; + +import art.arcane.volmlib.nativelib.terrain.NativeChunkWritePolicy; + import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.world.history.NativeBiomeSpawnSelection; -import java.util.Optional; -import art.arcane.iris.nativegen.NativeGenerationWriteGuard; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.PackValidationRegistry; import art.arcane.iris.generation.runtime.IrisEngine; @@ -33,103 +77,41 @@ import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.structure.nativegen.NativeFeatureGenerationPolicy; import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; import art.arcane.iris.world.history.GenerationEpoch; -import art.arcane.iris.world.history.GenerationHistory; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; import art.arcane.iris.world.history.GenerationRegistryContract; import art.arcane.iris.world.history.GenerationRegistryContractFactory; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import art.arcane.iris.nativegen.NativeStructureStartInjector; -import art.arcane.iris.nativegen.NativeStructureReferenceRepair; -import art.arcane.iris.nativegen.NativeStructureVanillaLocator; -import art.arcane.iris.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.generation.context.IrisContext; import art.arcane.volmlib.util.hunk.Hunk; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.Codec; -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.NoiseColumn; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.BiomeResolver; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.MobSpawnSettings; -import net.minecraft.world.level.block.Blocks; -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.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.LegacyRandomSource; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBiomeResolver; +import art.arcane.iris.modded.service.ModdedChunkUpdateService; import java.io.File; import java.io.IOException; import java.nio.file.Path; -import java.util.Arrays; import java.util.Collection; -import java.util.EnumSet; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.OptionalInt; import java.util.Set; import java.util.TreeSet; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.IntBinaryOperator; import java.util.function.IntConsumer; -public final class IrisModdedChunkGenerator extends ChunkGenerator { +public final class IrisModdedChunkGenerator implements NativeGeneratorOwner, NativeModdedGeneratorPolicy { // Vanilla-shaped fallback for an unbound generator (matches IrisDimension defaults). getMinY, // getSeaLevel and getGenDepth are called from world creation and client screens, so they must // answer without disk I/O and without throwing before a level is bound. + public static final NativeChunkGeneratorDefinition DEFINITION = new NativeChunkGeneratorDefinition( + "irisworldgen", "iris", IrisModdedChunkGenerator::new); private static final ModdedDimensionMetadata.DimensionMetadata UNBOUND_HEIGHTS = new ModdedDimensionMetadata.DimensionMetadata(-64, 320, 63); - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec((RecordCodecBuilder.Instance instance) -> instance.group( - BiomeSource.CODEC.fieldOf("biome_source").forGetter((IrisModdedChunkGenerator generator) -> generator.serializedBiomeSource), - Codec.STRING.fieldOf("dimension").forGetter((IrisModdedChunkGenerator generator) -> generator.dimensionKey) - ).apply(instance, IrisModdedChunkGenerator::new)); - public static void startGenPool() { ModdedGenPool.start(); } @@ -141,13 +123,9 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { private final String dimensionKey; private final String defaultPack; private final String defaultDimensionKey; - private final BiomeSource serializedBiomeSource; - final IrisModdedBiomeSource structureBiomeSource; private final ModdedEngineBinding engineBinding = new ModdedEngineBinding<>(60L, TimeUnit.SECONDS); - private final ModdedNativeStructureStage nativeStructures = new ModdedNativeStructureStage(this); - private final ModdedSpawnTableMerger spawnTables = new ModdedSpawnTableMerger(this); - private final ModdedImportedFeatureStage importedFeatures; - private final NativeTerrainHeightCache terrainHeights = new NativeTerrainHeightCache(); + private ModdedImportedFeatureStage importedFeatures; + private final NativeModdedChunkGenerator nativeGenerator; private final AtomicBoolean announced = new AtomicBoolean(false); private final IntConsumer generationRuntimeRetirementListener = this::retireGenerationRuntimeCaches; private volatile boolean unloading; @@ -161,37 +139,17 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { private volatile Set configuredStructureBiomeKeys; private volatile ModdedDimensionMetadata.ConfiguredPack configuredPack; private volatile ModdedDimensionMetadata.DimensionMetadata heightMetadata; - private volatile ServerLevel boundLevel; + private volatile NativeWorld boundLevel; private volatile IrisEngine retirementListenerEngine; - public IrisModdedChunkGenerator(BiomeSource biomeSource, String dimensionKey) { - this(biomeSource, dimensionKey, new IrisModdedBiomeSource(biomeSource)); - } - - private IrisModdedChunkGenerator(BiomeSource serializedBiomeSource, String dimensionKey, IrisModdedBiomeSource structureBiomeSource) { - this(serializedBiomeSource, dimensionKey, structureBiomeSource, - new ModdedImportedFeatureStage(structureBiomeSource)); - } - - private IrisModdedChunkGenerator(BiomeSource serializedBiomeSource, String dimensionKey, - IrisModdedBiomeSource structureBiomeSource, - ModdedImportedFeatureStage importedFeatures) { - // Two-argument ChunkGenerator constructor: the getter maps Iris custom-biome holders onto the - // generation settings of their vanilla derivative, which is what feeds the per-step feature lists and - // BiomeFilter's hasFeature gate. It is a pass-through to vanilla's default getter until a pack turns - // importedFeatures on, so with the control off nothing about generation changes. - super(structureBiomeSource, importedFeatures::generationSettings); - this.importedFeatures = importedFeatures; - importedFeatures.bind(this); - this.dimensionKey = dimensionKey; - this.serializedBiomeSource = serializedBiomeSource; - this.structureBiomeSource = structureBiomeSource; - this.structureBiomeSource.bind(this); + public IrisModdedChunkGenerator(NativeGeneratorContext context) { + this.dimensionKey = context.dimensionKey(); int colon = dimensionKey.indexOf(':'); this.defaultPack = colon >= 0 ? dimensionKey.substring(0, colon) : dimensionKey; this.defaultDimensionKey = colon >= 0 ? dimensionKey.substring(colon + 1) : dimensionKey; this.activePack = defaultPack; this.activeDimensionKey = defaultDimensionKey; + this.nativeGenerator = new NativeModdedChunkGenerator<>(new NativeModdedChunkGenerator.Options<>(context, this, this, DEFINITION)); } public synchronized void repoint(String pack, String packDimensionKey, long seed) { @@ -221,7 +179,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { Path packRoot, ModdedGenerationMode generationMode ) { - ServerLevel level = boundLevel(); + NativeWorld level = boundLevel(); if (level != null) { this.generationMode = generationMode; repointAndBind(level, pack, packDimensionKey, seed); @@ -230,13 +188,13 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { applyUnboundConfiguration(pack, packDimensionKey, seed, packRoot, generationMode); } - synchronized void repointAndBind(ServerLevel level, String pack, String packDimensionKey, long seed) { + synchronized void repointAndBind(NativeWorld level, String pack, String packDimensionKey, long seed) { requireBindingAllowed(); - if (level.getChunkSource().getGenerator() != this) { - throw new IllegalArgumentException("ServerLevel does not use Iris generator '" + dimensionKey + "'"); + if (!nativeGenerator.represents(level)) { + throw new IllegalArgumentException("NativeWorld does not use Iris generator '" + dimensionKey + "'"); } requireGlobalStructureGeneration( - level.getServer().getWorldGenSettings().options().generateStructures(), dimensionKey); + NativeModdedServer.forWorld(level).generateStructures(), dimensionKey); Engine activeEngine = engineIfBound(); Engine replacement = ModdedWorldEngines.prepareReplacement( level, @@ -250,7 +208,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { requireStructureBiomeUniverseCompatible( activeEngine.getDimension(), replacement.getDimension()); } - nativeStructures.installVolumeIndex(level, replacement); + nativeGenerator.installVolumeIndex(level, replacement); ModdedWorldEngines.installReplacement(level, replacement); replacement.getPlatformHooks().applyWorldBoundary(replacement); } catch (Throwable error) { @@ -290,14 +248,14 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { public synchronized void unbindEngine() { unloading = true; - ServerLevel level = boundLevel(); - unbindEngine(level); + NativeWorld level = boundLevel(); + unbindEngine(level == null ? null : level); } - synchronized void unbindEngine(ServerLevel level) { + synchronized void unbindEngine(NativeWorld world) { unloading = true; - if (level != null) { - ModdedWorldEngines.evictOrThrow(level); + if (world != null) { + ModdedWorldEngines.evictOrThrow(world); } clearEngineBinding(); } @@ -311,10 +269,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { this.immutablePackRoot = null; this.engineBinding.reset(); this.announced.set(false); - this.structureBiomeSource.clearCaches(); + this.nativeGenerator.clearCaches(); this.importedFeatures.invalidate(); - this.nativeStructures.clearWorldCheckStructureShifts(); - this.spawnTables.resetVanillaSpawnBiomes(); } private void applyUnboundConfiguration( @@ -336,10 +292,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { this.configuredPack = null; this.engineBinding.reset(); this.announced.set(false); - this.structureBiomeSource.clearCaches(); + this.nativeGenerator.clearCaches(); this.importedFeatures.invalidate(); - this.nativeStructures.clearWorldCheckStructureShifts(); - this.spawnTables.resetVanillaSpawnBiomes(); primeHeightMetadata(); } @@ -361,18 +315,6 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { return activeDimensionKey; } - @Override - protected MapCodec codec() { - return CODEC; - } - - @Override - public ChunkGeneratorStructureState createState(HolderLookup structureSets, RandomState randomState, long seed) { - ChunkGeneratorStructureState state = ChunkGeneratorStructureState.createForNormal( - randomState, seed, structureBiomeSource.forStructureState(structureSets), structureSets); - return ModdedStructureSetFrequencyOverrides.apply(state, configuredImportedStructures()); - } - private IrisImportedStructureControl configuredImportedStructures() { Engine current = engine; if (current != null && !current.isClosed() && !current.isClosing()) { @@ -385,57 +327,30 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { return importedStructures == null ? new IrisImportedStructureControl() : importedStructures; } - @Override - public Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, - BlockPos pos, int radius, - boolean findUnexplored) { - Engine current = engine(); - int chunkX = Math.floorDiv(pos.getX(), 16); - int chunkZ = Math.floorDiv(pos.getZ(), 16); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkX, chunkZ, "modded_structure_locate"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_structure_locate"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - HolderSet reachable = nativeStructures.filterReachableNativeStructures( - level, holders, current); - NativeStructureVanillaLocator.Candidate nativeCandidate = - reachable.size() == 0 ? null - : NativeStructureVanillaLocator.predict( - level, reachable, pos, radius, findUnexplored); - return nativeStructures.findNearestIrisStructure( - level, holders, pos, Math.max(0, radius), findUnexplored, current, nativeCandidate); - } - } - - public boolean isNativeStructureReachable(Holder structure) { - return structure != null && structureBiomeSource.isStructureReachable(structure); - } - - private ServerLevel boundLevel() { - ServerLevel cached = boundLevel; + private NativeWorld boundLevel() { + NativeWorld cached = boundLevel; if (cached != null) { return cached; } - MinecraftServer server = ModdedEngineBootstrap.currentServer(); + NativeModdedServer server = server(); if (server == null) { return null; } - ServerLevel resolved = resolveBoundLevel(server, ModdedServerLevels.levels(server)); + NativeWorld resolved = resolveBoundLevel(server, server.worlds()); if (resolved != null) { boundLevel = resolved; } return resolved; } - ServerLevel resolveBoundLevel(MinecraftServer server, List snapshot) { - for (ServerLevel level : snapshot) { - if (level.getChunkSource().getGenerator() == this) { + NativeWorld resolveBoundLevel(NativeModdedServer server, List snapshot) { + for (NativeWorld level : snapshot) { + if (nativeGenerator.represents(level)) { return level; } } - ServerLevel overworld = server.getLevel(Level.OVERWORLD); - return overworld != null && overworld.getChunkSource().getGenerator() == this ? overworld : null; + NativeWorld overworld = server.overworld(); + return overworld != null && nativeGenerator.represents(overworld) ? overworld : null; } Engine engine() { @@ -443,34 +358,34 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { if (cached != null) { return cached; } - ServerLevel level = boundLevel(); + NativeWorld level = boundLevel(); if (level == null) { - throw new IllegalStateException("Iris generator '" + dimensionKey + "' has no bound ServerLevel yet"); + throw new IllegalStateException("Iris generator '" + dimensionKey + "' has no bound NativeWorld yet"); } return bindGenerationLevel(level); } - private Engine engine(ResourceKey levelKey) { + private Engine engine(String levelKey) { Engine cached = readyEngine(); if (cached != null) { return cached; } - ServerLevel level = boundLevel; + NativeWorld level = boundLevel; if (level == null) { - level = requirePublishedLevel(ModdedEngineBootstrap.currentServer(), levelKey); + level = requirePublishedLevel(server(), levelKey); } else { requireGeneratorLevel(level, levelKey); } return bindGenerationLevel(level); } - private Engine engine(ServerLevel generationLevel) { + private Engine engine(NativeWorld generationLevel) { Engine cached = readyEngine(); if (cached != null) { return cached; } - ServerLevel level = boundLevel == null ? generationLevel : boundLevel; - requireGeneratorLevel(level, generationLevel.dimension()); + NativeWorld level = boundLevel == null ? generationLevel : boundLevel; + requireGeneratorLevel(level, generationLevel.name()); return bindGenerationLevel(level); } @@ -484,7 +399,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { return null; } - private Engine bindGenerationLevel(ServerLevel level) { + private Engine bindGenerationLevel(NativeWorld level) { bindLevel(level); Engine bound = readyEngine(); if (bound == null) { @@ -494,55 +409,54 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { return bound; } - ServerLevel requirePublishedLevel(MinecraftServer server, ResourceKey levelKey) { + NativeWorld requirePublishedLevel(NativeModdedServer server, String levelKey) { if (server == null) { throw new IllegalStateException("Iris generator '" + dimensionKey - + "' cannot resolve level '" + levelKey.identifier() + "': server is unavailable"); + + "' cannot resolve level '" + levelKey + "': server is unavailable"); } - ServerLevel level = server.getLevel(levelKey); + NativeWorld level = server.world(levelKey); if (level == null) { throw new IllegalStateException("Iris generator '" + dimensionKey - + "' has no published ServerLevel for '" + levelKey.identifier() + "'"); + + "' has no published NativeWorld for '" + levelKey + "'"); } requireGeneratorLevel(level, levelKey); return level; } - private void requireGeneratorLevel(ServerLevel level, ResourceKey levelKey) { - if (!levelKey.equals(level.dimension())) { + private void requireGeneratorLevel(NativeWorld level, String levelKey) { + if (!levelKey.equals(level.name())) { throw new IllegalStateException("Iris generator '" + dimensionKey + "' resolved level '" - + level.dimension().identifier() + "' while binding '" + levelKey.identifier() + "'"); + + level.name() + "' while binding '" + levelKey + "'"); } - ChunkGenerator publishedGenerator = level.getChunkSource().getGenerator(); - if (publishedGenerator != this) { - throw new IllegalStateException("Published ServerLevel '" + levelKey.identifier() + if (!nativeGenerator.represents(level)) { + throw new IllegalStateException("Published NativeWorld '" + levelKey + "' does not use Iris generator '" + dimensionKey + "'"); } } - synchronized void bindLevel(ServerLevel level) { - if (level.getChunkSource().getGenerator() != this) { - throw new IllegalArgumentException("ServerLevel does not use Iris generator '" + dimensionKey + "'"); + synchronized void bindLevel(NativeWorld level) { + if (!nativeGenerator.represents(level)) { + throw new IllegalArgumentException("NativeWorld does not use Iris generator '" + dimensionKey + "'"); } Engine current = engineIfBound(); - if (boundLevel == level && current != null && current.getComplex() != null) { + if (NativeDimensionRuntime.sameWorld(boundLevel, level) && current != null && current.getComplex() != null) { return; } requireCompletedShutdown(engine); unloading = false; Engine bound = bindEngine(level); - nativeStructures.installVolumeIndex(level, bound); + nativeGenerator.installVolumeIndex(level, bound); // Bind time: a feature-order cycle is reported here, once, and degrades to features-off. Non-waiting for // the same reason as repointAndBind: this method owns the generator monitor. importedFeatures.prepareWithoutWaiting(bound); - ModdedIrisLog.info("Iris bound {}: chunk system {}", level.dimension().identifier(), ModdedGenPool.describeChunkSystem()); + ModdedIrisLog.info("Iris bound {}: chunk system {}", level.name(), ModdedGenPool.describeChunkSystem()); } - private Engine bindEngine(ServerLevel level) { + private Engine bindEngine(NativeWorld level) { requireBindingAllowed(); try { requireGlobalStructureGeneration( - level.getServer().getWorldGenSettings().options().generateStructures(), dimensionKey); + NativeModdedServer.forWorld(level).generateStructures(), dimensionKey); } catch (RuntimeException error) { engineBinding.fail(error); throw error; @@ -582,7 +496,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { immutablePackRoot = created.getData().getDataFolder().toPath().toAbsolutePath().normalize(); heightMetadata = engineHeights(created); configuredStructureBiomeKeys = null; - structureBiomeSource.clearCaches(); + nativeGenerator.clearCaches(); engineBinding.complete(created); return created; } catch (Throwable error) { @@ -673,7 +587,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { if (cached != null && !cached.isClosed() && cached.getComplex() != null) { return cached; } - ServerLevel level = boundLevel(); + NativeWorld level = boundLevel(); return level == null ? null : bindEngine(level); } @@ -700,18 +614,18 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { if (current != null && !current.isClosed() && current.getComplex() != null) { return current; } - ServerLevel level = boundLevel(); + NativeWorld level = boundLevel(); if (level != null) { return bindEngine(level); } - MinecraftServer server = ModdedEngineBootstrap.currentServer(); + NativeModdedServer server = server(); if (server == null) { throw new IllegalStateException("Iris generator '" + dimensionKey + "' cannot answer " + operation + " without an active server"); } - if (server.isSameThread()) { + if (server.isServerThread()) { throw new IllegalStateException("Iris generator '" + dimensionKey + "' cannot answer " - + operation + " on the server thread before its ServerLevel is bound"); + + operation + " on the server thread before its NativeWorld is bound"); } return awaitStructureEngine(); } @@ -721,9 +635,9 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { if (configuredSeed != Long.MIN_VALUE) { return configuredSeed; } - ServerLevel level = boundLevel(); + NativeWorld level = boundLevel(); if (level != null) { - return level.getSeed(); + return level.seed(); } Engine current = engine; return current == null ? 0L : current.getWorld().getRawWorldSeed(); @@ -881,13 +795,13 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { return lastChunkGenAt; } - public void prepareRuntimeHotload(ServerLevel level, Engine current) { - if (this.engine != current || level.getChunkSource().getGenerator() != this) { + public void prepareRuntimeHotload(NativeWorld level, Engine current) { + if (this.engine != current || !nativeGenerator.represents(level)) { throw new IllegalStateException("Iris generator '" + dimensionKey + "' cannot prepare caches for an unrelated engine hotload"); } resetRuntimeCaches(); - nativeStructures.installVolumeIndex(level, current); + nativeGenerator.installVolumeIndex(level, current); } public static void requireStructureBiomeUniverseCompatible( @@ -899,8 +813,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { IrisData replacementData = Objects.requireNonNull(replacement.getLoader(), "Replacement Iris dimension has no pack loader"); requireStructureBiomeUniverseCompatible( - IrisModdedBiomeSource.collectStructureBiomeKeys(active, activeData), - IrisModdedBiomeSource.collectStructureBiomeKeys(replacement, replacementData)); + ModdedBiomePolicy.collectStructureBiomeKeys(active, activeData), + ModdedBiomePolicy.collectStructureBiomeKeys(replacement, replacementData)); } static void requireStructureBiomeUniverseCompatible(Set active, Set replacement) { @@ -914,445 +828,23 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { private void resetRuntimeCaches() { configuredStructureBiomeKeys = null; - structureBiomeSource.clearCaches(); + nativeGenerator.clearCaches(); importedFeatures.invalidate(); - nativeStructures.clearWorldCheckStructureShifts(); - spawnTables.resetVanillaSpawnBiomes(); } - @Override - public WeightedList getMobsAt( - Holder biome, StructureManager structureManager, MobCategory category, BlockPos pos) { - Engine current = engine(); - NativeBiomeSpawnSelection selection = NativeBiomeSpawnSelection.at(current, pos.getX(), pos.getY(), pos.getZ(), - biome.unwrapKey().map(key -> key.identifier().toString()).orElse("")); - if (selection.mode() == NativeBiomeSpawnSelection.Mode.LOADING) { - return WeightedList.of(List.of()); - } - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - current, pos.getX(), pos.getZ(), "modded_mob_spawn_table"); - GenerationSessionLease lease = requireGenerationLease(current, "modded_mob_spawn_table"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - WeightedList explicitSpawns = - biome.value().getMobSettings().getMobs(category); - WeightedList resolvedSpawns = super.getMobsAt( - biome, structureManager, category, pos); - if (resolvedSpawns != explicitSpawns) { - return resolvedSpawns; - } - - Registry registry = structureManager.registryAccess().lookupOrThrow(Registries.BIOME); - Holder vanillaSpawnBiome; - if (selection.mode() == NativeBiomeSpawnSelection.Mode.RETAINED) { - vanillaSpawnBiome = spawnTables.resolveBiomeHolder(registry, selection.derivativeKey()); - } else if (selection.mode() == NativeBiomeSpawnSelection.Mode.CURRENT) { - spawnTables.initializeVanillaSpawnBiomes(registry); - vanillaSpawnBiome = spawnTables.vanillaSpawnBiome(biome.value()); - } else { - vanillaSpawnBiome = null; - } - if (vanillaSpawnBiome == null) { - return explicitSpawns; - } - - WeightedList vanillaSpawns = - vanillaSpawnBiome.value().getMobSettings().getMobs(category); - if (explicitSpawns.isEmpty()) { - return vanillaSpawns; - } - if (vanillaSpawns.isEmpty()) { - return explicitSpawns; - } - - return spawnTables.mergedSpawnTable( - current, - biome.value(), - vanillaSpawnBiome.value(), - category, - vanillaSpawns, - explicitSpawns - ); + public void updateRegeneratedChunk(int chunkX, int chunkZ) { + ModdedChunkUpdateService service = ModdedEngineBootstrap.services().service(ModdedChunkUpdateService.class); + if (service == null) { + throw new IllegalStateException("Iris chunk update service is unavailable during regeneration"); } + service.updateRegeneratedChunk(engine(), boundLevel(), chunkX, chunkZ); } - @Override - public CompletableFuture createBiomes(RandomState randomState, Blender blender, - StructureManager structureManager, ChunkAccess chunk) { - Engine current = engine(); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_create_biomes"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_create_biomes"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - chunk.fillBiomesFromNoise(structureBiomeSource::getVisibleNoiseBiome, randomState.sampler()); - return CompletableFuture.completedFuture(chunk); - } - } - - @Override - public CompletableFuture fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk) { - Engine generationEngine = engine(); - ChunkPos pos = chunk.getPos(); - lastChunkGenAt = System.currentTimeMillis(); - ModdedIrisLog.debug("Iris generating chunk {},{}", pos.x(), pos.z()); - - PlatformBlockState air = IrisPlatforms.get().registries().air(); - GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - generationEngine, pos.x(), pos.z(), "modded_chunk_pipeline"); - - try { - if (ModdedGenPool.parallelChunkSystem()) { - try { - return CompletableFuture.completedFuture( - generateTerrain(chunk, generationEngine, pos, air, route)); - } finally { - if (route != null) { - route.close(); - } - } - } - CompletableFuture pipeline = CompletableFuture.supplyAsync( - () -> generateTerrain(chunk, generationEngine, pos, air, route), - ModdedGenPool.pool()); - return closeRouteOnCompletion(pipeline, route); - } catch (RuntimeException | Error failure) { - closeHistoryRoute(route, failure); - throw failure; - } - } - - private ChunkAccess generateTerrain(ChunkAccess chunk, Engine generationEngine, ChunkPos pos, - PlatformBlockState air, - GenerationHistoryRuntimeRouter.RuntimeRoute route) { - try (GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = generationEngine.acquireGenerationLease("modded_chunk_pipeline"); - IrisContext.Scope ignored = IrisContext.open(generationEngine, lease.sessionId(), null)) { - if (announced.compareAndSet(false, true)) { - ModdedIrisLog.info("Iris generating {} through IrisModdedChunkGenerator (dim={} first chunk {},{})", - dimensionKey, generationEngine.getDimension().getLoadKey(), pos.x(), pos.z()); - } - int dimMinY = generationEngine.getMinHeight(); - int dimMaxY = generationEngine.getMaxHeight(); - int height = dimMaxY - dimMinY; - ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, air); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); - generationEngine.generate(pos.getMinBlockX(), pos.getMinBlockZ(), blocks, biomes, false); - - writeBlocks(chunk, blocks, dimMinY, height); - if (route != null) { - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - route.claimGeneratedSemantics((x, y, z) -> { - BlockState state = chunk.getBlockState(position.set(x, dimMinY + y, z)); - return state.isAir() || state.liquid(); - }); - } - ModdedNativeTerrainReceipts.persist(chunk, route); - writeTerrainHeightmaps(chunk, generationEngine, pos, height); - Heightmap.primeHeightmaps(chunk, EnumSet.of( - Heightmap.Types.MOTION_BLOCKING, - Heightmap.Types.MOTION_BLOCKING_NO_LEAVES)); - ModdedWorldManager.enqueueGenerated(generationEngine, pos.x(), pos.z()); - return chunk; - } catch (GenerationSessionException e) { - if (generationEngine.isClosing() || e.isExpectedTeardown()) { - ModdedIrisLog.debug("Iris chunk {},{} skipped: engine sealed for hotload/teardown", pos.x(), pos.z()); - throw new IllegalStateException( - "Iris chunk generation was rejected during an engine transition.", e); - } - ModdedIrisLog.error("Iris failed to generate chunk {},{}", pos.x(), pos.z(), e); - throw new IllegalStateException("Iris generation failed for chunk " + pos.x() + "," + pos.z(), e); - } catch (Throwable e) { - ModdedIrisLog.error("Iris failed to generate chunk {},{}", pos.x(), pos.z(), e); - throw new IllegalStateException("Iris generation failed for chunk " + pos.x() + "," + pos.z(), e); - } - } - - private static CompletableFuture closeRouteOnCompletion( - CompletableFuture pipeline, - GenerationHistoryRuntimeRouter.RuntimeRoute route - ) { - if (route == null) { - return pipeline; - } - CompletableFuture completion = new CompletableFuture<>(); - pipeline.whenComplete((ChunkAccess chunk, Throwable failure) -> { - boolean cancelled = isCancellationFailure(failure); - Throwable completionFailure = failure; - try { - route.close(); - } catch (Throwable closeFailure) { - completionFailure = appendFailure(completionFailure, closeFailure); - } - if (completionFailure == null) { - completion.complete(chunk); - } else if (cancelled) { - completion.cancel(false); - } else { - completion.completeExceptionally(completionFailure); - } - }); - route.detachThread(); - return completion; - } - - private static void closeHistoryRoute( - GenerationHistoryRuntimeRouter.RuntimeRoute route, - Throwable failure - ) { - if (route == null) { - return; - } - try { - route.close(); - } catch (Throwable closeFailure) { - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - } - } - - private static Throwable appendFailure(Throwable failure, Throwable closeFailure) { - if (failure == null) { - return closeFailure; - } - if (failure != closeFailure) { - failure.addSuppressed(closeFailure); - } - return failure; - } - - private static boolean isCancellationFailure(Throwable failure) { - Throwable current = failure; - while (current instanceof CompletionException && current.getCause() != null) { - current = current.getCause(); - } - return current instanceof CancellationException; - } - - private void writeTerrainHeightmaps(ChunkAccess chunk, Engine generationEngine, ChunkPos pos, int height) { - int baseX = pos.getMinBlockX(); - int baseZ = pos.getMinBlockZ(); - writeTerrainHeightmap(chunk, Heightmap.Types.WORLD_SURFACE_WG, height, - (x, z) -> generationEngine.getHeight(baseX + x, baseZ + z, false) + 1); - writeTerrainHeightmap(chunk, Heightmap.Types.OCEAN_FLOOR_WG, height, - (x, z) -> generationEngine.getHeight(baseX + x, baseZ + z, true) + 1); - } - - private void writeTerrainHeightmap(ChunkAccess chunk, Heightmap.Types type, int height, - IntBinaryOperator heightResolver) { - Heightmap heightmap = chunk.getOrCreateHeightmapUnprimed(type); - heightmap.setRawData(chunk, type, ModdedHeightmaps.terrainRawData(height, heightResolver)); - } - - public BiomeResolver regenBiomeResolver() { + public NativeBiomeResolver regenBiomeResolver() { engine(); - return structureBiomeSource::getVisibleNoiseBiome; + return nativeGenerator.biomeResolver(); } - private void writeBlocks(ChunkAccess chunk, ModdedBlockBuffer blocks, int dimMinY, int height) { - int chunkMinY = chunk.getMinY(); - int chunkMaxY = chunkMinY + chunk.getHeight(); - int from = Math.max(dimMinY, chunkMinY); - int to = Math.min(dimMinY + height, chunkMaxY); - int baseX = chunk.getPos().getMinBlockX(); - int baseZ = chunk.getPos().getMinBlockZ(); - - for (int y = from; y < to; ) { - int sectionIndex = chunk.getSectionIndex(y); - LevelChunkSection section = chunk.getSection(sectionIndex); - int sectionMinY = chunk.getSectionYFromSectionIndex(sectionIndex) << 4; - int sectionEnd = Math.min(sectionMinY + 16, to); - section.acquire(); - try { - for (int blockY = y; blockY < sectionEnd; blockY++) { - int bufferY = blockY - dimMinY; - int localY = blockY & 15; - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - PlatformBlockState state = blocks.rawOrNull(x, bufferY, z); - if (state == null) { - continue; - } - BlockState blockState = (BlockState) state.nativeHandle(); - section.setBlockState(x, localY, z, blockState, false); - if (blockState.hasBlockEntity()) { - createDefaultBlockEntity(chunk, new BlockPos(baseX + x, blockY, baseZ + z), blockState); - } - } - } - } - } finally { - section.release(); - } - y = sectionEnd; - } - } - - static void createDefaultBlockEntity(ChunkAccess chunk, BlockPos position, BlockState state) { - if (!(state.getBlock() instanceof EntityBlock entityBlock)) { - return; - } - BlockEntity blockEntity = entityBlock.newBlockEntity(position, state); - if (blockEntity != null) { - chunk.setBlockEntity(blockEntity); - } - } - - @Override - public void applyCarvers(WorldGenRegion region, long seed, RandomState randomState, BiomeManager biomeManager, StructureManager structureManager, ChunkAccess chunk) { - Engine current = engine(region.getLevel()); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_apply_carvers"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_apply_carvers"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - } - } - - @Override - public void buildSurface(WorldGenRegion region, StructureManager structureManager, RandomState randomState, ChunkAccess chunk) { - Engine current = engine(region.getLevel()); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_build_surface"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_build_surface"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - } - } - - @Override - public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) { - Engine current = engine(level.getLevel()); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_biome_decoration"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_biome_decoration"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - if (chunk.getPersistedStatus().isOrAfter(ChunkStatus.FEATURES)) { - return; - } - nativeStructures.placeVanillaStructures(level, chunk, structureManager); - if (!allowsRoutedDiscreteGeneration( - current, - route, - chunkPos, - chunk, - ChunkStatus.FEATURES - )) { - return; - } - if (!NativeGenerationWriteGuard.allowsDecoration(current, level, chunkPos)) { - return; - } - importedFeatures.prepare(current); - // Vanilla's placed-feature pass, on THIS thread and never on ModdedGenPool: the FEATURES chunk - // step writes into the eight neighbouring chunks and is not parallel-safe. Inert unless the - // dimension set importedFeatures.enabled. - importedFeatures.run(level, chunk, current); - } - } - - @Override - public void createStructures(RegistryAccess registryAccess, ChunkGeneratorStructureState structureState, StructureManager structureManager, ChunkAccess chunk, StructureTemplateManager templateManager, ResourceKey levelKey) { - Engine current = engine(levelKey); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_create_structures"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_create_structures"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - if (!allowsRoutedDiscreteGeneration( - current, - route, - chunkPos, - chunk, - ChunkStatus.STRUCTURE_STARTS - )) { - return; - } - Map previousStarts = new HashMap<>(chunk.getAllStarts()); - super.createStructures(registryAccess, structureState, structureManager, chunk, templateManager, levelKey); - Map configuredStarts = NativeStructureStartInjector.inject( - new NativeStructureStartInjector.InjectionContext( - current, - registryAccess, - structureState, - structureManager, - chunk, - templateManager, - levelKey, - this, - structureBiomeSource - )); - nativeStructures.adjustGeneratedStructures( - registryAccess, chunk, previousStarts, configuredStarts, current, templateManager); - ModdedNativeTerrainReceipts.persistStructureActivation(chunk, route); - } - } - - @Override - public void createReferences(WorldGenLevel level, StructureManager structureManager, ChunkAccess chunk) { - Engine current = engine(level.getLevel()); - ChunkPos chunkPos = chunk.getPos(); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, chunkPos.x(), chunkPos.z(), "modded_create_references"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_create_references"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - NativeStructureReferenceRepair.createReferences( - current, level, structureManager, chunk); - } - } - - Integer worldCheckStructureShift(String structureId, ChunkPos startChunk) { - return nativeStructures.worldCheckStructureShift(structureId, startChunk); - } - - @Override - public void spawnOriginalMobs(WorldGenRegion region) { - ChunkPos center = region.getCenter(); - Engine current = engine(region.getLevel()); - try (GenerationHistoryRuntimeRouter.RuntimeRoute route = openHistoryRoute( - current, center.x(), center.z(), "modded_spawn_original_mobs"); - GenerationHistoryRuntimeRouter.RuntimeRoute.RuntimeScope runtimeScope = openHistoryRuntimeScope(route); - GenerationSessionLease lease = requireGenerationLease(current, "modded_spawn_original_mobs"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - ChunkAccess centerChunk = region.getChunk(center.x(), center.z()); - if (!allowsRoutedDiscreteGeneration( - current, - route, - center, - centerChunk, - ChunkStatus.SPAWN - )) { - return; - } - Registry registry = region.registryAccess().lookupOrThrow(Registries.BIOME); - spawnTables.initializeVanillaSpawnBiomes(registry); - Holder visibleBiome; - if (current.getDimensionStackContext() == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } else { - visibleBiome = structureBiomeSource.getVisibleSurfaceBiome( - center.getMinBlockX() + 8, - center.getMinBlockZ() + 8); - if (visibleBiome == null) { - visibleBiome = region.getBiome(center.getWorldPosition().atY(region.getMaxY())); - } - } - Holder vanillaBiome = spawnTables.vanillaSpawnBiome(visibleBiome.value()); - WorldgenRandom random = new WorldgenRandom(new LegacyRandomSource(RandomSupport.generateUniqueSeed())); - random.setDecorationSeed(region.getSeed(), center.getMinBlockX(), center.getMinBlockZ()); - NaturalSpawner.spawnMobsForChunkGeneration( - region, vanillaBiome == null ? visibleBiome : vanillaBiome, center, random); - } - } - - @Override public int getGenDepth() { Engine current = engine; return current == null || current.isClosed() @@ -1360,7 +852,6 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { : current.getMaxHeight() - current.getMinHeight(); } - @Override public int getSeaLevel() { Engine current = engine; return current == null || current.isClosed() @@ -1368,7 +859,6 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { : current.getMinHeight() + current.getDimension().getFluidHeight(); } - @Override public int getMinY() { Engine current = engine; return current == null || current.isClosed() @@ -1376,99 +866,12 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { : current.getMinHeight(); } - @Override - public int getSpawnHeight(LevelHeightAccessor heightAccessor) { - return ModdedDimensionMetadata.clampSpawnHeight(heightAccessor.getMinY(), heightAccessor.getHeight()); - } - - @Override - public int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor, RandomState randomState) { - Engine current = requireDataQueryEngine("base height"); - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - current, x, z, "modded_base_height"); - GenerationSessionLease lease = current.acquireGenerationLease("modded_base_height"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - NativeTerrainHeightCache.Query query = new NativeTerrainHeightCache.Query( - current.getCacheID(), x, z, type, heightAccessor.getMinY(), heightAccessor.getHeight()); - OptionalInt resolved = terrainHeights.resolvedHeight(query, - () -> resolvedBaseHeight(current, x, z, type, heightAccessor)); - if (resolved.isPresent()) { - return resolved.getAsInt(); - } - boolean ignoreFluid = !type.isOpaque().test(Blocks.WATER.defaultBlockState()); - int height = current.getDimensionStackContext() == null - ? current.getHeight(x, z, ignoreFluid) - : Engine.hostHeight(current, x, z, ignoreFluid); - return heightAccessor.getMinY() + height + 1; - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base height query could not acquire its engine runtime.", e); - } - } - - private OptionalInt resolvedBaseHeight(Engine current, int x, int z, Heightmap.Types type, LevelHeightAccessor heightAccessor) { - Optional resolved = current.getComplex().resolvedTerrainColumn(x, z); - return resolved.isPresent() - ? OptionalInt.of(NativeTransitionColumn.height(resolved.get(), type, heightAccessor)) - : OptionalInt.empty(); - } - - @Override - public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor heightAccessor, RandomState randomState) { - int minY = heightAccessor.getMinY(); - Engine current = requireDataQueryEngine("base column"); - try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( - current, x, z, "modded_base_column"); - GenerationSessionLease lease = current.acquireGenerationLease("modded_base_column"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - Optional resolved = current.getComplex().resolvedTerrainColumn(x, z); - if (resolved.isPresent()) { - return NativeTransitionColumn.column(resolved.get(), heightAccessor); - } - BlockState[] states = new BlockState[heightAccessor.getHeight()]; - BlockState airState = Blocks.AIR.defaultBlockState(); - boolean dimensionStack = current.getDimensionStackContext() != null; - int surface = dimensionStack - ? Engine.hostHeight(current, x, z, true) - : current.getHeight(x, z, true); - int fluid = dimensionStack - ? Engine.hostHeight(current, x, z, false) - : current.getHeight(x, z, false); - BlockState stone = Blocks.STONE.defaultBlockState(); - BlockState water = Blocks.WATER.defaultBlockState(); - int solidEnd = Math.max(0, Math.min(states.length, surface + 1)); - int fluidEnd = Math.max(solidEnd, Math.max(0, Math.min(states.length, fluid + 1))); - Arrays.fill(states, 0, solidEnd, stone); - Arrays.fill(states, solidEnd, fluidEnd, water); - Arrays.fill(states, fluidEnd, states.length, airState); - return new NoiseColumn(minY, states); - } catch (GenerationSessionException e) { - throw new IllegalStateException("Iris base column query could not acquire its engine runtime.", e); - } - } - public boolean allowsNativeChunkWrite(int chunkX, int chunkZ) { Engine current = engine; return current == null || !current.isClosing() && !current.isClosed() && current.getComplex().allowsMantleChunkWrite(chunkX, chunkZ); } - private boolean allowsRoutedDiscreteGeneration( - Engine current, - GenerationHistoryRuntimeRouter.RuntimeRoute route, - ChunkPos chunkPos, - ChunkAccess chunk, - ChunkStatus stage - ) { - if (chunk.getPersistedStatus().isOrAfter(stage)) { - return false; - } - if (route == null) { - return allowsGenerationHistoryBypass(current); - } - return NativeGenerationWriteGuard.allowsPendingStage(current, chunk, stage) - || current.getComplex().allowsNewGenerationChunk(chunkPos.x(), chunkPos.z()); - } - private void bindGenerationRuntimeRetirementListener(Engine current) { if (!(current instanceof IrisEngine irisEngine)) { unbindGenerationRuntimeRetirementListener(); @@ -1495,10 +898,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { } private void retireGenerationRuntimeCaches(int runtimeIdentity) { - terrainHeights.evictRuntime(runtimeIdentity); - structureBiomeSource.evictRuntime(runtimeIdentity); + nativeGenerator.evictRuntime(runtimeIdentity); importedFeatures.evictRuntime(runtimeIdentity); - spawnTables.evictRuntime(runtimeIdentity); } private GenerationHistoryRuntimeRouter.RuntimeRoute openHistoryRoute( @@ -1571,9 +972,251 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator { } } + public Integer worldCheckStructureShift(String key, int chunkX, int chunkZ) { + return nativeGenerator.worldCheckStructureShift(key, chunkX, chunkZ); + } + @Override - public void addDebugScreenInfo(List info, RandomState randomState, BlockPos pos) { + public NativeGeneratorHandle nativeGenerator() { + return nativeGenerator; + } + + @Override + public NativeModdedBiomePolicy biomePolicy(NativeBiomeSourceAccess source) { + ModdedBiomePolicy policy = new ModdedBiomePolicy<>(source); + policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(this::structureEngineOrNull, this::awaitStructureEngine, + this::allowsGenerationHistoryBypass, this::configuredStructureBiomeKeys, + IrisModdedChunkGenerator::retainedBiomeKeys, this::visibleBiomeSeed)); + return policy; + } + + @Override + public FeatureStage featureStage(NativeFeatureBiomeSource source) { + importedFeatures = new ModdedImportedFeatureStage(source); + importedFeatures.bind(this); + return importedFeatures; + } + + @Override + public NativeModdedStructureStage.Policy structures() { + return new ModdedNativeStructurePolicy(this); + } + + @Override + public NativeSpawnBiomePolicy spawns() { + return new ModdedSpawnBiomePolicy(this::engine, this::requireGenerationLease); + } + + @Override + public NativeModdedServer server() { + return ModdedEngineBootstrap.currentServer(); + } + + @Override + public Engine current() { + return engine(); + } + + @Override + public Engine current(NativeWorld world) { + return engine(world); + } + + @Override + public Engine current(String worldKey) { + return engine(worldKey); + } + + @Override + public Engine queryCurrent(String operation) { + return requireDataQueryEngine(operation); + } + + @Override + public NativeGenerationRoute route(Engine current, Position position, String operation) { + return openHistoryRoute(current, position.x(), position.z(), operation); + } + + @Override + public NativeGenerationScope coordinateScope(Engine current, Position position, String operation) { + return openHistoryCoordinateScope(current, position.x(), position.z(), operation); + } + + @Override + public NativeGenerationLease lease(Engine current, String operation) { + return requireGenerationLease(current, operation); + } + + @Override + public NativeGenerationScope context(Engine current, long sessionId) { + return IrisContext.open(current, sessionId, null); + } + + @Override + public NativeChunkWritePolicy chunkWrites(Engine current) { + return current.getComplex()::allowsMantleChunkWrite; + } + + @Override + public boolean allowsNewGeneration(Engine current, int x, int z) { + return current.getComplex().allowsNewGenerationChunk(x, z); + } + + @Override + public boolean historyBypass(Engine current) { + return allowsGenerationHistoryBypass(current); + } + + @Override + public boolean stacked(Engine current) { + return current.getDimensionStackContext() != null; + } + + @Override + public int runtimeId(Engine current) { + return current.getCacheID(); + } + + @Override + public int terrainHeight(Engine current, int x, int z, boolean ignoreFluid, boolean host) { + return host && current.getDimensionStackContext() != null + ? Engine.hostHeight(current, x, z, ignoreFluid) + : current.getHeight(x, z, ignoreFluid); + } + + @Override + public NativeBlockColumn resolvedColumn(Engine current, int x, int z) { + return current.getComplex().resolvedTerrainColumn(x, z).map(TerrainBoundarySignature::geometry).orElse(null); + } + + @Override + public String placementKey(String key) { + return TerrainNativeBlockKeys.placementKey(key); + } + + @Override + public Terrain generate(Engine current, int chunkX, int chunkZ) throws Exception { + if (announced.compareAndSet(false, true)) { + ModdedIrisLog.info("Iris generating {} through IrisModdedChunkGenerator (dim={} first chunk {},{})", + dimensionKey, current.getDimension().getLoadKey(), chunkX, chunkZ); + } + int minimumY = current.getMinHeight(); + int height = current.getMaxHeight() - minimumY; + ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, IrisPlatforms.get().registries().air()); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); + current.generate(chunkX << 4, chunkZ << 4, blocks, biomes, false); + return new Terrain(minimumY, height, blocks); + } + + @Override + public void generated(Engine current, int x, int z) { + ModdedWorldManager.enqueueGenerated(current, x, z); + } + + @Override + public void generating(int x, int z) { + lastChunkGenAt = System.currentTimeMillis(); + ModdedIrisLog.debug("Iris generating chunk {},{}", x, z); + } + + @Override + public RuntimeException generationFailure(Engine current, Position chunk, Throwable failure) { + if (failure instanceof GenerationSessionException session && (current.isClosing() || session.isExpectedTeardown())) { + ModdedIrisLog.debug("Iris chunk {},{} skipped: engine sealed for hotload/teardown", chunk.x(), chunk.z()); + return new IllegalStateException("Iris chunk generation was rejected during an engine transition.", failure); + } + ModdedIrisLog.error("Iris failed to generate chunk {},{}", chunk.x(), chunk.z(), failure); + return new IllegalStateException("Iris generation failed for chunk " + chunk.x() + "," + chunk.z(), failure); + } + + @Override + public byte[] terrainReceipt(NativeGenerationRoute nativeRoute) throws IOException { + GenerationHistoryRuntimeRouter.RuntimeRoute route = (GenerationHistoryRuntimeRouter.RuntimeRoute) nativeRoute; + return ModdedNativeTerrainReceipts.encode(route); + } + + @Override + public long structureActivation(NativeGenerationRoute route) { + return ((GenerationHistoryRuntimeRouter.RuntimeRoute) route).activation().activationId(); + } + + @Override + public NativeGenerationLease terrainLease(Engine current) throws GenerationSessionException { + return current.acquireGenerationLease("modded_chunk_pipeline"); + } + + @Override + public NativeGenerationLease queryLease(Engine current, String operation) throws GenerationSessionException { + return current.acquireGenerationLease("modded_" + operation.replace(' ', '_')); + } + + @Override + public RuntimeException queryFailure(String operation, Throwable failure) { + if (failure instanceof GenerationSessionException) { + return new IllegalStateException("Iris " + operation + " query could not acquire its engine runtime.", failure); + } + if (failure instanceof RuntimeException runtime) { + return runtime; + } + if (failure instanceof Error error) { + throw error; + } + return new IllegalStateException("Iris " + operation + " query failed.", failure); + } + + @Override + public void addDebugInformation(List info) { info.add("Iris dimension: " + dimensionKey); } + @Override + public StructureInjectionPolicy injectionPolicy(Engine current) { + return new IrisStructurePolicy(current); + } + + @Override + public boolean parallelChunkSystem() { + return ModdedGenPool.parallelChunkSystem(); + } + + @Override + public Executor executor() { + return ModdedGenPool.pool(); + } + + @Override + public NativeSpawnSelection spawnSelection(Engine current, SpawnQuery query) { + return NativeBiomeSpawnSelection.at(current, query.x(), query.y(), query.z(), query.visibleBiomeKey()); + } + + @Override + public StructureReferencePolicy structurePolicy(Engine current) { + return new IrisStructurePolicy(current); + } + + @Override + public StructureFrequencyControl structureFrequencies() { + return configuredImportedStructures(); + } + + @Override + public int depth() { + return getGenDepth(); + } + + @Override + public int seaLevel() { + return getSeaLevel(); + } + + @Override + public int minimumY() { + return getMinY(); + } + + @Override + public int spawnHeight(int minimumY, int height) { + return ModdedDimensionMetadata.clampSpawnHeight(minimumY, height); + } + } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/MainWorldService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/MainWorldService.java index b382e21ba..663a0e6fc 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/MainWorldService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/MainWorldService.java @@ -18,6 +18,9 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeServerWorldLayout; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeServerWorldLayout.MissingWorldRootException; + import art.arcane.iris.generation.runtime.IrisEngineMantle; import java.io.IOException; @@ -27,28 +30,15 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.UUID; public final class MainWorldService { private static final String MARKER_NAME = "mainworld.pending"; - private static final String PROPERTIES_NAME = "server.properties"; /** * Distinct exit status for the staged main-world restart, so a wrapper can tell it apart from a clean * operator stop (0) and from a crash. 64 is the conventional first application-defined status. */ private static final int AUTO_RESTART_EXIT_STATUS = 64; - private static final String[] VANILLA_DIMENSION_FOLDERS = { - "region", - "entities", - "poi", - IrisEngineMantle.STORAGE_FOLDER_NAME, - "dimensions/minecraft/overworld", - "dimensions/minecraft/the_nether", - "dimensions/minecraft/the_end", - "DIM-1", - "DIM1" - }; private MainWorldService() { } @@ -72,9 +62,9 @@ public final class MainWorldService { if (instanceRoot == null) { return; } - Path properties = instanceRoot.resolve(PROPERTIES_NAME); + Path properties = instanceRoot.resolve(NativeServerWorldLayout.SERVER_PROPERTIES); String target = presetIdFor(pack); - String currentType = readProperty(properties, "level-type"); + String currentType = readProperty(properties, NativeServerWorldLayout.LEVEL_TYPE); if (!target.equals(currentType)) { writeLevelProperties(properties, target, config.mainWorldSeed()); markPending(); @@ -125,7 +115,7 @@ public final class MainWorldService { return false; } try { - writeLevelProperties(instanceRoot.resolve(PROPERTIES_NAME), presetIdFor(packRef), seed); + writeLevelProperties(instanceRoot.resolve(NativeServerWorldLayout.SERVER_PROPERTIES), presetIdFor(packRef), seed); markPending(); return true; } catch (IOException e) { @@ -143,8 +133,8 @@ public final class MainWorldService { } static Path configuredWorldRootIfPresent() throws IOException { - Path instanceRoot = Path.of("").toAbsolutePath().normalize(); - Path properties = instanceRoot.resolve(PROPERTIES_NAME); + Path instanceRoot = NativeServerWorldLayout.instanceRoot(); + Path properties = instanceRoot.resolve(NativeServerWorldLayout.SERVER_PROPERTIES); if (!Files.isRegularFile(properties)) { return null; } @@ -156,19 +146,19 @@ public final class MainWorldService { } /** - * net.minecraft.server.Main reads server.properties as Paths.get("server.properties"), so the authoritative + * The dedicated server reads its properties from the working directory, so the authoritative * instance root is the JVM working directory - not configDir().getParent(), which points somewhere else * entirely whenever the loader config tree is relocated (-Dfabric.configDir, a shared config mount, a * launcher that starts the server from another directory). Refuse loudly rather than write or move files * against a guessed root: every caller treats null as "not a dedicated instance we may touch". */ private static Path verifiedInstanceRoot(String operation) { - Path workingDirectory = Path.of("").toAbsolutePath().normalize(); - if (Files.isRegularFile(workingDirectory.resolve(PROPERTIES_NAME))) { + Path workingDirectory = NativeServerWorldLayout.instanceRoot(); + if (Files.isRegularFile(workingDirectory.resolve(NativeServerWorldLayout.SERVER_PROPERTIES))) { return workingDirectory; } - ModdedIrisLog.error("Iris refuses to {}: no {} in the server working directory {}", operation, PROPERTIES_NAME, workingDirectory); - ModdedIrisLog.error("Iris only edits main-world properties in the directory the dedicated server reads {} from, and it moves no world data outside it. Start the server from its instance directory, or clear mainWorldPack in irisworldgen/modded.json.", PROPERTIES_NAME); + ModdedIrisLog.error("Iris refuses to {}: no {} in the server working directory {}", operation, NativeServerWorldLayout.SERVER_PROPERTIES, workingDirectory); + ModdedIrisLog.error("Iris only edits main-world properties in the directory the dedicated server reads {} from, and it moves no world data outside it. Start the server from its instance directory, or clear mainWorldPack in irisworldgen/modded.json.", NativeServerWorldLayout.SERVER_PROPERTIES); return null; } @@ -213,12 +203,12 @@ public final class MainWorldService { List lines = Files.isRegularFile(properties) ? new ArrayList<>(Files.readAllLines(properties, StandardCharsets.UTF_8)) : new ArrayList<>(); - setProperty(lines, "level-type", escape(target)); + setProperty(lines, NativeServerWorldLayout.LEVEL_TYPE, escape(target)); if (seed != 0L) { - setProperty(lines, "level-seed", Long.toString(seed)); + setProperty(lines, NativeServerWorldLayout.LEVEL_SEED, Long.toString(seed)); } - Path temp = properties.resolveSibling(PROPERTIES_NAME + ".iris-tmp-" + UUID.randomUUID()); + Path temp = properties.resolveSibling(NativeServerWorldLayout.SERVER_PROPERTIES + ".iris-tmp-" + UUID.randomUUID()); Files.write(temp, lines, StandardCharsets.UTF_8); try { Files.move(temp, properties, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); @@ -239,102 +229,25 @@ public final class MainWorldService { } /** - * Mirrors net.minecraft.server.Main world resolution: the universe root is --universe (default the working - * directory) and the world folder name is --world, falling back to the level-name property. + * Resolves the configured world using the dedicated server launch options and level-name property. */ private static Path resolveWorldRoot(Path instanceRoot, Path properties) throws IOException { - List arguments = processArguments(); - Path universe = universeRoot(instanceRoot, commandLineOption(arguments, "universe")); - String levelName = firstNonBlank(commandLineOption(arguments, "world"), - firstNonBlank(readProperty(properties, "level-name"), "world")); - return resolveWorldRoot(universe, levelName); - } - - static Path universeRoot(Path instanceRoot, String universeOption) { - return (universeOption == null || universeOption.isBlank() - ? instanceRoot - : instanceRoot.resolve(universeOption)).toAbsolutePath().normalize(); - } - - static Path resolveWorldRoot(Path universe, String levelName) throws IOException { - if (!Files.isDirectory(universe)) { - throw new MissingWorldRootException("Server universe directory does not exist: " + universe, universe); - } - Path worldRoot = universe.resolve(levelName).toAbsolutePath().normalize(); - if (worldRoot.equals(universe) || !worldRoot.startsWith(universe)) { - throw new IOException("Unsafe world name outside the server universe " + universe + ": " + levelName); - } - if (!Files.isDirectory(worldRoot)) { - throw new MissingWorldRootException("Server world directory does not exist: " + worldRoot, worldRoot); - } - return worldRoot; - } - - /** - * A universe or world directory that is simply absent. Separated from every other IO failure so - * reconciliation can treat it as nothing-to-quarantine instead of refusing startup; an unsafe world name - * stays a plain IOException and still refuses. - */ - static final class MissingWorldRootException extends IOException { - private static final long serialVersionUID = 1L; - - private final Path path; - - MissingWorldRootException(String message, Path path) { - super(message); - this.path = path; - } - - Path path() { - return path; - } - } - - /** - * Best effort read of a dedicated-server launch option. The parsed OptionSet is not reachable from mod - * bootstrap, so read the process arguments; when they are unavailable we fall back to the vanilla defaults, - * which is what the unpatched code assumed unconditionally. - */ - static String commandLineOption(List arguments, String name) { - String flag = "--" + name; - for (int index = 0; index < arguments.size(); index++) { - String argument = arguments.get(index); - if (argument.equals(flag)) { - return index + 1 < arguments.size() ? arguments.get(index + 1) : null; - } - if (argument.startsWith(flag + "=")) { - return argument.substring(flag.length() + 1); - } - } - return null; - } - - private static List processArguments() { - try { - Optional arguments = ProcessHandle.current().info().arguments(); - if (arguments.isPresent() && arguments.get().length > 0) { - return List.of(arguments.get()); - } - } catch (RuntimeException unavailable) { - ModdedIrisLog.debug("Iris could not read the process arguments", unavailable); - } - // Whitespace split only: sun.java.command is a flattened string with no quoting information, so a - // --universe or --world value containing spaces cannot be recovered from it. Deliberately not parsed - // further - a half-correct quote parser would hand world resolution a wrong directory, and the missing - // world root path already degrades to the vanilla defaults instead of failing the boot. - String command = System.getProperty("sun.java.command"); - if (command == null || command.isBlank()) { - return List.of(); - } - return List.of(command.trim().split("\\s+")); + return NativeServerWorldLayout.configuredWorldRoot(instanceRoot, + readProperty(properties, NativeServerWorldLayout.LEVEL_NAME), + NativeServerWorldLayout.processArguments(failure -> ModdedIrisLog.debug("Iris could not read the process arguments", failure))); } private static Path quarantineVanillaDimensions(Path worldRoot) throws IOException { Path recovery = markerFile().getParent().resolve("mainworld-recovery-" + UUID.randomUUID()); List moved = new ArrayList<>(); - moveToRecovery(worldRoot, worldRoot.resolve("level.dat"), recovery, moved); - moveToRecovery(worldRoot, worldRoot.resolve("level.dat_old"), recovery, moved); - for (String folder : VANILLA_DIMENSION_FOLDERS) { + for (String file : NativeServerWorldLayout.metadataFiles()) { + moveToRecovery(worldRoot, worldRoot.resolve(file), recovery, moved); + } + for (String folder : NativeServerWorldLayout.primaryFolders()) { + moveToRecovery(worldRoot, worldRoot.resolve(folder), recovery, moved); + } + moveToRecovery(worldRoot, worldRoot.resolve(IrisEngineMantle.STORAGE_FOLDER_NAME), recovery, moved); + for (String folder : NativeServerWorldLayout.dimensionFolders()) { moveToRecovery(worldRoot, worldRoot.resolve(folder), recovery, moved); } if (moved.isEmpty()) { @@ -378,7 +291,4 @@ public final class MainWorldService { return value.replace("\\:", ":").replace("\\=", "="); } - private static String firstNonBlank(String value, String fallback) { - return value == null || value.isBlank() ? fallback : value; - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiome.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiome.java deleted file mode 100644 index 18867b739..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiome.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.PlatformBiome; -import net.minecraft.world.level.biome.Biome; - -import java.util.concurrent.ConcurrentHashMap; - -public final class ModdedBiome implements PlatformBiome { - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); - - private final Biome biome; - private final String key; - private final String namespace; - - private ModdedBiome(Biome biome, String key) { - this.biome = biome; - this.key = key; - int colon = key.indexOf(':'); - this.namespace = colon >= 0 ? key.substring(0, colon) : "minecraft"; - } - - public static ModdedBiome of(Biome biome, String key) { - return CACHE.computeIfAbsent(key, (String k) -> new ModdedBiome(biome, k)); - } - - @Override - public String key() { - return key; - } - - @Override - public String namespace() { - return namespace; - } - - @Override - public Object nativeHandle() { - return biome; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedBiomeSource.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java similarity index 73% rename from adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedBiomeSource.java rename to adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java index f0186ee25..daadd5334 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedBiomeSource.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java @@ -18,6 +18,16 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess; +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy; +import art.arcane.volmlib.nativelib.terrain.NativeModdedBiomePolicy; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeResourceKeys; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.function.Function; +import java.util.function.LongSupplier; + + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.DimensionStackContext; import art.arcane.iris.generation.runtime.DimensionStackLayout; @@ -32,26 +42,6 @@ import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.context.IrisContext; import art.arcane.volmlib.util.math.RNG; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.QuartPos; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.util.RandomSource; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Climate; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureSet; import java.io.IOException; import java.util.ArrayList; @@ -67,49 +57,49 @@ import java.util.concurrent.atomic.AtomicReferenceArray; import java.util.function.Predicate; import java.util.stream.Stream; -final class IrisModdedBiomeSource extends BiomeSource { +final class ModdedBiomePolicy implements NativeModdedBiomePolicy { private static final int UNRESOLVED_WARN_KEYS_MAX = 256; private static final int STRONGHOLD_RING_SEARCH_Y = 0; private static final int STRONGHOLD_RING_SEARCH_RADIUS = 112; private static final int STRONGHOLD_RING_SEARCH_QUART_STEP = 4; - private final BiomeSource serializedSource; + private final NativeBiomeSourceAccess access; private final Set warnedUnresolvedBiomeKeys = ConcurrentHashMap.newKeySet(); // Pack generation. Every cache on this source is keyed by it, which is what keeps memoized tables from // surviving a repoint with the previous pack's content. private final AtomicLong packGeneration = new AtomicLong(); - private volatile BiomeHolderTable visibleBiomeCache = new BiomeHolderTable(); - private volatile BiomeHolderTable structureBiomeCache = new BiomeHolderTable(); - private volatile BiomeHolderTable surfaceStructureBiomeCache = new BiomeHolderTable(); - private volatile IrisModdedChunkGenerator generator; + private volatile BiomeHolderTable visibleBiomeCache = new BiomeHolderTable<>(); + private volatile BiomeHolderTable structureBiomeCache = new BiomeHolderTable<>(); + private volatile BiomeHolderTable surfaceStructureBiomeCache = new BiomeHolderTable<>(); + private volatile RuntimeCallbacks runtime; private volatile Set possibleStructureBiomeKeys; private volatile BiomeKeySets biomeKeySets; - private volatile PossibleBiomes possibleBiomesCache; + private volatile PossibleBiomes possibleBiomesCache; - IrisModdedBiomeSource(BiomeSource serializedSource) { - this.serializedSource = serializedSource; + ModdedBiomePolicy(NativeBiomeSourceAccess access) { + this.access = Objects.requireNonNull(access, "access"); } - void bind(IrisModdedChunkGenerator generator) { - this.generator = generator; + void bind(RuntimeCallbacks runtime) { + this.runtime = runtime; } - void clearCaches() { + public void clearCaches() { // Republish empty tables instead of iterating: a repoint must not walk hundreds of thousands of slots // on the calling thread, and every reader is a pure function of the key so a lost entry is only a miss. // An empty table allocates no slot array, and a reader that captured the previous table writes its // in-flight value there, which is what keeps a pre-repoint holder from ever landing in the new table. packGeneration.incrementAndGet(); - visibleBiomeCache = new BiomeHolderTable(); - structureBiomeCache = new BiomeHolderTable(); - surfaceStructureBiomeCache = new BiomeHolderTable(); + visibleBiomeCache = new BiomeHolderTable<>(); + structureBiomeCache = new BiomeHolderTable<>(); + surfaceStructureBiomeCache = new BiomeHolderTable<>(); warnedUnresolvedBiomeKeys.clear(); possibleStructureBiomeKeys = null; biomeKeySets = null; possibleBiomesCache = null; } - void evictRuntime(int runtimeIdentity) { + public void evictRuntime(int runtimeIdentity) { visibleBiomeCache.evictRuntime(runtimeIdentity); structureBiomeCache.evictRuntime(runtimeIdentity); surfaceStructureBiomeCache.evictRuntime(runtimeIdentity); @@ -119,62 +109,26 @@ final class IrisModdedBiomeSource extends BiomeSource { * Pack generation counter. Platform code that memoizes anything derived from this source (the imported * feature table) keys its memo on this value so {@code repoint} cannot leave stale content behind. */ - long packGeneration() { + public long packGeneration() { return packGeneration.get(); } - BiomeSource forStructureState(HolderLookup structureSets) { - LinkedHashSet> possible = new LinkedHashSet<>(); - Registry registry = biomeRegistry(); - if (registry == null) { - throw new IllegalStateException("Iris cannot create structure state without the biome registry"); - } - Set generatedBiomeKeys = requireConfiguredStructureBiomeKeys(exactStructureBiomeKeys()); - Set missingBiomeKeys = new LinkedHashSet<>(generatedBiomeKeys); - missingBiomeKeys.removeAll(registeredBiomeKeys(registry)); - if (!missingBiomeKeys.isEmpty()) { - throw new IllegalStateException("Iris structure biomes are not registered: " + missingBiomeKeys); - } - structureSets.listElements().forEach((Holder.Reference structureSet) -> { - for (StructureSet.StructureSelectionEntry entry : structureSet.value().structures()) { - for (Holder biome : entry.structure().value().biomes()) { - String key = holderKey(biome); - if (isGeneratedBiomeKey(key, generatedBiomeKeys)) { - possible.add(biome); - } - } - } - }); - StructureStateBiomeSource source = new StructureStateBiomeSource(this, Set.copyOf(possible)); - return source; - } - - @Override - protected MapCodec codec() { - throw new UnsupportedOperationException("IrisModdedBiomeSource is serialized through IrisModdedChunkGenerator"); - } - - @Override - protected Stream> collectPossibleBiomes() { - return resolvePossibleBiomes().ordered().stream(); - } - /** - * Overridden because {@link BiomeSource#possibleBiomes()} memoizes its answer for the lifetime of the + * Overridden because {@code BiomeSource.possibleBiomes()} memoizes its answer for the lifetime of the * instance, and this source outlives a {@code repoint} to a different pack. The returned set keeps * biome-registry iteration order: vanilla builds its feature-per-step table with * {@code List.copyOf(possibleBiomes())}, and FeatureSorter's cycle detection walks that list, so an * unordered set makes cycle detection depend on JVM hash order. */ @Override - public Set> possibleBiomes() { + public Set possibleBiomes() { return resolvePossibleBiomes().set(); } /** * Registry-ordered view of {@link #possibleBiomes()} for platform code that has to build a feature table. */ - List> orderedPossibleBiomes() { + public List orderedPossibleBiomes() { return resolvePossibleBiomes().ordered(); } @@ -184,8 +138,8 @@ final class IrisModdedBiomeSource extends BiomeSource { * structure derivative is rewritten away from that derivative, so the derivative itself is not always one of * the biomes this source claims. Null when the key is unknown or the registry is not up yet. */ - Holder registeredBiome(String key) { - Registry registry = biomeRegistry(); + public H registeredBiome(String key) { + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); return registry == null ? null : resolveHolder(registry, key); } @@ -194,32 +148,32 @@ final class IrisModdedBiomeSource extends BiomeSource { * generator takes its monitor before invalidating this cache. A lock here would close that cycle. Two * threads racing only duplicate idempotent work. */ - private PossibleBiomes resolvePossibleBiomes() { + private PossibleBiomes resolvePossibleBiomes() { long generation = packGeneration.get(); - PossibleBiomes cached = possibleBiomesCache; + PossibleBiomes cached = possibleBiomesCache; if (cached != null && cached.generation() == generation) { return cached; } - List> ordered = collectPossibleBiomeHolders(); - PossibleBiomes resolved = new PossibleBiomes(generation, ordered, + List ordered = collectPossibleBiomeHolders(); + PossibleBiomes resolved = new PossibleBiomes<>(generation, ordered, Collections.unmodifiableSet(new LinkedHashSet<>(ordered))); possibleBiomesCache = resolved; return resolved; } - private List> collectPossibleBiomeHolders() { + private List collectPossibleBiomeHolders() { BiomeKeySets keys = biomeKeySets(); LinkedHashSet generatedBiomeKeys = new LinkedHashSet<>( requireConfiguredStructureBiomeKeys(keys.structureRequired())); generatedBiomeKeys.addAll(keys.visibleRequired()); LinkedHashSet visibleBiomeKeys = new LinkedHashSet<>(keys.visibleOnly()); - Registry registry = biomeRegistry(); - LinkedHashSet> possible = new LinkedHashSet<>(); + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); + LinkedHashSet possible = new LinkedHashSet<>(); if (registry == null) { if (keys.visibleFallbackPossible()) { visibleBiomeKeys.add("minecraft:plains"); } - for (Holder biome : serializedSource.possibleBiomes()) { + for (H biome : access.serializedBiomes()) { String key = holderKey(biome); if (isGeneratedBiomeKey(key, generatedBiomeKeys) || isGeneratedBiomeKey(key, visibleBiomeKeys)) { @@ -237,7 +191,7 @@ final class IrisModdedBiomeSource extends BiomeSource { visibleBiomeKeys.add("minecraft:plains"); } // Registry-ordered, never a hash-ordered walk: see possibleBiomes(). - registry.listElements().forEach((Holder.Reference reference) -> { + registry.forEach((H reference) -> { String key = holderKey(reference); if (isGeneratedBiomeKey(key, generatedBiomeKeys) || isGeneratedBiomeKey(key, visibleBiomeKeys)) { @@ -254,15 +208,15 @@ final class IrisModdedBiomeSource extends BiomeSource { return List.copyOf(possible); } - private List> collectVisibleBiomeHolders() { + private List collectVisibleBiomeHolders() { BiomeKeySets keys = biomeKeySets(); Set visibleBiomeKeys = keys.visibleEmittable(); - Registry registry = biomeRegistry(); + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); boolean includeFallback = registry == null ? keys.visibleFallbackPossible() : needsVisibleFallback(keys, registry); - ArrayList> visible = new ArrayList<>(); - for (Holder biome : resolvePossibleBiomes().ordered()) { + ArrayList visible = new ArrayList<>(); + for (H biome : resolvePossibleBiomes().ordered()) { String key = holderKey(biome); if (isGeneratedBiomeKey(key, visibleBiomeKeys) || includeFallback && "minecraft:plains".equals(key)) { @@ -272,7 +226,7 @@ final class IrisModdedBiomeSource extends BiomeSource { return List.copyOf(visible); } - private static boolean needsVisibleFallback(BiomeKeySets keys, Registry registry) { + private static boolean needsVisibleFallback(BiomeKeySets keys, NativeBiomeSourceAccess.RegistryView registry) { if (keys.visibleFallbackPossible()) { return true; } @@ -288,7 +242,7 @@ final class IrisModdedBiomeSource extends BiomeSource { * Scatter and derivative keys are advisory: a typo there must not stop a world from loading the way a * missing structure biome does, so they are reported once and skipped. */ - private void warnUnregisteredVisibleBiomes(Registry registry, Set visibleBiomeKeys) { + private void warnUnregisteredVisibleBiomes(NativeBiomeSourceAccess.RegistryView registry, Set visibleBiomeKeys) { if (visibleBiomeKeys.isEmpty()) { return; } @@ -307,10 +261,10 @@ final class IrisModdedBiomeSource extends BiomeSource { } @Override - public Holder getNoiseBiome(int quartX, int quartY, int quartZ, Climate.Sampler sampler) { + public H getNoiseBiome(int quartX, int quartY, int quartZ, S sampler) { Engine engine = engineOrNull(); if (engine == null) { - return serializedSource.getNoiseBiome(quartX, quartY, quartZ, sampler); + return access.serializedNoise(quartX, quartY, quartZ, sampler); } GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( engine, quartX << 2, quartZ << 2, "modded_structure_biome"); @@ -329,20 +283,10 @@ final class IrisModdedBiomeSource extends BiomeSource { } @Override - public Pair> findClosestBiome3d( - BlockPos origin, - int searchRadius, - int sampleResolutionHorizontal, - int sampleResolutionVertical, - Predicate> allowed, - Climate.Sampler sampler, - LevelReader level - ) { + public NativeBiomeSourcePolicy.BiomeLocation findClosestBiome3d(ClosestQuery query) { Engine engine = engineOrNull(); if (engine == null) { - return serializedSource.findClosestBiome3d( - origin, searchRadius, sampleResolutionHorizontal, sampleResolutionVertical, - allowed, sampler, level); + return query.unboundFallback().get(); } GenerationSessionLease lease = tryAcquireGenerationLease(engine, "modded_locate_visible_biome"); if (lease == null) { @@ -354,73 +298,57 @@ final class IrisModdedBiomeSource extends BiomeSource { } DimensionStackContext stackContext = engine.getDimensionStackContext(); if (stackContext == null) { - return super.findClosestBiome3d( - origin, searchRadius, sampleResolutionHorizontal, sampleResolutionVertical, - allowed, sampler, level); + return query.defaultFallback().get(); } - Set> candidates = new HashSet<>(); - for (Holder biome : collectVisibleBiomeHolders()) { - if (allowed.test(biome)) { + Set candidates = new HashSet<>(); + for (H biome : collectVisibleBiomeHolders()) { + if (query.allowed().test(biome)) { candidates.add(biome); } } if (candidates.isEmpty()) { return null; } - int sampleRadius = Math.floorDiv(searchRadius, sampleResolutionHorizontal); - int[] sampleYs = Mth.outFromOrigin( - origin.getY(), level.getMinY() + 1, level.getMaxY() + 1, sampleResolutionVertical).toArray(); - for (BlockPos.MutableBlockPos sampleColumn : BlockPos.spiralAround( - BlockPos.ZERO, sampleRadius, Direction.EAST, Direction.SOUTH)) { - int blockX = origin.getX() + sampleColumn.getX() * sampleResolutionHorizontal; - int blockZ = origin.getZ() + sampleColumn.getZ() * sampleResolutionHorizontal; - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - DimensionStackLayout stackLayout = stackContext.sample( - QuartPos.toBlock(quartX), QuartPos.toBlock(quartZ)); - for (int blockY : sampleYs) { - int quartY = QuartPos.fromBlock(blockY); - Holder visibleBiome = resolveVisibleBiome( - engine, quartX, quartY, quartZ, sampler, stackLayout); - if (candidates.contains(visibleBiome)) { - return Pair.of(new BlockPos(blockX, blockY, blockZ), visibleBiome); - } - } - } - return null; + return query.search().search(candidates, (blockX, blockZ) -> { + int quartX = blockX >> 2; + int quartZ = blockZ >> 2; + DimensionStackLayout stackLayout = stackContext.sample(quartX << 2, quartZ << 2); + return blockY -> resolveVisibleBiome(engine, quartX, blockY >> 2, quartZ, + query.sampler(), stackLayout); + }); } } - private Holder getNoiseBiome(Engine engine, int quartX, int quartY, int quartZ, - Climate.Sampler sampler) { + private H getNoiseBiome(Engine engine, int quartX, int quartY, int quartZ, + S sampler) { if (isGuaranteedSurfaceBiome(quartY, engine.getMinHeight())) { return getSurfaceStructureBiome(engine, quartX, quartZ, sampler); } boolean cacheable = isBiomeCacheable( engine, - QuartPos.toBlock(quartX), - QuartPos.toBlock(quartZ) + ((quartX) << 2), + ((quartZ) << 2) ); long key = packNoiseKey(quartX, quartY, quartZ); int runtimeIdentity = engine.getCacheID(); - BiomeHolderTable cache = structureBiomeCache; + BiomeHolderTable cache = structureBiomeCache; if (cacheable) { - Holder cached = cache.get(runtimeIdentity, key); + H cached = cache.get(runtimeIdentity, key); if (cached != null) { return cached; } } - Holder resolved = resolveStructureBiome(engine, quartX, quartY, quartZ, sampler); + H resolved = resolveStructureBiome(engine, quartX, quartY, quartZ, sampler); if (cacheable) { cache.put(runtimeIdentity, key, resolved); } return resolved; } - Holder getVisibleNoiseBiome(int quartX, int quartY, int quartZ, Climate.Sampler sampler) { + public H getVisibleNoiseBiome(int quartX, int quartY, int quartZ, S sampler) { Engine engine = engineOrNull(); if (engine == null) { - return serializedSource.getNoiseBiome(quartX, quartY, quartZ, sampler); + return access.serializedNoise(quartX, quartY, quartZ, sampler); } GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( engine, quartX << 2, quartZ << 2, "modded_visible_biome"); @@ -439,28 +367,28 @@ final class IrisModdedBiomeSource extends BiomeSource { } } - private Holder getVisibleNoiseBiomeWithActiveGenerationLease( + private H getVisibleNoiseBiomeWithActiveGenerationLease( Engine engine, int quartX, int quartY, int quartZ, - Climate.Sampler sampler + S sampler ) { boolean cacheable = isBiomeCacheable( engine, - QuartPos.toBlock(quartX), - QuartPos.toBlock(quartZ) + ((quartX) << 2), + ((quartZ) << 2) ); long key = packNoiseKey(quartX, quartY, quartZ); int runtimeIdentity = engine.getCacheID(); - BiomeHolderTable cache = visibleBiomeCache; + BiomeHolderTable cache = visibleBiomeCache; if (cacheable) { - Holder cached = cache.get(runtimeIdentity, key); + H cached = cache.get(runtimeIdentity, key); if (cached != null) { return cached; } } - Holder resolved = resolveVisibleBiome( + H resolved = resolveVisibleBiome( engine, quartX, quartY, quartZ, sampler, null); if (cacheable) { cache.put(runtimeIdentity, key, resolved); @@ -468,7 +396,7 @@ final class IrisModdedBiomeSource extends BiomeSource { return resolved; } - Holder getVisibleSurfaceBiome(int blockX, int blockZ) { + public H getVisibleSurfaceBiome(int blockX, int blockZ) { Engine engine = engineOrNull(); if (engine == null) { return null; @@ -484,14 +412,14 @@ final class IrisModdedBiomeSource extends BiomeSource { if (!isReady(engine)) { return null; } - Registry registry = biomeRegistry(); + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); if (registry == null) { return null; } - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - int sampleX = QuartPos.toBlock(quartX); - int sampleZ = QuartPos.toBlock(quartZ); + int quartX = ((blockX) >> 2); + int quartZ = ((blockZ) >> 2); + int sampleX = ((quartX) << 2); + int sampleZ = ((quartZ) << 2); boolean cacheable = isBiomeCacheable(engine, sampleX, sampleZ); DimensionStackContext stackContext = engine.getDimensionStackContext(); DimensionStackLayout.Layer layer = stackContext == null @@ -500,17 +428,17 @@ final class IrisModdedBiomeSource extends BiomeSource { int internalY = layer == null ? Engine.hostHeight(engine, sampleX, sampleZ, true) : layer.clippedSurfaceY(); - int quartY = QuartPos.fromBlock(internalY + engine.getMinHeight()); + int quartY = ((internalY + engine.getMinHeight()) >> 2); long key = packNoiseKey(quartX, quartY, quartZ); int runtimeIdentity = engine.getCacheID(); - BiomeHolderTable cache = visibleBiomeCache; + BiomeHolderTable cache = visibleBiomeCache; if (cacheable) { - Holder cached = cache.get(runtimeIdentity, key); + H cached = cache.get(runtimeIdentity, key); if (cached != null) { return cached; } } - Holder resolved = resolveVisibleBiome( + H resolved = resolveVisibleBiome( engine, quartX, quartY, quartZ, null, null); if (cacheable) { cache.put(runtimeIdentity, key, resolved); @@ -520,22 +448,22 @@ final class IrisModdedBiomeSource extends BiomeSource { } } - boolean isStructureReachable(Holder structure) { + public boolean isStructureReachable(Iterable biomes) { Engine engine = engineOrNull(); if (engine == null) { - return isStructureReachable(structure, possibleStructureBiomeKeys()); + return isStructureReachable(biomes, possibleStructureBiomeKeys()); } GenerationSessionLease lease = tryAcquireGenerationLease(engine, "modded_structure_reachability"); if (lease == null) { throw new IllegalStateException("Iris structure reachability was rejected during an engine transition"); } try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - return isStructureReachable(structure, possibleStructureBiomeKeys()); + return isStructureReachable(biomes, possibleStructureBiomeKeys()); } } - private boolean isStructureReachable(Holder structure, Set possible) { - for (Holder biome : structure.value().biomes()) { + private boolean isStructureReachable(Iterable biomes, Set possible) { + for (H biome : biomes) { String key = holderKey(biome); if (isGeneratedBiomeKey(key, possible)) { return true; @@ -545,11 +473,16 @@ final class IrisModdedBiomeSource extends BiomeSource { } @Override - public Set> getBiomesWithin(int x, int y, int z, int radius, Climate.Sampler sampler) { - int minQuartY = QuartPos.fromBlock(y - radius); + public Set getBiomesWithin(NearbyQuery query) { + int x = query.x(); + int y = query.y(); + int z = query.z(); + int radius = query.radius(); + S sampler = query.sampler(); + int minQuartY = ((y - radius) >> 2); Engine engine = engineOrNull(); if (engine == null) { - return super.getBiomesWithin(x, y, z, radius, sampler); + return query.fallback().get(); } GenerationSessionLease lease = tryAcquireGenerationLease(engine, "modded_biomes_within"); if (lease == null) { @@ -568,14 +501,14 @@ final class IrisModdedBiomeSource extends BiomeSource { surfaceQuery = monumentQuery || isGuaranteedSurfaceBiome(minQuartY, engine.getMinHeight()); } if (!surfaceQuery) { - return super.getBiomesWithin(x, y, z, radius, sampler); + return query.fallback().get(); } - int minQuartX = QuartPos.fromBlock(x - radius); - int maxQuartX = QuartPos.fromBlock(x + radius); - int minQuartZ = QuartPos.fromBlock(z - radius); - int maxQuartZ = QuartPos.fromBlock(z + radius); + int minQuartX = ((x - radius) >> 2); + int maxQuartX = ((x + radius) >> 2); + int minQuartZ = ((z - radius) >> 2); + int maxQuartZ = ((z + radius) >> 2); int columns = (maxQuartX - minQuartX + 1) * (maxQuartZ - minQuartZ + 1); - Set> biomes = new HashSet<>(columns); + Set biomes = new HashSet<>(columns); for (int quartZ = minQuartZ; quartZ <= maxQuartZ; quartZ++) { for (int quartX = minQuartX; quartX <= maxQuartX; quartX++) { try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( @@ -589,23 +522,23 @@ final class IrisModdedBiomeSource extends BiomeSource { } } - private Holder getSurfaceStructureBiome(Engine engine, int quartX, int quartZ, - Climate.Sampler sampler) { + private H getSurfaceStructureBiome(Engine engine, int quartX, int quartZ, + S sampler) { long key = packColumnKey(quartX, quartZ); int runtimeIdentity = engine.getCacheID(); - BiomeHolderTable cache = surfaceStructureBiomeCache; - Holder cached = cache.get(runtimeIdentity, key); + BiomeHolderTable cache = surfaceStructureBiomeCache; + H cached = cache.get(runtimeIdentity, key); if (cached != null) { return cached; } - Holder resolved = resolveSurfaceStructureBiome(engine, quartX, quartZ, sampler); + H resolved = resolveSurfaceStructureBiome(engine, quartX, quartZ, sampler); cache.put(runtimeIdentity, key, resolved); return resolved; } - private Holder resolveSurfaceStructureBiome(Engine engine, int quartX, int quartZ, - Climate.Sampler sampler) { - Registry registry = biomeRegistry(); + private H resolveSurfaceStructureBiome(Engine engine, int quartX, int quartZ, + S sampler) { + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); if (!isReady(engine)) { throw new IllegalStateException("Iris structure biome lookup ran before engine binding"); } @@ -619,7 +552,7 @@ final class IrisModdedBiomeSource extends BiomeSource { throw new IllegalStateException("Iris returned no surface structure biome at quart " + quartX + "," + quartZ); } - Holder resolved = resolveHolder(registry, irisBiome.getStructureDerivativeKey()); + H resolved = resolveHolder(registry, irisBiome.getStructureDerivativeKey()); if (resolved == null) { throw new IllegalStateException("Iris structure biome derivative '" + irisBiome.getStructureDerivativeKey() + "' is not registered at quart " @@ -628,9 +561,9 @@ final class IrisModdedBiomeSource extends BiomeSource { return resolved; } - private Holder resolveStructureBiome(Engine engine, int quartX, int quartY, int quartZ, - Climate.Sampler sampler) { - Registry registry = biomeRegistry(); + private H resolveStructureBiome(Engine engine, int quartX, int quartY, int quartZ, + S sampler) { + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); BiomeResolution resolution = resolveStructureBiomeResolution( engine, quartX, quartY, quartZ); if (resolution == null) { @@ -640,7 +573,7 @@ final class IrisModdedBiomeSource extends BiomeSource { if (registry == null) { throw new IllegalStateException("Iris structure biome lookup has no biome registry"); } - Holder resolved = resolveHolder(registry, resolution.irisBiome().getStructureDerivativeKey()); + H resolved = resolveHolder(registry, resolution.irisBiome().getStructureDerivativeKey()); if (resolved == null) { throw new IllegalStateException("Iris structure biome derivative '" + resolution.irisBiome().getStructureDerivativeKey() + "' is not registered at block " @@ -649,26 +582,26 @@ final class IrisModdedBiomeSource extends BiomeSource { return resolved; } - private Holder resolveVisibleBiome( + private H resolveVisibleBiome( Engine engine, int quartX, int quartY, int quartZ, - Climate.Sampler sampler, + S sampler, DimensionStackLayout stackLayout ) { - Registry registry = biomeRegistry(); + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); BiomeResolution resolution = resolveBiomeResolution( engine, quartX, quartY, quartZ, true, stackLayout); if (resolution == null || registry == null) { - return serializedSource.getNoiseBiome(quartX, quartY, quartZ, sampler); + return access.serializedNoise(quartX, quartY, quartZ, sampler); } String historicalBiomeKey = engine.getComplex().historicalPhysicalBiomeKeyAt( resolution.blockX(), resolution.blockY(), resolution.blockZ()).orElse(null); if (historicalBiomeKey != null) { - Holder historical = resolveHolder(registry, historicalBiomeKey); + H historical = resolveHolder(registry, historicalBiomeKey); if (historical == null) { throw new IllegalStateException("Historical Iris biome '" + historicalBiomeKey + "' is not registered at block " + resolution.blockX() + "," @@ -703,7 +636,7 @@ final class IrisModdedBiomeSource extends BiomeSource { resolution.rng(), engine, resolution.blockX(), resolution.blockY(), resolution.blockZ()); unresolvedBiomeKey = biomeKey; } - Holder resolved = resolveHolder(registry, biomeKey); + H resolved = resolveHolder(registry, biomeKey); return resolved == null ? fallbackBiome(registry, unresolvedBiomeKey, quartX, quartY, quartZ, sampler) : resolved; @@ -820,17 +753,17 @@ final class IrisModdedBiomeSource extends BiomeSource { ); } - private Holder resolveRequiredStructureBiome( + public H requiredStructureBiome( int quartX, int quartY, int quartZ, - Climate.Sampler sampler + S sampler ) { - IrisModdedChunkGenerator current = generator; + RuntimeCallbacks current = runtime; if (current == null) { throw new IllegalStateException("Iris structure biome source is not bound to its generator"); } - Engine engine = current.awaitStructureEngine(); + Engine engine = current.awaitEngine().get(); GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( engine, quartX << 2, quartZ << 2, "modded_structure_state_biome"); try (historyScope) { @@ -894,8 +827,8 @@ final class IrisModdedBiomeSource extends BiomeSource { } private Engine engineOrNull() { - IrisModdedChunkGenerator current = generator; - return current == null ? null : current.structureEngineOrNull(); + RuntimeCallbacks current = runtime; + return current == null ? null : current.currentEngine().get(); } private GenerationHistoryRuntimeRouter.CoordinateScope openHistoryCoordinateScope( @@ -904,8 +837,8 @@ final class IrisModdedBiomeSource extends BiomeSource { int blockZ, String operation ) { - IrisModdedChunkGenerator current = generator; - if (current != null && current.allowsGenerationHistoryBypass(engine)) { + RuntimeCallbacks current = runtime; + if (current != null && current.historyBypass().test(engine)) { return null; } if (!(engine instanceof IrisEngine irisEngine)) { @@ -943,7 +876,7 @@ final class IrisModdedBiomeSource extends BiomeSource { return cached; } Set possible = requireConfiguredStructureBiomeKeys(exactStructureBiomeKeys()); - Registry registry = biomeRegistry(); + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); if (registry == null) { throw new IllegalStateException("Iris cannot resolve structure biomes without the biome registry"); } @@ -980,14 +913,14 @@ final class IrisModdedBiomeSource extends BiomeSource { } private BiomeKeySets collectBiomeKeySets(long generation) { - IrisModdedChunkGenerator current = generator; + RuntimeCallbacks current = runtime; if (current == null) { return new BiomeKeySets(generation, Set.of(), Set.of(), Set.of(), Set.of(), false); } - Engine engine = current.structureEngineOrNull(); + Engine engine = current.currentEngine().get(); if (engine == null) { return new BiomeKeySets( - generation, current.configuredStructureBiomeKeys(), Set.of(), Set.of(), Set.of(), false); + generation, current.configuredStructureKeys().get(), Set.of(), Set.of(), Set.of(), false); } GenerationSessionLease lease = tryAcquireGenerationLease(engine, "modded_structure_biome_keys"); if (lease == null) { @@ -999,7 +932,7 @@ final class IrisModdedBiomeSource extends BiomeSource { } LinkedHashSet structureRequired = new LinkedHashSet<>(collectStructureBiomeKeys( engine.getDimension(), engine.getData())); - structureRequired.addAll(IrisModdedChunkGenerator.retainedBiomeKeys(engine)); + structureRequired.addAll(current.retainedBiomeKeys().apply(engine)); LinkedHashSet visibleRequired = new LinkedHashSet<>(); LinkedHashSet visibleEmittable = new LinkedHashSet<>(); boolean visibleFallbackPossible = false; @@ -1071,10 +1004,10 @@ final class IrisModdedBiomeSource extends BiomeSource { int blockY, int blockZ ) { - IrisModdedChunkGenerator current = generator; + RuntimeCallbacks current = runtime; long worldSeed = current == null ? engine.getWorld().getRawWorldSeed() - : current.visibleBiomeSeed(); + : current.visibleBiomeSeed().getAsLong(); long seed = biomeResolutionSeed(worldSeed, blockX, blockY, blockZ); return new BiomeResolution( irisBiome, @@ -1146,9 +1079,9 @@ final class IrisModdedBiomeSource extends BiomeSource { return engine.getComplex().getTrueBiomeStream().get(blockX, blockZ); } - private static Set registeredBiomeKeys(Registry registry) { + private Set registeredBiomeKeys(NativeBiomeSourceAccess.RegistryView registry) { Set registered = new HashSet<>(); - registry.listElements().forEach((Holder.Reference reference) -> { + registry.forEach(reference -> { String key = holderKey(reference); if (key != null) { registered.add(key); @@ -1157,37 +1090,26 @@ final class IrisModdedBiomeSource extends BiomeSource { return registered; } - private Registry biomeRegistry() { - MinecraftServer server = ModdedEngineBootstrap.currentServer(); - return server == null ? null : server.registryAccess().lookupOrThrow(Registries.BIOME); + private NativeBiomeSourceAccess.RegistryView biomeRegistry() { + return access.registry(); } - private static Holder resolveHolder(Registry registry, String key) { - if (key == null || key.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return null; - } - return registry.get(identifier).>map((Holder.Reference reference) -> reference).orElse(null); + private static H resolveHolder(NativeBiomeSourceAccess.RegistryView registry, String key) { + return registry.lookup(key); } - private static String holderKey(Holder holder) { - return holder.unwrapKey() - .map((ResourceKey key) -> key.identifier().toString().toLowerCase(Locale.ROOT)) - .orElse(null); + private String holderKey(H holder) { + return access.holderKey(holder); } private static String normalizeKey(String key) { - Identifier identifier = key == null ? null : Identifier.tryParse(key); - return identifier == null ? null : identifier.toString().toLowerCase(Locale.ROOT); + return NativeResourceKeys.normalize(key); } - private Holder fallbackBiome(Registry registry, String unresolvedKey, + private H fallbackBiome(NativeBiomeSourceAccess.RegistryView registry, String unresolvedKey, int quartX, int quartY, int quartZ, - Climate.Sampler sampler) { - Holder plains = resolveHolder(registry, "minecraft:plains"); + S sampler) { + H plains = resolveHolder(registry, "minecraft:plains"); warnUnresolvedBiome(unresolvedKey, plains == null ? "the serialized biome source" : "minecraft:plains", quartX, quartY, quartZ); if (plains != null) { @@ -1196,7 +1118,7 @@ final class IrisModdedBiomeSource extends BiomeSource { if (sampler == null) { throw new IllegalStateException("Iris cannot resolve a registered fallback biome"); } - return serializedSource.getNoiseBiome(quartX, quartY, quartZ, sampler); + return access.serializedNoise(quartX, quartY, quartZ, sampler); } private void warnUnresolvedBiome(String unresolvedKey, String fallback, @@ -1249,7 +1171,7 @@ final class IrisModdedBiomeSource extends BiomeSource { ) { } - private record PossibleBiomes(long generation, List> ordered, Set> set) { + private record PossibleBiomes(long generation, List ordered, Set set) { } /** @@ -1264,21 +1186,21 @@ final class IrisModdedBiomeSource extends BiomeSource { * eager arrays cost tens of megabytes of tables that nothing ever reads. An empty table answers every get * with a miss, which is the same answer a cold table gives. */ - private static final class BiomeHolderTable { + private static final class BiomeHolderTable { private static final int SLOTS = 32768; private static final int MASK = SLOTS - 1; private static final int PROBE_LIMIT = 8; - private volatile AtomicReferenceArray table; + private volatile AtomicReferenceArray> table; - Holder get(int runtimeIdentity, long key) { - AtomicReferenceArray slots = table; + H get(int runtimeIdentity, long key) { + AtomicReferenceArray> slots = table; if (slots == null) { return null; } int home = home(runtimeIdentity, key); for (int probe = 0; probe < PROBE_LIMIT; probe++) { - Entry entry = slots.get((home + probe) & MASK); + Entry entry = slots.get((home + probe) & MASK); if (entry == null) { return null; } @@ -1289,16 +1211,16 @@ final class IrisModdedBiomeSource extends BiomeSource { return null; } - void put(int runtimeIdentity, long key, Holder value) { - AtomicReferenceArray slots = table; + void put(int runtimeIdentity, long key, H value) { + AtomicReferenceArray> slots = table; if (slots == null) { slots = install(); } int home = home(runtimeIdentity, key); - Entry entry = new Entry(runtimeIdentity, key, value); + Entry entry = new Entry<>(runtimeIdentity, key, value); for (int probe = 0; probe < PROBE_LIMIT; probe++) { int slot = (home + probe) & MASK; - Entry existing = slots.get(slot); + Entry existing = slots.get(slot); if (existing == null || (existing.runtimeIdentity() == runtimeIdentity && existing.key() == key)) { slots.set(slot, entry); @@ -1308,25 +1230,25 @@ final class IrisModdedBiomeSource extends BiomeSource { slots.set(home, entry); } - void evictRuntime(int runtimeIdentity) { - AtomicReferenceArray slots = table; + public void evictRuntime(int runtimeIdentity) { + AtomicReferenceArray> slots = table; if (slots == null) { return; } for (int index = 0; index < slots.length(); index++) { - Entry entry = slots.get(index); + Entry entry = slots.get(index); if (entry != null && entry.runtimeIdentity() == runtimeIdentity) { slots.compareAndSet(index, entry, null); } } } - private synchronized AtomicReferenceArray install() { - AtomicReferenceArray existing = table; + private synchronized AtomicReferenceArray> install() { + AtomicReferenceArray> existing = table; if (existing != null) { return existing; } - AtomicReferenceArray created = new AtomicReferenceArray<>(SLOTS); + AtomicReferenceArray> created = new AtomicReferenceArray<>(SLOTS); table = created; return created; } @@ -1337,55 +1259,23 @@ final class IrisModdedBiomeSource extends BiomeSource { return (int) ((mixed ^ (mixed >>> 32)) & MASK); } - private record Entry(int runtimeIdentity, long key, Holder value) { + private record Entry(int runtimeIdentity, long key, H value) { } } - private static final class StructureStateBiomeSource extends BiomeSource { - private final IrisModdedBiomeSource delegate; - private final Set> possibleBiomes; - private StructureStateBiomeSource(IrisModdedBiomeSource delegate, Set> possibleBiomes) { - this.delegate = delegate; - this.possibleBiomes = possibleBiomes; - } + @Override + public Set structureBiomeKeys() { + return requireConfiguredStructureBiomeKeys(exactStructureBiomeKeys()); + } - @Override - protected MapCodec codec() { - throw new UnsupportedOperationException("Structure state biome sources are not serializable"); - } + @Override + public int horizontalSearchStep(int blockY, int searchRadius) { + return horizontalBiomeSearchQuartStep(blockY, searchRadius); + } - @Override - protected Stream> collectPossibleBiomes() { - return possibleBiomes.stream(); - } - - @Override - public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { - return delegate.resolveRequiredStructureBiome(x, y, z, sampler); - } - - @Override - public Pair> findBiomeHorizontal( - int x, - int y, - int z, - int searchRadius, - Predicate> allowed, - RandomSource random, - Climate.Sampler sampler - ) { - int quartStep = horizontalBiomeSearchQuartStep(y, searchRadius); - if (quartStep == 1) { - return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler); - } - return super.findBiomeHorizontal( - x, y, z, searchRadius, quartStep, allowed, random, false, sampler); - } - - @Override - public Set> getBiomesWithin(int x, int y, int z, int radius, Climate.Sampler sampler) { - return super.getBiomesWithin(x, y, z, radius, sampler); - } + record RuntimeCallbacks(Supplier currentEngine, Supplier awaitEngine, + Predicate historyBypass, Supplier> configuredStructureKeys, + Function> retainedBiomeKeys, LongSupplier visibleBiomeSeed) { } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomeWriter.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomeWriter.java index 93021b5f1..a52e8b019 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomeWriter.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomeWriter.java @@ -25,75 +25,38 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBiomeRegistry; import art.arcane.iris.spi.PlatformBiomeWriter; import art.arcane.iris.generation.context.IrisContext; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.level.biome.Biome; import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; public final class ModdedBiomeWriter implements PlatformBiomeWriter { private static final String VANILLA_FALLBACK_KEY = "minecraft:plains"; - private static final int MAX_CACHED_IDS = 4096; - /** NUL cannot occur in a pack or registry key, so the composite cache key stays unambiguous. */ - private static final char SCOPE_SEPARATOR = (char) 0; - - private final Supplier server; + private final NativeBiomeRegistry biomes; private final AtomicBoolean serverMissingReported = new AtomicBoolean(); - private volatile RegistryCache cache; - public ModdedBiomeWriter(Supplier server) { - this.server = server; + public ModdedBiomeWriter(NativeBiomeRegistry biomes) { + this.biomes = biomes; } @Override public int biomeIdFor(String key) { - Registry registry = biomeRegistry(); + NativeBiomeRegistry.View registry = biomeRegistry(); if (registry == null) { reportMissingServer("resolve the biome id for '" + key + "'", "using biome id 0"); return 0; } if (key == null) { ModdedIrisLog.warn("Iris biome writer got a null biome key; falling back to {}", VANILLA_FALLBACK_KEY); - return fallbackId(registry); + return registry.fallbackId(); } - RegistryCache cached = cacheFor(registry); - String scoped = scopedBiomeKey(key); - // The scoped key depends on the calling engine, so the same pack key can resolve differently per - // dimension. Cache on both halves; the derivative path below reads the raw key. - String cacheKey = scoped.equals(key) ? key : key + SCOPE_SEPARATOR + scoped; - Integer hit = cached.ids.get(cacheKey); - if (hit != null) { - return hit; - } - - int resolved = resolve(registry, key, scoped); - if (cached.ids.size() < MAX_CACHED_IDS) { - cached.ids.put(cacheKey, resolved); - } - return resolved; - } - - private int resolve(Registry registry, String key, String scoped) { - int direct = idForKey(registry, scoped); - if (direct >= 0) { - return direct; - } - int derivative = idForDerivative(registry, key); - if (derivative >= 0) { - return derivative; - } - return fallbackId(registry); + return registry.resolve(key, scopedBiomeKey(key), this::derivativeKey); } private String scopedBiomeKey(String key) { @@ -110,48 +73,19 @@ public final class ModdedBiomeWriter implements PlatformBiomeWriter { } @Override - public List allBiomes() { - Registry registry = biomeRegistry(); + public List allBiomes() { + NativeBiomeRegistry.View registry = biomeRegistry(); if (registry == null) { reportMissingServer("enumerate the biome registry", "returning no biomes"); return new ArrayList<>(); } - RegistryCache cached = cacheFor(registry); - List snapshot = cached.biomes; - if (snapshot == null) { - List built = new ArrayList<>(); - for (Identifier identifier : registry.keySet()) { - Biome biome = registry.getValue(identifier); - if (biome != null) { - built.add(ModdedBiome.of(biome, identifier.toString())); - } - } - snapshot = List.copyOf(built); - cached.biomes = snapshot; - } - return new ArrayList<>(snapshot); + return registry.allBiomes(); } - private int idForKey(Registry registry, String key) { - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return -1; - } - Biome biome = registry.getValue(identifier); - return biome == null ? -1 : registry.getId(biome); - } - - private int idForDerivative(Registry registry, String key) { + private String derivativeKey(String key) { BiomeOwner owner = findCustomBiomeOwner(key); - if (owner == null) { - return -1; - } - String derivativeKey = owner.biome().getVanillaDerivativeKey(); - if (derivativeKey == null) { - return -1; - } - return idForKey(registry, derivativeKey); + return owner == null ? null : owner.biome().getVanillaDerivativeKey(); } private BiomeOwner findCustomBiomeOwner(String key) { @@ -224,39 +158,12 @@ public final class ModdedBiomeWriter implements PlatformBiomeWriter { return null; } - private int fallbackId(Registry registry) { - int plains = idForKey(registry, VANILLA_FALLBACK_KEY); - return plains >= 0 ? plains : 0; - } - - private Registry biomeRegistry() { - MinecraftServer instance = server.get(); - if (instance == null) { - return null; - } - // Read before write: this runs for every biome id on every generation thread, and an unconditional - // store on a shared cache line is a contended write on the hot path for a flag that is almost always - // already false. - if (serverMissingReported.get()) { + private NativeBiomeRegistry.View biomeRegistry() { + NativeBiomeRegistry.View registry = biomes.current(); + if (registry != null && serverMissingReported.get()) { serverMissingReported.set(false); } - return instance.registryAccess().lookupOrThrow(Registries.BIOME); - } - - /** - * The SPI requires biome writers to cache their registry lookups: biomeIdFor runs from generation threads - * for every biome a pack names, and the derivative path walks every active engine and every custom biome. - * The cache is keyed on the biome Registry instance, which the server replaces whenever datapacks reload, - * so a reload invalidates everything for free. - */ - private RegistryCache cacheFor(Registry registry) { - RegistryCache current = cache; - if (current != null && current.registry == registry) { - return current; - } - RegistryCache replacement = new RegistryCache(registry); - cache = replacement; - return replacement; + return registry; } private void reportMissingServer(String operation, String fallback) { @@ -265,16 +172,6 @@ public final class ModdedBiomeWriter implements PlatformBiomeWriter { } } - private static final class RegistryCache { - private final Registry registry; - private final ConcurrentHashMap ids = new ConcurrentHashMap<>(); - private volatile List biomes; - - private RegistryCache(Registry registry) { - this.registry = registry; - } - } - private record BiomeOwner(IrisDimension dimension, IrisData data, IrisBiome biome, String physicalKey) { } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBreakHandler.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBreakHandler.java index 1bb552c53..c2cef4e7f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBreakHandler.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBreakHandler.java @@ -18,6 +18,17 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockProperties; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockBreakContext; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockDropHooks; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.LootResolver; @@ -28,21 +39,11 @@ import art.arcane.iris.world.loot.IrisBlockDrops; import art.arcane.iris.world.loot.IrisLoot; import art.arcane.iris.world.entity.IrisMarker; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.modded.service.ModdedTreeFellerService; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.matter.MatterMarker; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.entity.EntityTypes; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkGenerator; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -53,12 +54,11 @@ public final class ModdedBlockBreakHandler { private ModdedBlockBreakHandler() { } - public static void prepare( - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState brokenState - ) { + public static void prepare(NativeBlockBreakContext context) { + NativeWorld level = context.world(); + NativeProtocolPlayer player = context.player(); + NativeBlockPoint position = context.position(); + NativeBlockState brokenState = context.state(); if (ModdedTreeFellerService.isBreakProbe()) { return; } @@ -70,10 +70,10 @@ public final class ModdedBlockBreakHandler { // finishPending is the only thing that evicts an unconsumed entry. With no scheduler there is no // sweep, so an entry inserted here would leak for the rest of the server uptime. ModdedIrisLog.debug("Iris skipped block-break provenance at {},{},{}: scheduler unavailable", - position.getX(), position.getY(), position.getZ()); + position.x(), position.y(), position.z()); return; } - BreakKey key = new BreakKey(level, position.asLong()); + BreakKey key = new BreakKey(level, position); ModdedTreeFellerService treeFeller = treeFellerService(); ModdedTreeFellerService.PreparedOrigin preparedOrigin = treeFeller == null ? null @@ -87,73 +87,51 @@ public final class ModdedBlockBreakHandler { PENDING.clear(); } - public static void cancel(ServerLevel level, BlockPos position) { - PENDING.remove(new BreakKey(level, position.asLong())); + public static void cancel(NativeWorld level, NativeBlockPoint position) { + PENDING.remove(new BreakKey(level, position)); } - public static Result complete(ServerLevel level, BlockPos position, BlockState fallbackState) { - BreakKey key = new BreakKey(level, position.asLong()); + public static Result complete(NativeWorld level, NativeBlockPoint position, NativeBlockState fallbackState) { + BreakKey key = new BreakKey(level, position); PendingBreak pending = PENDING.remove(key); PendingBreak resolved = pending == null ? new PendingBreak(fallbackState, null) : pending; return evaluateSafely(level, position, resolved); } - public static Result completePrepared(ServerLevel level, BlockPos position) { - BreakKey key = new BreakKey(level, position.asLong()); + public static Result completePrepared(NativeWorld level, NativeBlockPoint position) { + BreakKey key = new BreakKey(level, position); PendingBreak pending = PENDING.get(key); - if (pending == null || level.getBlockState(position).equals(pending.brokenState())) { + if (pending == null || level.getBlock(position.x(), position.y(), position.z()).equals(pending.brokenState())) { return null; } return PENDING.remove(key, pending) ? evaluateSafely(level, position, pending) : null; } - public static void spawn(ServerLevel level, BlockPos position, KList drops) { - for (ItemStack stack : drops) { - ItemEntity item = createDrop(level, position, stack); - if (item != null) { - level.addFreshEntity(item); - } - } - } - - public static ItemEntity createDrop(ServerLevel level, BlockPos position, ItemStack stack) { - if (stack == null || stack.isEmpty()) { - return null; - } - RandomSource random = level.getRandom(); - double x = position.getX() + 0.5D + Mth.nextDouble(random, -0.25D, 0.25D); - double y = position.getY() + 0.5D + Mth.nextDouble(random, -0.25D, 0.25D) - EntityTypes.ITEM.getHeight() / 2D; - double z = position.getZ() + 0.5D + Mth.nextDouble(random, -0.25D, 0.25D); - ItemEntity item = new ItemEntity(level, x, y, z, stack); - item.setDefaultPickUpDelay(); - return item; - } - - private static void finishPending(BreakKey key, PendingBreak pending, BlockPos position) { + private static void finishPending(BreakKey key, PendingBreak pending, NativeBlockPoint position) { if (!PENDING.remove(key, pending)) { return; } - ServerLevel level = key.level(); - if (level.getBlockState(position).equals(pending.brokenState())) { + NativeWorld level = key.level(); + if (level.getBlock(position.x(), position.y(), position.z()).equals(pending.brokenState())) { return; } Result result = evaluateSafely(level, position, pending); if (!result.routeCombinedDrops(List.of())) { - spawn(level, position, result.drops()); + NativeBlockDropHooks.spawn(level, position, result.drops()); } } - private static Result evaluateSafely(ServerLevel level, BlockPos position, PendingBreak pending) { + private static Result evaluateSafely(NativeWorld level, NativeBlockPoint position, PendingBreak pending) { try { return evaluate(level, position, pending); } catch (Throwable error) { - ModdedIrisLog.error("Iris block-break processing failed at {},{},{} in {}", position.getX(), position.getY(), position.getZ(), - level.dimension().identifier(), error); + ModdedIrisLog.error("Iris block-break processing failed at {},{},{} in {}", position.x(), position.y(), position.z(), + level.name(), error); return Result.empty(); } } - private static Result evaluate(ServerLevel level, BlockPos position, PendingBreak pending) { + private static Result evaluate(NativeWorld level, NativeBlockPoint position, PendingBreak pending) { Engine engine = engineFor(level); if (engine == null || engine.isClosed()) { return Result.empty(); @@ -171,7 +149,7 @@ public final class ModdedBlockBreakHandler { return drops.withRoute(route); } - public static Result evaluateManagedDrops(ServerLevel level, BlockPos position, BlockState brokenState) { + public static Result evaluateManagedDrops(NativeWorld level, NativeBlockPoint position, NativeBlockState brokenState) { Engine engine = engineFor(level); if (engine == null || engine.isClosed()) { return Result.empty(); @@ -179,13 +157,13 @@ public final class ModdedBlockBreakHandler { try { return evaluateDrops(level, position, brokenState, engine); } catch (Throwable error) { - ModdedIrisLog.error("Iris managed block-drop processing failed at {},{},{} in {}", position.getX(), position.getY(), position.getZ(), - level.dimension().identifier(), error); + ModdedIrisLog.error("Iris managed block-drop processing failed at {},{},{} in {}", position.x(), position.y(), position.z(), + level.name(), error); return Result.empty(); } } - public static void completeManagedBreak(ServerLevel level, BlockPos position) { + public static void completeManagedBreak(NativeWorld level, NativeBlockPoint position) { Engine engine = engineFor(level); if (engine != null && !engine.isClosed()) { removeMarker(engine, position); @@ -193,14 +171,14 @@ public final class ModdedBlockBreakHandler { } } - public static void clearPlacedProvenance(ServerLevel level, BlockPos position) { + public static void clearPlacedProvenance(NativeWorld level, NativeBlockPoint position) { completeManagedBreak(level, position); } private static Result evaluateDrops( - ServerLevel level, - BlockPos position, - BlockState brokenState, + NativeWorld level, + NativeBlockPoint position, + NativeBlockState brokenState, Engine engine ) { KList providers = providers(engine, position, brokenState); @@ -208,7 +186,7 @@ public final class ModdedBlockBreakHandler { return Result.empty(); } - KList drops = new KList<>(); + KList drops = new KList<>(); boolean replaceVanillaDrops = false; for (IrisBlockDrops provider : providers) { replaceVanillaDrops |= provider.isReplaceVanillaDrops(); @@ -216,7 +194,7 @@ public final class ModdedBlockBreakHandler { if (!LootResolver.oneIn(RNG.r, loot.getRarity())) { continue; } - ItemStack stack = ModdedItemTranslator.stack(loot, RNG.r, level); + NativeItemStack stack = ModdedItemTranslator.stack(loot, RNG.r, ModdedItemTranslator.context(level)); if (stack != null && !stack.isEmpty()) { drops.add(stack); } @@ -225,9 +203,9 @@ public final class ModdedBlockBreakHandler { return new Result(drops, replaceVanillaDrops, null); } - private static void removeMarker(Engine engine, BlockPos position) { - int mantleY = position.getY() - engine.getMinHeight(); - MatterMarker marker = engine.getMantle().getMantle().get(position.getX(), mantleY, position.getZ(), MatterMarker.class); + private static void removeMarker(Engine engine, NativeBlockPoint position) { + int mantleY = position.y() - engine.getMinHeight(); + MatterMarker marker = engine.getMantle().getMantle().get(position.x(), mantleY, position.z(), MatterMarker.class); if (marker == null) { return; } @@ -237,28 +215,28 @@ public final class ModdedBlockBreakHandler { } IrisMarker configured = engine.getData().getMarkerLoader().load(tag); if (configured == null || configured.isRemoveOnChange()) { - engine.getMantle().getMantle().remove(position.getX(), mantleY, position.getZ(), MatterMarker.class); + engine.getMantle().getMantle().remove(position.x(), mantleY, position.z(), MatterMarker.class); } } - private static void clearTreeProvenance(Engine engine, BlockPos position) { - int mantleY = position.getY() - engine.getMinHeight(); - engine.getMantle().getMantle().remove(position.getX(), mantleY, position.getZ(), String.class); - engine.getMantle().getMantle().remove(position.getX(), mantleY, position.getZ(), TreeBlockMaterial.class); + private static void clearTreeProvenance(Engine engine, NativeBlockPoint position) { + int mantleY = position.y() - engine.getMinHeight(); + engine.getMantle().getMantle().remove(position.x(), mantleY, position.z(), String.class); + engine.getMantle().getMantle().remove(position.x(), mantleY, position.z(), TreeBlockMaterial.class); } - private static KList providers(Engine engine, BlockPos position, BlockState brokenState) { + private static KList providers(Engine engine, NativeBlockPoint position, NativeBlockState brokenState) { KList providers = new KList<>(); IrisData data = engine.getData(); - int relativeY = position.getY() - engine.getMinHeight(); - IrisBiome biome = engine.getBiome(position.getX(), relativeY, position.getZ()); + int relativeY = position.y() - engine.getMinHeight(); + IrisBiome biome = engine.getBiome(position.x(), relativeY, position.z()); if (biome != null) { addMatching(providers, biome.getBlockDrops(), brokenState, data); } if (skipsParents(providers)) { return providers; } - IrisRegion region = engine.getRegion(position.getX(), relativeY, position.getZ()); + IrisRegion region = engine.getRegion(position.x(), relativeY, position.z()); if (region != null) { addMatching(providers, region.getBlockDrops(), brokenState, data); } @@ -266,7 +244,7 @@ public final class ModdedBlockBreakHandler { return providers; } - private static void addMatching(KList matches, KList candidates, BlockState brokenState, IrisData data) { + private static void addMatching(KList matches, KList candidates, NativeBlockState brokenState, IrisData data) { for (IrisBlockDrops candidate : candidates) { if (matches(candidate, brokenState, data)) { matches.add(candidate); @@ -283,26 +261,26 @@ public final class ModdedBlockBreakHandler { return false; } - private static boolean matches(IrisBlockDrops provider, BlockState brokenState, IrisData data) { + private static boolean matches(IrisBlockDrops provider, NativeBlockState brokenState, IrisData data) { for (IrisBlockData configuredBlock : provider.getBlocks()) { - PlatformBlockState resolved = configuredBlock.getBlockData(data); - if (resolved == null || !(resolved.nativeHandle() instanceof BlockState configuredState)) { + NativeBlockState resolved = configuredBlock.getBlockData(data); + if (resolved == null) { continue; } - if (matchesState(configuredState, brokenState, provider.isExactBlocks())) { + if (matchesState(resolved, brokenState, provider.isExactBlocks())) { return true; } } return false; } - static boolean matchesState(BlockState configuredState, BlockState brokenState, boolean exact) { - return exact ? configuredState.equals(brokenState) : configuredState.getBlock() == brokenState.getBlock(); + static boolean matchesState(NativeBlockState configuredState, NativeBlockState brokenState, boolean exact) { + return NativeBlockProperties.matches(configuredState, brokenState, exact); } - public static Engine engineFor(ServerLevel level) { - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (!(generator instanceof IrisModdedChunkGenerator irisGenerator)) { + public static Engine engineFor(NativeWorld level) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { return null; } Engine engine = irisGenerator.engineIfBound(); @@ -312,7 +290,7 @@ public final class ModdedBlockBreakHandler { try { return irisGenerator.commandEngine(); } catch (Throwable error) { - ModdedIrisLog.error("Iris could not resolve the engine for a block break in {}", level.dimension().identifier(), error); + ModdedIrisLog.error("Iris could not resolve the engine for a block break in {}", level.name(), error); return null; } } @@ -321,13 +299,13 @@ public final class ModdedBlockBreakHandler { return ModdedEngineBootstrap.services().service(ModdedTreeFellerService.class); } - public static final class Result { - private final KList drops; + public static final class Result implements NativeBlockDropHooks.PolicyResult { + private final KList drops; private final boolean replaceVanillaDrops; private final ModdedTreeFellerService.OriginDropRoute route; private Result( - KList drops, + KList drops, boolean replaceVanillaDrops, ModdedTreeFellerService.OriginDropRoute route ) { @@ -336,7 +314,7 @@ public final class ModdedBlockBreakHandler { this.route = route; } - public KList drops() { + public KList drops() { return drops; } @@ -344,23 +322,27 @@ public final class ModdedBlockBreakHandler { return replaceVanillaDrops; } - public boolean routeCombinedDrops(Iterable vanillaDrops) { + public boolean hasRoute() { + return route != null; + } + + public boolean routeCombinedDrops(Iterable vanillaDrops) { if (route == null) { return false; } return route.route(combinedDrops(vanillaDrops)); } - public KList combinedDrops(Iterable vanillaDrops) { - KList combined = new KList<>(); + public KList combinedDrops(Iterable vanillaDrops) { + KList combined = new KList<>(); if (!replaceVanillaDrops) { - for (ItemStack stack : vanillaDrops) { + for (NativeItemStack stack : vanillaDrops) { if (stack != null && !stack.isEmpty()) { combined.add(stack.copy()); } } } - for (ItemStack stack : drops) { + for (NativeItemStack stack : drops) { if (stack != null && !stack.isEmpty()) { combined.add(stack.copy()); } @@ -377,11 +359,11 @@ public final class ModdedBlockBreakHandler { } } - private record BreakKey(ServerLevel level, long position) { + private record BreakKey(NativeWorld level, NativeBlockPoint position) { } private record PendingBreak( - BlockState brokenState, + NativeBlockState brokenState, ModdedTreeFellerService.PreparedOrigin preparedOrigin ) { } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBuffer.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBuffer.java index f3e7e75a6..452960a38 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBuffer.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockBuffer.java @@ -18,16 +18,17 @@ package art.arcane.iris.modded; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedGeneratorPolicy; import art.arcane.volmlib.util.hunk.Hunk; -public final class ModdedBlockBuffer implements Hunk { - private final PlatformBlockState[] data; - private final PlatformBlockState air; +public final class ModdedBlockBuffer implements Hunk, NativeModdedGeneratorPolicy.BlockBuffer { + private final NativeBlockState[] data; + private final NativeBlockState air; private final int height; - public ModdedBlockBuffer(int height, PlatformBlockState air) { - this.data = new PlatformBlockState[16 * height * 16]; + public ModdedBlockBuffer(int height, NativeBlockState air) { + this.data = new NativeBlockState[16 * height * 16]; this.air = air; this.height = height; } @@ -44,7 +45,7 @@ public final class ModdedBlockBuffer implements Hunk { * Direct slot read, null when unset — lets writeBlocks pay one index + one array load per * block instead of the isAir + getRaw pair. */ - public PlatformBlockState rawOrNull(int x, int y, int z) { + public NativeBlockState rawOrNull(int x, int y, int z) { return data[index(x, y, z)]; } @@ -64,7 +65,7 @@ public final class ModdedBlockBuffer implements Hunk { } @Override - public void set(int x, int y, int z, PlatformBlockState t) { + public void set(int x, int y, int z, NativeBlockState t) { if (t == null) { return; } @@ -75,7 +76,7 @@ public final class ModdedBlockBuffer implements Hunk { } @Override - public void setRaw(int x, int y, int z, PlatformBlockState t) { + public void setRaw(int x, int y, int z, NativeBlockState t) { if (t == null) { return; } @@ -83,13 +84,13 @@ public final class ModdedBlockBuffer implements Hunk { } @Override - public PlatformBlockState getRaw(int x, int y, int z) { - PlatformBlockState state = data[index(x, y, z)]; + public NativeBlockState getRaw(int x, int y, int z) { + NativeBlockState state = data[index(x, y, z)]; return state == null ? air : state; } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { if (x < 0 || y < 0 || z < 0 || x >= 16 || y >= height || z >= 16) { return air; } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockResolution.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockResolution.java index 93428a58c..f38d4525e 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockResolution.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockResolution.java @@ -1,161 +1,47 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.modded; import art.arcane.iris.configuration.IrisSettings; -import art.arcane.iris.modded.api.ModdedCustomContentRegistry; import art.arcane.iris.modded.api.ModdedBlockData; +import art.arcane.iris.modded.api.ModdedCustomContentRegistry; import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockResolver; import art.arcane.volmlib.util.data.UnresolvedKeyLog; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.commands.arguments.blocks.BlockStateParser; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.EmptyBlockGetter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.LeavesBlock; -import net.minecraft.world.level.block.SpeleothemBlock; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BlockStateProperties; -import net.minecraft.world.level.block.state.properties.SpeleothemThickness; -import net.minecraft.world.level.block.state.properties.Property; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; -public final class ModdedBlockResolution { - private static final Set FOLIAGE = blockSet( - "poppy", "dandelion", "cornflower", "sweet_berry_bush", "crimson_roots", "warped_roots", - "nether_sprouts", "allium", "azure_bluet", "blue_orchid", "oxeye_daisy", "lily_of_the_valley", - "wither_rose", "dark_oak_sapling", "acacia_sapling", "jungle_sapling", "birch_sapling", - "spruce_sapling", "oak_sapling", "orange_tulip", "pink_tulip", "red_tulip", "white_tulip", - "fern", "large_fern", "short_grass", "tall_grass"); - private static final Set DECORANT = decorantSet(); - private static final Set LIT = blockSet( - "glowstone", "amethyst_cluster", "small_amethyst_bud", "medium_amethyst_bud", "large_amethyst_bud", - "end_rod", "soul_sand", "torch", "redstone_torch", "soul_torch", "redstone_wall_torch", "wall_torch", - "soul_wall_torch", "lantern", "candle", "jack_o_lantern", "redstone_lamp", "magma_block", "light", - "shroomlight", "sea_lantern", "soul_lantern", "fire", "soul_fire", "sea_pickle", "brewing_stand", - "redstone_ore"); - private static final Set STORAGE = blockSet( - "chest", "smoker", "trapped_chest", "shulker_box", "white_shulker_box", "orange_shulker_box", - "magenta_shulker_box", "light_blue_shulker_box", "yellow_shulker_box", "lime_shulker_box", - "pink_shulker_box", "gray_shulker_box", "light_gray_shulker_box", "cyan_shulker_box", - "purple_shulker_box", "blue_shulker_box", "brown_shulker_box", "green_shulker_box", - "red_shulker_box", "black_shulker_box", "barrel", "dispenser", "dropper", "hopper", "furnace", - "blast_furnace"); - private static final Set STORAGE_CHEST = storageChestSet(); - private static final Set DEEPSLATE = blockSet( - "deepslate", "deepslate_bricks", "deepslate_brick_slab", "deepslate_brick_stairs", - "deepslate_brick_wall", "deepslate_tile_slab", "deepslate_tiles", "deepslate_tile_stairs", - "deepslate_tile_wall", "cracked_deepslate_tiles", "deepslate_coal_ore", "deepslate_iron_ore", - "deepslate_copper_ore", "deepslate_diamond_ore", "deepslate_emerald_ore", "deepslate_gold_ore", - "deepslate_lapis_ore", "deepslate_redstone_ore"); - private static final Map NORMAL_TO_DEEPSLATE = oreMap(false); - private static final Map DEEPSLATE_TO_NORMAL = oreMap(true); - private static final Set FOLIAGE_PLANTABLE_STATE = blockSet( - "grass_block", "moss_block", "rooted_dirt", "dirt", "coarse_dirt", "podzol"); - private static final Set FOLIAGE_PLANTABLE_MATERIAL = blockSet( - "grass_block", "moss_block", "dirt", "tall_grass", "tall_seagrass", "large_fern", "sunflower", - "peony", "lilac", "rose_bush", "rooted_dirt", "coarse_dirt", "podzol"); - private static final Set PLACE_ONTO_LEAVES = blockSet( - "acacia_leaves", "birch_leaves", "dark_oak_leaves", "jungle_leaves", "oak_leaves", "spruce_leaves"); - private static final BlockState AIR = Blocks.AIR.defaultBlockState(); +public final class ModdedBlockResolution implements NativeBlockResolver.Policy { private static final UnresolvedKeyLog UNRESOLVED = new UnresolvedKeyLog("Iris modded block resolution", 30_000L); private static final int REPORTED_FAILURE_KEYS_MAX = 256; private static final Set REPORTED_FAILURE_KEYS = ConcurrentHashMap.newKeySet(); + public static final NativeBlockResolver BLOCKS = new NativeBlockResolver(new ModdedBlockResolution()); private ModdedBlockResolution() { } - record Parsed(BlockState state, Map, Comparable> properties, String deferredPlacementKey) { - } - - private static Set blockSet(String... ids) { - Set blocks = new HashSet<>(); - for (String id : ids) { - Identifier identifier = Identifier.parse("minecraft:" + id); - if (BuiltInRegistries.BLOCK.containsKey(identifier)) { - blocks.add(BuiltInRegistries.BLOCK.getValue(identifier)); - } + @Override + public ModdedBlockState resolveCustomBlock(String key) { + ModdedBlockData provided = ModdedCustomContentRegistry.resolveBlock(key); + if (provided == null) { + return null; } - return blocks; + return provided.deferredPlacement() + ? provided.state().withDeferredPlacement(key) + : provided.state(); } - private static Set decorantSet() { - Set blocks = blockSet( - "short_grass", "tall_grass", "fern", "large_fern", "cornflower", "sunflower", "chorus_flower", - "poppy", "dandelion", "oxeye_daisy", "orange_tulip", "pink_tulip", "red_tulip", "white_tulip", - "lilac", "dead_bush", "sweet_berry_bush", "rose_bush", "wither_rose", "allium", "blue_orchid", - "lily_of_the_valley", "crimson_fungus", "warped_fungus", "red_mushroom", "brown_mushroom", - "crimson_roots", "azure_bluet", "cactus", "weeping_vines", "weeping_vines_plant", "warped_roots", - "nether_sprouts", "twisting_vines", "twisting_vines_plant", "sugar_cane", "wheat", "potatoes", - "carrots", "beetroots", "nether_wart", "sea_pickle", "seagrass", "tall_seagrass", - "acacia_button", "birch_button", "crimson_button", "dark_oak_button", "jungle_button", - "oak_button", "polished_blackstone_button", "spruce_button", "stone_button", "warped_button", - "torch", "soul_torch", "glow_lichen", "vine", "sculk_vein"); - blocks.addAll(FOLIAGE); - return blocks; + @Override + public boolean preventLeafDecay() { + return IrisSettings.get().getGenerator().isPreventLeafDecay(); } - private static Set storageChestSet() { - Set blocks = new HashSet<>(STORAGE); - blocks.remove(Blocks.SMOKER); - blocks.remove(Blocks.FURNACE); - blocks.remove(Blocks.BLAST_FURNACE); - return blocks; + @Override + public void debug(String message) { + IrisLogging.debug(message); } - private static Map oreMap(boolean deepslateToNormal) { - String[][] pairs = { - {"coal_ore", "deepslate_coal_ore"}, - {"emerald_ore", "deepslate_emerald_ore"}, - {"diamond_ore", "deepslate_diamond_ore"}, - {"copper_ore", "deepslate_copper_ore"}, - {"gold_ore", "deepslate_gold_ore"}, - {"iron_ore", "deepslate_iron_ore"}, - {"lapis_ore", "deepslate_lapis_ore"}, - {"redstone_ore", "deepslate_redstone_ore"} - }; - Map map = new HashMap<>(); - for (String[] pair : pairs) { - Block normal = BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:" + pair[0])); - Block deep = BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:" + pair[1])); - if (deepslateToNormal) { - map.put(deep, normal); - } else { - map.put(normal, deep); - } - } - return map; - } - - private static void reportResolveFailure(String key, Throwable error) { + public void reportError(String key, Throwable error) { String failureKey = key == null ? "" : key; if (!REPORTED_FAILURE_KEYS.add(failureKey)) { return; @@ -166,7 +52,7 @@ public final class ModdedBlockResolution { IrisLogging.reportError("Iris block data '" + failureKey + "' failed to resolve", error); } - private static void warnUnresolved(String key, String message) { + public void warnUnresolved(String key, String message) { if (UNRESOLVED.firstOccurrence(key)) { IrisLogging.warn(message); } @@ -176,383 +62,4 @@ public final class ModdedBlockResolution { } } - public static BlockState airState() { - return AIR; - } - - public static ModdedBlockState getAir() { - return ModdedBlockState.of(AIR, null); - } - - public static ModdedBlockState get(String bdxf) { - Parsed parsed = resolveGet(bdxf); - return stateFrom(parsed); - } - - public static ModdedBlockState getNoCompat(String bdxf) { - Parsed parsed = resolveNoCompat(bdxf); - return stateFrom(parsed); - } - - public static ModdedBlockState getOrNull(String bdxf) { - return getOrNull(bdxf, false); - } - - public static ModdedBlockState getOrNull(String bdxf, boolean warn) { - Parsed parsed = resolveOrNull(bdxf, warn); - return parsed == null ? null : stateFrom(parsed); - } - - private static ModdedBlockState stateFrom(Parsed parsed) { - return parsed.deferredPlacementKey() == null - ? ModdedBlockState.of(parsed.state(), parsed.properties()) - : ModdedBlockState.deferred(parsed.state(), parsed.properties(), parsed.deferredPlacementKey()); - } - - static Parsed resolveGet(String bdxf) { - // Mirrors the Bukkit path: an unknown key warns (rate limited) and falls back to air, instead of - // resolving to air with no output at all. - return resolveNoCompat(bdxf); - } - - static Parsed resolveNoCompat(String bdxf) { - Parsed parsed = resolveOrNull(bdxf, true); - if (parsed != null) { - return parsed; - } - return new Parsed(AIR, null, null); - } - - /** - * Resolves a key, returning null when nothing claims it. Never substitutes air - {@link #resolveNoCompat(String)} - * owns the air fallback. - */ - static Parsed resolveOrNull(String bdxf, boolean warn) { - try { - String bd = bdxf.trim(); - - if (bd.startsWith("minecraft:cauldron[level=")) { - bd = bd.replaceAll("\\Q:cauldron[\\E", ":water_cauldron["); - } - - if (bd.equals("minecraft:grass_path")) { - return new Parsed(Blocks.DIRT_PATH.defaultBlockState(), null, null); - } - - Parsed bdx = parseBlockData(bd, warn); - - if (bdx == null) { - ModdedBlockData provided = ModdedCustomContentRegistry.resolveBlock(bd); - if (provided != null) { - return new Parsed(provided.state(), null, provided.deferredPlacement() ? bd : null); - } - if (warn) { - warnUnresolved(bd, "Unknown Block Data '" + bd + "'"); - } - return null; - } - - return bdx; - } catch (Throwable e) { - reportResolveFailure(bdxf, e); - if (warn) { - warnUnresolved(bdxf, "Unknown Block Data '" + bdxf + "'"); - } - } - - return null; - } - - public static ModdedBlockState strictParse(String key) { - Parsed parsed = parseStrict(key); - return stateFrom(parsed); - } - - private static Parsed parseStrict(String key) { - StringReader reader = new StringReader(key); - BlockStateParser.BlockResult result; - try { - result = BlockStateParser.parseForBlock(BuiltInRegistries.BLOCK, reader, false); - } catch (CommandSyntaxException e) { - throw new IllegalArgumentException("Could not parse data: " + key, e); - } - if (reader.canRead()) { - throw new IllegalArgumentException("Could not parse remainder: " + reader.getRemaining()); - } - return new Parsed(result.blockState(), result.properties(), null); - } - - private static Parsed createBlockData(String s, boolean warn) { - try { - return parseStrict(s); - } catch (IllegalArgumentException e) { - if (s.contains("[")) { - String base = s.split("\\Q[\\E")[0]; - Parsed stripped = createBlockData(base, warn); - if (stripped != null && warn) { - // Dedup on the base block key, not the full state string. UnresolvedKeyLog interns every key it - // is handed into a set it never trims, and a rejected property is usually rejected for every - // value and every combination a pack uses - keying on the state string would intern one entry per - // distinct state (16 levels x 6 facings x ...) for a single authoring mistake. - warnUnresolved("props:" + base, - "Block '" + base + "' rejected state '" + propertySection(s) + "'; using its default state"); - } - return stripped; - } - } - - if (warn) { - warnUnresolved(s, "Can't find block data for " + s); - } - return null; - } - - private static String propertySection(String key) { - int open = key.indexOf('['); - if (open < 0) { - return ""; - } - int close = key.indexOf(']', open); - return close < 0 ? key.substring(open + 1) : key.substring(open + 1, close); - } - - private static Parsed materialBlockData(String ix) { - if (ix.contains("[") || ix.contains(":")) { - return null; - } - Identifier identifier = Identifier.tryParse("minecraft:" + ix.toLowerCase(Locale.ROOT)); - if (identifier == null || !BuiltInRegistries.BLOCK.containsKey(identifier)) { - return null; - } - return new Parsed(BuiltInRegistries.BLOCK.getValue(identifier).defaultBlockState(), null, null); - } - - private static Parsed parseBlockData(String ix, boolean warn) { - try { - Parsed bx = createBlockData(ix.toLowerCase(), warn); - - if (bx == null) { - bx = createBlockData("minecraft:" + ix.toLowerCase(), warn); - } - - if (bx == null) { - bx = materialBlockData(ix); - } - - if (bx == null) { - if (warn) { - warnUnresolved(ix, "Unknown Block Data: " + ix); - } - return null; - } - - if (bx.state().getBlock() instanceof LeavesBlock) { - BlockState mutated = bx.state().setValue(LeavesBlock.PERSISTENT, shouldPreventLeafDecay()); - bx = new Parsed(mutated, bx.properties(), bx.deferredPlacementKey()); - } - - return bx; - } catch (Throwable e) { - String block = ix.contains(":") ? ix.split(":")[1].toLowerCase() : ix.toLowerCase(); - String state = block.contains("[") ? block.split("\\Q[\\E")[1].split("\\Q]\\E")[0] : ""; - Map stateMap = new HashMap<>(); - if (!state.equals("")) { - Arrays.stream(state.split(",")).forEach((String s) -> stateMap.put(s.split("=")[0], s.split("=")[1])); - } - block = block.split("\\Q[\\E")[0]; - - switch (block) { - case "cauldron" -> block = "water_cauldron"; - case "grass_path" -> block = "dirt_path"; - case "concrete" -> block = "white_concrete"; - case "wool" -> block = "white_wool"; - case "beetroots" -> { - if (stateMap.containsKey("age")) { - String updated = stateMap.get("age"); - switch (updated) { - case "7" -> updated = "3"; - case "3", "4", "5" -> updated = "2"; - case "1", "2" -> updated = "1"; - } - stateMap.put("age", updated); - } - } - } - - Map newStates = new HashMap<>(); - for (String key : stateMap.keySet()) { - createBlockData(block + "[" + key + "=" + stateMap.get(key) + "]", false); - newStates.put(key, stateMap.get(key)); - } - - String joined = newStates.entrySet().stream() - .map((Map.Entry entry) -> entry.getKey() + "=" + entry.getValue()) - .collect(Collectors.joining(",")); - if (!joined.equals("")) { - joined = "[" + joined + "]"; - } - String newBlock = block + joined; - IrisLogging.debug("Converting " + ix + " to " + newBlock); - - try { - return createBlockData(newBlock, false); - } catch (Throwable e1) { - IrisLogging.reportError(e1); - } - - return null; - } - } - - private static boolean shouldPreventLeafDecay() { - return IrisSettings.get().getGenerator().isPreventLeafDecay(); - } - - public static BlockState toDeepSlateOre(BlockState block, BlockState ore) { - Block key = ore.getBlock(); - - if (isDeepSlate(block)) { - Block mapped = NORMAL_TO_DEEPSLATE.get(key); - if (mapped != null) { - return mapped.defaultBlockState(); - } - } else { - Block mapped = DEEPSLATE_TO_NORMAL.get(key); - if (mapped != null) { - return mapped.defaultBlockState(); - } - } - - return ore; - } - - public static boolean isDeepSlate(BlockState state) { - return DEEPSLATE.contains(state.getBlock()); - } - - public static boolean isOre(BlockState state) { - return BuiltInRegistries.BLOCK.getKey(state.getBlock()).getPath().endsWith("_ore"); - } - - public static boolean isAir(BlockState state) { - if (state == null) { - return true; - } - Block block = state.getBlock(); - return block == Blocks.AIR || block == Blocks.CAVE_AIR || block == Blocks.VOID_AIR; - } - - public static boolean isSolid(BlockState state) { - if (state == null) { - return false; - } - return isSolidMaterial(state.getBlock()); - } - - private static boolean isSolidMaterial(Block block) { - return block.defaultBlockState().blocksMotion(); - } - - public static boolean isOccluding(BlockState state) { - return state.getBlock().defaultBlockState().isRedstoneConductor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); - } - - public static boolean isFluid(BlockState state) { - Block block = state.getBlock(); - return block == Blocks.WATER || block == Blocks.LAVA; - } - - public static boolean isWater(BlockState state) { - return state.getBlock() == Blocks.WATER; - } - - public static boolean isWaterLogged(BlockState state) { - return state.hasProperty(BlockStateProperties.WATERLOGGED) && state.getValue(BlockStateProperties.WATERLOGGED); - } - - public static boolean isLit(BlockState state) { - return LIT.contains(state.getBlock()); - } - - public static boolean isUpdatable(BlockState state) { - return isStorage(state) - || (state.getBlock() instanceof SpeleothemBlock - && state.getValue(SpeleothemBlock.THICKNESS) == SpeleothemThickness.TIP); - } - - public static boolean isFoliage(BlockState state) { - return FOLIAGE.contains(state.getBlock()); - } - - public static boolean isFoliagePlantable(BlockState state) { - return FOLIAGE_PLANTABLE_STATE.contains(state.getBlock()); - } - - public static boolean isDecorant(BlockState state) { - return DECORANT.contains(state.getBlock()); - } - - public static boolean isStorage(BlockState state) { - return STORAGE.contains(state.getBlock()); - } - - public static boolean isStorageChest(BlockState state) { - return STORAGE_CHEST.contains(state.getBlock()); - } - - public static boolean isVineBlock(BlockState state) { - Block block = state.getBlock(); - return block == Blocks.VINE || block == Blocks.SCULK_VEIN || block == Blocks.GLOW_LICHEN; - } - - public static boolean hasTileEntity(BlockState state) { - return state.getBlock().defaultBlockState().hasBlockEntity(); - } - - public static boolean canPlaceOnto(Block mat, Block onto) { - if (mat == Blocks.SUGAR_CANE) { - return onto == Blocks.SUGAR_CANE || onto == Blocks.GRASS_BLOCK || onto == Blocks.DIRT - || onto == Blocks.COARSE_DIRT || onto == Blocks.PODZOL || onto == Blocks.MYCELIUM - || onto == Blocks.ROOTED_DIRT - || onto == Blocks.MOSS_BLOCK || onto == Blocks.PALE_MOSS_BLOCK || onto == Blocks.MUD - || onto == Blocks.MUDDY_MANGROVE_ROOTS || onto == Blocks.SAND || onto == Blocks.RED_SAND - || onto == Blocks.SUSPICIOUS_SAND; - } - - if (mat == Blocks.CACTUS) { - return onto == Blocks.CACTUS || onto == Blocks.SAND || onto == Blocks.RED_SAND; - } - - if ((onto == Blocks.CRIMSON_NYLIUM || onto == Blocks.WARPED_NYLIUM) - && (mat == Blocks.CRIMSON_FUNGUS || mat == Blocks.CRIMSON_ROOTS - || mat == Blocks.WARPED_FUNGUS || mat == Blocks.WARPED_ROOTS)) { - return true; - } - - if (FOLIAGE.contains(mat)) { - if (!FOLIAGE_PLANTABLE_MATERIAL.contains(onto)) { - return false; - } - } - - if (onto == Blocks.AIR || onto == Blocks.CAVE_AIR || onto == Blocks.VOID_AIR) { - return false; - } - - if (onto == Blocks.GRASS_BLOCK && mat == Blocks.DEAD_BUSH) { - return false; - } - - if (onto == Blocks.DIRT_PATH) { - if (!isSolidMaterial(mat)) { - return false; - } - } - - if (PLACE_ONTO_LEAVES.contains(onto)) { - return isSolidMaterial(mat); - } - - return true; - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockState.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockState.java deleted file mode 100644 index 6733c8c88..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBlockState.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.platform.BlockStateKey; -import art.arcane.iris.spi.PlatformBlockState; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; - -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; - -public final class ModdedBlockState implements PlatformBlockState { - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); - - private final BlockState state; - private final Map, Comparable> parsedProperties; - private final String key; - private final String namespace; - private final String deferredPlacementKey; - private volatile String materialKey; - private volatile Boolean air; - private volatile Boolean solid; - private volatile Boolean occluding; - private volatile Boolean fluid; - private volatile Boolean water; - private volatile Boolean foliage; - private volatile Boolean treeBlock; - private volatile Boolean decorant; - - private ModdedBlockState(BlockState state, Map, Comparable> parsedProperties, String key, String deferredPlacementKey) { - this.state = state; - this.parsedProperties = parsedProperties; - this.key = key; - this.namespace = BlockStateKey.namespace(key); - this.deferredPlacementKey = deferredPlacementKey; - } - - public static ModdedBlockState of(BlockState state, Map, Comparable> parsedProperties) { - String key = serialize(state); - return CACHE.computeIfAbsent(key, (String k) -> new ModdedBlockState(state, parsedProperties, k, null)); - } - - public static ModdedBlockState deferred(BlockState state, Map, Comparable> parsedProperties, String placementKey) { - return new ModdedBlockState(state, parsedProperties, serialize(state), Objects.requireNonNull(placementKey)); - } - - public static String serialize(BlockState state) { - StringBuilder result = new StringBuilder(BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString()); - if (!state.isSingletonState()) { - result.append('['); - result.append(state.getValues() - .map((Property.Value value) -> value.property().getName() + "=" + value.valueName()) - .collect(Collectors.joining(","))); - result.append(']'); - } - return result.toString(); - } - - public BlockState handle() { - return state; - } - - Map, Comparable> parsedProperties() { - return parsedProperties; - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof ModdedBlockState blockState)) { - return false; - } - return state.equals(blockState.state) && Objects.equals(deferredPlacementKey, blockState.deferredPlacementKey); - } - - @Override - public int hashCode() { - return Objects.hash(state, deferredPlacementKey); - } - - @Override - public String key() { - return key; - } - - @Override - public String namespace() { - return namespace; - } - - @Override - public String materialKey() { - String cached = materialKey; - if (cached == null) { - int bracket = key.indexOf('['); - cached = bracket < 0 ? key : key.substring(0, bracket); - materialKey = cached; - } - return cached; - } - - @Override - public boolean isAir() { - Boolean cached = air; - if (cached == null) { - cached = ModdedBlockResolution.isAir(state); - air = cached; - } - return cached; - } - - @Override - public boolean isSolid() { - Boolean cached = solid; - if (cached == null) { - cached = ModdedBlockResolution.isSolid(state); - solid = cached; - } - return cached; - } - - @Override - public boolean isOccluding() { - Boolean cached = occluding; - if (cached == null) { - cached = ModdedBlockResolution.isOccluding(state); - occluding = cached; - } - return cached; - } - - @Override - public boolean isCustom() { - return deferredPlacementKey != null; - } - - @Override - public String deferredPlacementKey() { - return deferredPlacementKey; - } - - @Override - public PlatformBlockState placementBaseState() { - return deferredPlacementKey == null ? this : of(state, parsedProperties); - } - - @Override - public boolean isFluid() { - Boolean cached = fluid; - if (cached == null) { - cached = ModdedBlockResolution.isFluid(state); - fluid = cached; - } - return cached; - } - - @Override - public boolean isWater() { - Boolean cached = water; - if (cached == null) { - cached = ModdedBlockResolution.isWater(state); - water = cached; - } - return cached; - } - - @Override - public boolean isWaterLogged() { - return ModdedBlockResolution.isWaterLogged(state); - } - - @Override - public boolean isLit() { - return ModdedBlockResolution.isLit(state); - } - - @Override - public boolean isUpdatable() { - return ModdedBlockResolution.isUpdatable(state); - } - - @Override - public boolean isFoliage() { - Boolean cached = foliage; - if (cached == null) { - cached = ModdedBlockResolution.isFoliage(state); - foliage = cached; - } - return cached; - } - - @Override - public boolean isTreeBlock() { - Boolean cached = treeBlock; - if (cached == null) { - cached = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - treeBlock = cached; - } - return cached; - } - - @Override - public boolean isFoliagePlantable() { - return ModdedBlockResolution.isFoliagePlantable(state); - } - - @Override - public boolean isDecorant() { - Boolean cached = decorant; - if (cached == null) { - cached = ModdedBlockResolution.isDecorant(state); - decorant = cached; - } - return cached; - } - - @Override - public boolean isStorage() { - return ModdedBlockResolution.isStorage(state); - } - - @Override - public boolean isStorageChest() { - return ModdedBlockResolution.isStorageChest(state); - } - - @Override - public boolean isOre() { - return ModdedBlockResolution.isOre(state); - } - - @Override - public boolean isDeepSlate() { - return ModdedBlockResolution.isDeepSlate(state); - } - - @Override - public boolean isVineBlock() { - return ModdedBlockResolution.isVineBlock(state); - } - - @Override - public boolean canPlaceOnto(PlatformBlockState onto) { - return ModdedBlockResolution.canPlaceOnto(state.getBlock(), ((BlockState) onto.nativeHandle()).getBlock()); - } - - @Override - public boolean matches(PlatformBlockState other) { - if (!(other instanceof ModdedBlockState blockState)) { - return false; - } - if (state.getBlock() != blockState.state.getBlock()) { - return false; - } - if (state.equals(blockState.state)) { - return true; - } - if (blockState.parsedProperties == null) { - return false; - } - BlockState merged = state; - for (Map.Entry, Comparable> entry : blockState.parsedProperties.entrySet()) { - merged = applyProperty(merged, entry.getKey(), entry.getValue()); - } - return merged.equals(state); - } - - @SuppressWarnings("unchecked") - private static > BlockState applyProperty(BlockState state, Property property, Comparable value) { - return state.setValue((Property) property, (T) value); - } - - @Override - public boolean hasTileEntity() { - return ModdedBlockResolution.hasTileEntity(state); - } - - @Override - public PlatformBlockState withProperty(String name, String value) { - String merged = BlockStateKey.withProperty(key, name, value); - ModdedBlockState resolved = ModdedBlockResolution.strictParse(merged); - return withHandle(resolved.handle(), resolved.parsedProperties()); - } - - ModdedBlockState withHandle(BlockState updated, Map, Comparable> properties) { - if (updated == state && properties == parsedProperties) { - return this; - } - return deferredPlacementKey == null - ? of(updated, properties) - : deferred(updated, properties, deferredPlacementKey); - } - - @Override - public Object nativeHandle() { - return state; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDeathLoot.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDeathLoot.java index 487fefd48..101560e9a 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDeathLoot.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDeathLoot.java @@ -18,20 +18,19 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnedEntity; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityLoot; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.world.loot.InventorySlotType; import art.arcane.iris.world.loot.IrisLootTable; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.vehicle.ContainerEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.chunk.ChunkGenerator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -44,30 +43,32 @@ public final class ModdedDeathLoot { private ModdedDeathLoot() { } - public static void bind(Engine engine, Entity entity, KList tableKeys, int spawnX, int spawnY, int spawnZ, RNG rng) { + public static void bind(Engine engine, NativeSpawnedEntity spawned, KList tableKeys, int spawnX, int spawnY, int spawnZ, RNG rng) { + NativeEntityLoot entity = NativeEntityLoot.of(spawned); if (engine == null || entity == null || tableKeys == null || tableKeys.isEmpty()) { return; } LootBinding binding = new LootBinding(rng.getSeed(), spawnX, spawnY, spawnZ, new KList<>(tableKeys)); - if (entity instanceof Mob) { - entity.addTag(encode(binding)); + if (entity.mob()) { + entity.tag(encode(binding)); return; } - if (entity instanceof ContainerEntity container && entity.level() instanceof ServerLevel level) { - fillContainer(engine, level, container, binding); + if (entity.container()) { + fillContainer(engine, entity, binding); return; } - String type = String.valueOf(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType())); + String type = entity.type(); if (WARNED_ENTITY_TYPES.add(type)) { IrisLogging.warn("Iris entity loot: entity type '" + type + "' does not expose a vanilla lootable path"); } } - public static boolean replaceBaseLoot(LivingEntity entity) { - if (entity == null || !(entity.level() instanceof ServerLevel level)) { + public static boolean replaceBaseLoot(NativeEntityLoot entity) { + if (entity == null || entity.world() == null) { return false; } + NativeWorld level = entity.world(); String encoded = findTag(entity); if (encoded == null) { return false; @@ -80,8 +81,8 @@ public final class ModdedDeathLoot { if (engine == null) { return true; } - KList stacks = resolve(engine, level, binding); - emit(level, entity, stacks); + KList stacks = resolve(engine, level, binding); + entity.emit(stacks); return true; } @@ -94,15 +95,14 @@ public final class ModdedDeathLoot { return false; } - private static void fillContainer(Engine engine, ServerLevel level, ContainerEntity container, LootBinding binding) { - container.setContainerLootTable(null); - container.clearItemStacks(); - KList items = resolve(engine, level, binding); - ModdedLootApplier.fillContainer(container, items, new RNG(binding.seed())); + private static void fillContainer(Engine engine, NativeEntityLoot entity, LootBinding binding) { + entity.prepareContainer(); + KList items = resolve(engine, entity.world(), binding); + entity.fill(items, new RNG(binding.seed()), message -> IrisLogging.debug("Iris loot: " + message)); } - private static KList resolve(Engine engine, ServerLevel level, LootBinding binding) { - KList drops = new KList<>(); + private static KList resolve(Engine engine, NativeWorld level, LootBinding binding) { + KList drops = new KList<>(); for (String key : binding.tableKeys()) { IrisLootTable table = engine.getData().getLootLoader().load(key); if (table == null) { @@ -111,21 +111,12 @@ public final class ModdedDeathLoot { } continue; } - drops.addAll(ModdedItemTranslator.loot(table, engine.getSeedManager().getLoot(), InventorySlotType.STORAGE, level, + drops.addAll(ModdedItemTranslator.loot(table, engine.getSeedManager().getLoot(), InventorySlotType.STORAGE, ModdedItemTranslator.context(level), binding.spawnX(), binding.spawnY(), binding.spawnZ())); } return drops; } - private static void emit(ServerLevel level, LivingEntity entity, KList drops) { - for (ItemStack stack : drops) { - if (stack == null || stack.isEmpty()) { - continue; - } - entity.spawnAtLocation(level, stack); - } - } - private static String encode(LootBinding binding) { return TAG_PREFIX + binding.seed() + "|" + binding.spawnX() + "|" + binding.spawnY() + "|" + binding.spawnZ() + "|" + String.join(",", binding.tableKeys()); @@ -156,8 +147,8 @@ public final class ModdedDeathLoot { } } - private static String findTag(Entity entity) { - for (String tag : entity.entityTags()) { + private static String findTag(NativeEntityLoot entity) { + for (String tag : entity.tags()) { if (tag.startsWith(TAG_PREFIX)) { return tag; } @@ -165,12 +156,9 @@ public final class ModdedDeathLoot { return null; } - private static Engine engineFor(ServerLevel level) { - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (generator instanceof IrisModdedChunkGenerator irisGenerator) { - return irisGenerator.engineIfBound(); - } - return null; + private static Engine engineFor(NativeWorld level) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + return generator == null ? null : generator.engineIfBound(); } private record LootBinding(long seed, int spawnX, int spawnY, int spawnZ, KList tableKeys) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDecoratorHooks.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDecoratorHooks.java index 738804744..70ba9c6b2 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDecoratorHooks.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDecoratorHooks.java @@ -20,100 +20,38 @@ package art.arcane.iris.modded; import art.arcane.iris.generation.decoration.DecoratorPlatformHooks; import art.arcane.iris.generation.mantle.EngineMantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockFaces; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.world.level.EmptyBlockGetter; -import net.minecraft.world.level.block.SupportType; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.Property; - -import java.util.LinkedHashMap; -import java.util.Map; public final class ModdedDecoratorHooks implements DecoratorPlatformHooks.FaceFixer, DecoratorPlatformHooks.SurfaceSturdiness { - private static final Direction[] CARTESIAN = {Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP, Direction.DOWN}; - private static final String[] FACE_NAMES = {"north", "east", "south", "west", "up", "down"}; - @Override - public PlatformBlockState fixFaces(PlatformBlockState state, Hunk hunk, int rX, int rZ, int x, int y, int z, EngineMantle mantle) { - if (!(state instanceof ModdedBlockState fabric)) { - return state; - } - BlockState cloned = fabric.handle(); - Map allowed = faceProperties(cloned); - - for (Map.Entry entry : allowed.entrySet()) { - if (cloned.getValue(entry.getValue())) { - cloned = cloned.setValue(entry.getValue(), Boolean.FALSE); - } - } - - boolean found = false; - for (Direction f : CARTESIAN) { - int yy = y + f.getStepY(); - - PlatformBlockState rs = null; - if (mantle != null) { - rs = mantle.getMantle().get(x + f.getStepX(), yy, z + f.getStepZ(), PlatformBlockState.class); - } - BlockState r = rs == null ? (BlockState) EngineMantle.AIR.get().nativeHandle() : (BlockState) rs.nativeHandle(); - if (isFaceSturdy(r, f.getOpposite())) { - BooleanProperty property = allowed.get(f.getSerializedName()); - if (property != null) { - found = true; - cloned = cloned.setValue(property, Boolean.TRUE); - } - continue; - } - - int xx = rX + f.getStepX(); - int zz = rZ + f.getStepZ(); - if (xx < 0 || xx > 15 || zz < 0 || zz > 15 || yy < 0 || yy > hunk.getHeight()) { - continue; - } - - r = (BlockState) hunk.get(xx, yy, zz).nativeHandle(); - if (isFaceSturdy(r, f.getOpposite())) { - BooleanProperty property = allowed.get(f.getSerializedName()); - if (property != null) { - found = true; - cloned = cloned.setValue(property, Boolean.TRUE); - } - } - } - - if (!found) { - String fallback = allowed.containsKey("down") ? "down" : "up"; - BooleanProperty property = allowed.get(fallback); - if (property != null) { - cloned = cloned.setValue(property, Boolean.TRUE); - } - } - - return fabric.withHandle(cloned, fabric.parsedProperties()); + public NativeBlockState fixFaces(NativeBlockState state, Hunk hunk, int rX, int rZ, + int x, int y, int z, EngineMantle mantle) { + return NativeBlockFaces.fixFaces(state, new Neighbors(hunk, rX, rZ, x, y, z, mantle)); } @Override - public boolean canGoOn(PlatformBlockState surface, boolean upward) { - return isFaceSturdy((BlockState) surface.nativeHandle(), upward ? Direction.UP : Direction.DOWN); + public boolean canGoOn(NativeBlockState surface, boolean upward) { + return NativeBlockFaces.canGoOn(surface, upward); } - private static boolean isFaceSturdy(BlockState state, Direction face) { - return state.isFaceSturdy(EmptyBlockGetter.INSTANCE, BlockPos.ZERO, face, SupportType.FULL); - } + private record Neighbors(Hunk hunk, int rX, int rZ, int x, int y, int z, + EngineMantle mantle) implements NativeBlockFaces.Neighbors { + @Override + public NativeBlockState primary(int dx, int dy, int dz) { + NativeBlockState state = mantle == null ? null + : mantle.getMantle().get(x + dx, y + dy, z + dz, NativeBlockState.class); + return state == null ? EngineMantle.AIR.get() : state; + } - private static Map faceProperties(BlockState state) { - Map properties = new LinkedHashMap<>(); - for (String name : FACE_NAMES) { - for (Property property : state.getProperties()) { - if (property.getName().equals(name) && property instanceof BooleanProperty bool) { - properties.put(name, bool); - } - } + @Override + public NativeBlockState secondary(int dx, int dy, int dz) { + int xx = rX + dx; + int yy = y + dy; + int zz = rZ + dz; + return xx < 0 || xx > 15 || zz < 0 || zz > 15 || yy < 0 || yy > hunk.getHeight() + ? null : hunk.get(xx, yy, zz); } - return properties; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionManager.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionManager.java index 2e01f9652..2780a9511 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionManager.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionManager.java @@ -18,36 +18,24 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldTeleport; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDimensionRuntime; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerAccess; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.PackValidationRegistry; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.world.history.GenerationActivation; import art.arcane.iris.generation.terrain.IrisDimension; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.TicketType; -import net.minecraft.world.entity.Relative; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.biome.Biomes; -import net.minecraft.world.level.biome.FixedBiomeSource; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.dimension.LevelStem; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.storage.DerivedLevelData; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.ServerLevelData; -import net.minecraft.world.level.storage.WorldData; import java.nio.file.Path; import java.util.ArrayList; @@ -55,34 +43,24 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; -import java.util.Set; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; public final class ModdedDimensionManager { - private static final int TELEPORT_WARM_RADIUS = 0; private static final Object LOCK = new Object(); private static final ConcurrentHashMap HANDLES = new ConcurrentHashMap<>(); - private static final TicketType TELEPORT_WARM_TICKET = new TicketType(TicketType.NO_TIMEOUT, - TicketType.FLAG_LOADING | TicketType.FLAG_KEEP_DIMENSION_ACTIVE); - private static final long TELEPORT_TIMEOUT_SECONDS = 10L; - private static volatile ModdedServerAccess access; + private static volatile NativeDimensionRuntime access; private ModdedDimensionManager() { } - public static synchronized ModdedServerAccess bindAccess(ModdedServerAccess serverAccess) { - ModdedServerAccess previous = access; - access = serverAccess; + public static synchronized NativeDimensionRuntime bindAccess(ModdedServerAccess serverAccess) { + NativeDimensionRuntime previous = access; + access = new NativeDimensionRuntime(serverAccess); return previous; } - public static synchronized void restoreAccess(ModdedServerAccess serverAccess) { + public static synchronized void restoreAccess(NativeDimensionRuntime serverAccess) { access = serverAccess; } @@ -98,35 +76,30 @@ public final class ModdedDimensionManager { return new ArrayList<>(HANDLES.values()); } - public static ServerLevel level(MinecraftServer server, String dimensionId) { + public static NativeWorld level(NativeModdedServer server, String dimensionId) { Handle handle = HANDLES.get(dimensionId); - if (handle != null && handle.level().getServer() == server) { + if (handle != null && server.owns(handle.level())) { return handle.level(); } - ResourceKey key = levelKey(dimensionId); // Server thread only (create/remove hold LOCK, teleport and the primary-world router tick, command - // handlers). Off-thread callers must use ModdedServerLevels.level instead of the live map. - for (ServerLevel level : server.getAllLevels()) { - if (level.dimension().equals(key)) { - return level; - } - } - return null; + // handlers). Off-thread callers must use the published world snapshot instead of the live map. + return server.liveWorld(dimensionId); } - public static Engine engine(MinecraftServer server, String dimensionId) { - ServerLevel level = level(server, dimensionId); + public static Engine engine(NativeModdedServer server, String dimensionId) { + NativeWorld level = level(server, dimensionId); if (level == null) { return null; } - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (generator == null) { return null; } return generator.commandEngine(); } public static Handle createTransientStudio( - MinecraftServer server, + NativeModdedServer server, String dimensionId, String pack, String packDimensionKey, @@ -143,7 +116,7 @@ public final class ModdedDimensionManager { } static Handle restorePersistent( - MinecraftServer server, + NativeModdedServer server, String dimensionId, String pack, String packDimensionKey, @@ -160,16 +133,16 @@ public final class ModdedDimensionManager { } private static Handle create( - MinecraftServer server, + NativeModdedServer server, String dimensionId, String pack, String packDimensionKey, long seed, ModdedGenerationMode generationMode ) { - ModdedServerAccess serverAccess = requireAccess(); + NativeDimensionRuntime serverAccess = requireAccess(); synchronized (LOCK) { - ResourceKey key = levelKey(dimensionId); + String key = dimensionId; RuntimePack runtimePack = switch (generationMode) { case TRANSIENT_STUDIO -> transientStudioPack(pack, packDimensionKey); case PERSISTENT_CREATE -> persistentRuntimePack( @@ -200,8 +173,9 @@ public final class ModdedDimensionManager { return refreshed; } if (serverAccess.hasLevel(server, key)) { - ServerLevel present = level(server, dimensionId); - if (present == null || !(present.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + NativeWorld present = level(server, dimensionId); + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(present, IrisModdedChunkGenerator.class); + if (generator == null) { throw new IllegalStateException("Iris cannot inject dimension '" + dimensionId + "': a non-Iris level with that id is already loaded"); } ModdedIrisLog.warn("Iris dimension '{}' is already present in the running server; reusing it", dimensionId); @@ -231,7 +205,7 @@ public final class ModdedDimensionManager { } } - public static Handle createPersistent(MinecraftServer server, String dimensionId, String pack, String packDimensionKey, long seed) { + public static Handle createPersistent(NativeModdedServer server, String dimensionId, String pack, String packDimensionKey, long seed) { synchronized (LOCK) { ModdedDimensionRegistryStore.PersistentDimension previous = ModdedDimensionRegistryStore.get(server, dimensionId); @@ -272,7 +246,7 @@ public final class ModdedDimensionManager { } public static UpdateResult stagePersistentUpdate( - MinecraftServer server, + NativeModdedServer server, String dimensionId, String pack, String packDimensionKey @@ -296,7 +270,7 @@ public final class ModdedDimensionManager { ModdedGenerationHistoryStorage.ActivePack active = ModdedGenerationHistoryStorage.createOrStage( server, - levelKey(dimensionId), + dimensionId, pack, packDimensionKey, previous.seed() @@ -338,7 +312,7 @@ public final class ModdedDimensionManager { } } - public static boolean removePersistent(MinecraftServer server, String dimensionId, boolean wipeStorage) { + public static boolean removePersistent(NativeModdedServer server, String dimensionId, boolean wipeStorage) { ModdedDimensionRegistryStore.PersistentDimension previous = ModdedDimensionRegistryStore.get(server, dimensionId); ModdedDimensionRegistryStore.remove(server, dimensionId); @@ -356,11 +330,11 @@ public final class ModdedDimensionManager { } } - public static boolean remove(MinecraftServer server, String dimensionId, boolean wipeStorage) { - ModdedServerAccess serverAccess = requireAccess(); + public static boolean remove(NativeModdedServer server, String dimensionId, boolean wipeStorage) { + NativeDimensionRuntime serverAccess = requireAccess(); synchronized (LOCK) { - ResourceKey key = levelKey(dimensionId); - ServerLevel level = level(server, dimensionId); + String key = dimensionId; + NativeWorld level = level(server, dimensionId); if (level == null) { HANDLES.remove(dimensionId); if (wipeStorage) { @@ -368,24 +342,21 @@ public final class ModdedDimensionManager { } return false; } - IrisModdedChunkGenerator generator = level.getChunkSource().getGenerator() - instanceof IrisModdedChunkGenerator irisGenerator - ? irisGenerator - : null; + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); boolean unloadEventStarted = false; try { - evacuate(server, level); - level.save(null, true, false); + NativeWorldTeleport.evacuate(server, level); + serverAccess.save(level); unloadEventStarted = true; - ModdedEngineBootstrap.loader().fireDynamicLevelUnload(server, level); + ModdedEngineBootstrap.loader().fireDynamicLevelUnload(level); if (generator != null) { generator.unbindEngine(level); } ModdedWorldEngines.evictOrThrow(level); - serverAccess.removeLevel(server, key); - // Undo snapshots pin the ServerLevel and could replay into the dead level. + serverAccess.remove(server, key); + // Undo snapshots pin the NativeWorld and could replay into the dead level. art.arcane.iris.modded.command.ModdedObjectUndo.forget(level); - level.close(); + serverAccess.close(level); HANDLES.remove(dimensionId); if (wipeStorage) { ModdedDimensionStorage.wipe(server, key); @@ -400,8 +371,8 @@ public final class ModdedDimensionManager { } } - private static void rollbackRemoval(MinecraftServer server, ModdedServerAccess serverAccess, - ResourceKey key, ServerLevel level, + private static void rollbackRemoval(NativeModdedServer server, NativeDimensionRuntime serverAccess, + String key, NativeWorld level, IrisModdedChunkGenerator generator, boolean unloadEventStarted, Throwable failure) { try { @@ -411,351 +382,54 @@ public final class ModdedDimensionManager { if (generator != null) { generator.bindLevel(level); } - ModdedEngineBootstrap.loader().fireDynamicLevelLoad(server, level); + ModdedEngineBootstrap.loader().fireDynamicLevelLoad(level); } catch (Throwable rollbackFailure) { if (rollbackFailure != failure) { failure.addSuppressed(rollbackFailure); } ModdedIrisLog.error("Iris failed to restore the engine for retained runtime dimension '{}'", - key.identifier(), rollbackFailure); + key, rollbackFailure); } } - public static CompletableFuture teleportAsync( - ServerPlayer player, - MinecraftServer server, - String dimensionId, - double x, - double y, - double z - ) { - return teleportAsync(player, server, dimensionId, x, y, z, - System.nanoTime() + TimeUnit.SECONDS.toNanos(TELEPORT_TIMEOUT_SECONDS)); - } - - public static CompletableFuture teleportAsync( - ServerPlayer player, - MinecraftServer server, - String dimensionId, - double x, - double y, - double z, - long deadlineNanos - ) { - ServerLevel level = level(server, dimensionId); - if (level == null) { - return CompletableFuture.completedFuture(false); - } - return teleportAsync(player, server, level, x, y, z, deadlineNanos); - } - - public static CompletableFuture teleportAsync( - ServerPlayer player, - MinecraftServer server, - ServerLevel level, - double x, - double y, - double z - ) { - return teleportAsync(player, server, level, x, y, z, 0L); - } - - public static CompletableFuture teleportAsync( - ServerPlayer player, - MinecraftServer server, - ServerLevel level, - double x, - double y, - double z, - long deadlineNanos - ) { - CompletableFuture result = new CompletableFuture<>(); - if (player == null || server == null || level == null || level.getServer() != server) { - result.complete(false); - return result; - } - if (!Double.isFinite(x) || !Double.isFinite(z) - || (y != Double.MIN_VALUE && !Double.isFinite(y))) { - result.completeExceptionally(new IllegalArgumentException("Teleport coordinates must be finite.")); - return result; - } - if (deadlineNanos != 0L) { - long remainingNanos = deadlineNanos - System.nanoTime(); - if (remainingNanos <= 0L) { - result.completeExceptionally(teleportTimeout(level, x, z)); - return result; - } - result.orTimeout(remainingNanos, TimeUnit.NANOSECONDS); - } - UUID playerId = player.getUUID(); - runOnServer(server, () -> beginTeleport( - result, - playerId, - server, - level, - x, - y, - z, - deadlineNanos)); - return result; - } - - private static void beginTeleport( - CompletableFuture result, - UUID playerId, - MinecraftServer server, - ServerLevel level, - double x, - double y, - double z, - long deadlineNanos - ) { - if (result.isDone()) { - return; - } - if (deadlineNanos != 0L && System.nanoTime() >= deadlineNanos) { - result.completeExceptionally(teleportTimeout(level, x, z)); - return; - } - ServerLevel active = level(server, level.dimension().identifier().toString()); - if (active != level) { - result.complete(false); - return; - } - int blockX = ModdedTeleportBounds.blockCoordinate(x); - int blockZ = ModdedTeleportBounds.blockCoordinate(z); - ChunkPos chunkPos = new ChunkPos(blockX >> 4, blockZ >> 4); - if (level.getChunkSource().hasChunk(chunkPos.x(), chunkPos.z())) { - completeTeleport(result, playerId, server, level, x, y, z, blockX, blockZ, deadlineNanos); - return; - } - warmAndTeleport(result, playerId, server, level, x, y, z, blockX, blockZ, chunkPos, deadlineNanos); - } - - private static void warmAndTeleport( - CompletableFuture result, - UUID playerId, - MinecraftServer server, - ServerLevel level, - double x, - double y, - double z, - int blockX, - int blockZ, - ChunkPos chunkPos, - long deadlineNanos - ) { - AtomicBoolean ticketReleased = new AtomicBoolean(); - CompletableFuture chunkLoad; - try { - chunkLoad = level.getChunkSource().addTicketAndLoadWithRadius( - TELEPORT_WARM_TICKET, - chunkPos, - TELEPORT_WARM_RADIUS); - } catch (Throwable failure) { - result.completeExceptionally(failure); - return; - } - if (chunkLoad == null) { - releaseTeleportTicket(level, chunkPos, ticketReleased); - result.completeExceptionally(new IllegalStateException( - "Chunk warm returned no completion future for " + level.dimension().identifier() - + " at " + chunkPos.x() + "," + chunkPos.z() + ".")); - return; - } - result.whenComplete((success, failure) -> runOnServer(server, - () -> releaseTeleportTicket(level, chunkPos, ticketReleased))); - chunkLoad.whenComplete((ignored, failure) -> runOnServer(server, () -> { - releaseTeleportTicket(level, chunkPos, ticketReleased); - if (result.isDone()) { - return; - } - if (failure != null) { - result.completeExceptionally(failure); - return; - } - completeTeleport(result, playerId, server, level, x, y, z, blockX, blockZ, deadlineNanos); - })); - } - - private static void releaseTeleportTicket( - ServerLevel level, - ChunkPos chunkPos, - AtomicBoolean ticketReleased - ) { - if (ticketReleased.compareAndSet(false, true)) { - level.getChunkSource().removeTicketWithRadius( - TELEPORT_WARM_TICKET, - chunkPos, - TELEPORT_WARM_RADIUS); - } - } - - private static void completeTeleport( - CompletableFuture result, - UUID playerId, - MinecraftServer server, - ServerLevel level, - double x, - double y, - double z, - int blockX, - int blockZ, - long deadlineNanos - ) { - if (result.isDone()) { - return; - } - if (deadlineNanos != 0L && System.nanoTime() >= deadlineNanos) { - result.completeExceptionally(teleportTimeout(level, x, z)); - return; - } - ServerLevel active = level(server, level.dimension().identifier().toString()); - ServerPlayer player = server.getPlayerList().getPlayer(playerId); - if (active != level || player == null) { - result.complete(false); - return; - } - try { - int targetY = resolveSafeTeleportY(level, blockX, blockZ, y); - boolean teleported = player.teleportTo( - level, - x, - targetY, - z, - Set.of(), - player.getYRot(), - player.getXRot(), - false); - result.complete(teleported); - } catch (Throwable failure) { - result.completeExceptionally(failure); - } - } - - private static int resolveSafeTeleportY(ServerLevel level, int blockX, int blockZ, double requestedY) { - int initialY = requestedY == Double.MIN_VALUE - ? level.getHeight(Heightmap.Types.MOTION_BLOCKING, blockX, blockZ) - : (int) Math.floor(requestedY); - int startY = ModdedTeleportBounds.clampY(level.getMinY(), level.getMaxY(), initialY); - int maximumY = ModdedTeleportBounds.maximumY(level.getMinY(), level.getMaxY()); - int minimumY = ModdedTeleportBounds.minimumY(level.getMinY(), level.getMaxY()); - for (int candidateY = startY; candidateY <= maximumY; candidateY++) { - if (isSafeStandingPosition(level, blockX, candidateY, blockZ)) { - return candidateY; - } - } - for (int candidateY = startY - 1; candidateY >= minimumY; candidateY--) { - if (isSafeStandingPosition(level, blockX, candidateY, blockZ)) { - return candidateY; - } - } - throw new IllegalStateException("No safe teleport position exists in " - + level.dimension().identifier() + " at " + blockX + "," + blockZ + "."); - } - - private static boolean isSafeStandingPosition(ServerLevel level, int blockX, int blockY, int blockZ) { - BlockPos feet = new BlockPos(blockX, blockY, blockZ); - BlockPos head = feet.above(); - BlockPos support = feet.below(); - return level.getBlockState(feet).getCollisionShape(level, feet).isEmpty() - && level.getBlockState(feet).getFluidState().isEmpty() - && level.getBlockState(head).getCollisionShape(level, head).isEmpty() - && level.getBlockState(head).getFluidState().isEmpty() - && !level.getBlockState(support).getCollisionShape(level, support).isEmpty(); - } - - private static TimeoutException teleportTimeout(ServerLevel level, double x, double z) { - return new TimeoutException("Teleport into " + level.dimension().identifier() - + " at " + ModdedTeleportBounds.blockCoordinate(x) + "," - + ModdedTeleportBounds.blockCoordinate(z) - + " exceeded " + TELEPORT_TIMEOUT_SECONDS + " seconds."); - } - - private static void runOnServer(MinecraftServer server, Runnable task) { - if (server.isSameThread()) { - task.run(); - return; - } - server.execute(task); - } - - private static Holder resolveDimensionType( - RegistryAccess registryAccess, - String pack, - String packDimensionKey, - RuntimePack runtimePack - ) { - Registry registry = registryAccess.lookupOrThrow(Registries.DIMENSION_TYPE); - String typeRef = runtimePack.dimensionTypeKey(); - ResourceKey typeKey = ResourceKey.create(Registries.DIMENSION_TYPE, Identifier.parse(typeRef)); - return ModdedForcedDatapack.requireRegisteredDimensionType( - typeRef, registry.get(typeKey), pack, packDimensionKey); - } - private static Handle inject( - MinecraftServer server, - ModdedServerAccess serverAccess, + NativeModdedServer server, + NativeDimensionRuntime serverAccess, String dimensionId, - ResourceKey key, + String key, String pack, String packDimensionKey, long seed, RuntimePack runtimePack ) { - RegistryAccess registryAccess = server.registryAccess(); - Holder dimensionType = resolveDimensionType( - registryAccess, - pack, - packDimensionKey, - runtimePack - ); - Holder plains = registryAccess.lookupOrThrow(Registries.BIOME).getOrThrow(Biomes.PLAINS); - FixedBiomeSource biomeSource = new FixedBiomeSource(plains); + String typeRef = runtimePack.dimensionTypeKey(); + ModdedForcedDatapack.requireRegisteredDimensionType(typeRef, + serverAccess.hasDimensionType(server, typeRef) ? Optional.of(Boolean.TRUE) : Optional.empty(), + pack, packDimensionKey); String generatorRef = pack.equals(packDimensionKey) ? pack : pack + ":" + packDimensionKey; - IrisModdedChunkGenerator generator = new IrisModdedChunkGenerator(biomeSource, generatorRef); - generator.repoint( - pack, - runtimePack.dimensionKey(), - seed, - runtimePack.packRoot(), - runtimePack.generationMode() - ); - LevelStem stem = new LevelStem(dimensionType, generator); - - WorldData worldData = server.getWorldData(); - ServerLevelData overworldData = worldData.overworldData(); - DerivedLevelData derivedLevelData = new DerivedLevelData(worldData, overworldData); - - Executor executor = serverAccess.levelExecutor(server); - LevelStorageSource.LevelStorageAccess storage = serverAccess.levelStorage(server); - long obfuscatedSeed = BiomeManager.obfuscateSeed(seed); - - ServerLevel level = new ServerLevel( - server, - executor, - storage, - derivedLevelData, - key, - stem, - false, - obfuscatedSeed, - List.of(), - false); + NativeDimensionRuntime.Created created = serverAccess.create(server, + new NativeDimensionRuntime.Creation<>(dimensionId, typeRef, seed, generatorRef, context -> { + IrisModdedChunkGenerator generator = new IrisModdedChunkGenerator(context); + generator.repoint(pack, runtimePack.dimensionKey(), seed, runtimePack.packRoot(), + runtimePack.generationMode()); + return generator; + })); + NativeWorld level = created.world(); + IrisModdedChunkGenerator generator = created.generator(); boolean loadEventStarted = false; try { - serverAccess.initializeLevelData(server, level); + serverAccess.initialize(server, level); generator.bindLevel(level); Handle handle = new Handle(dimensionId, pack, packDimensionKey, seed, level, generator); - ServerLevel previous = serverAccess.putLevelIfAbsent(server, key, level); + NativeWorld previous = serverAccess.publish(server, level); if (previous != null) { throw new IllegalStateException("Iris cannot inject dimension '" + dimensionId + "': the level was registered concurrently"); } - server.getPlayerList().addWorldborderListener(level); + serverAccess.addWorldBorderListener(server, level); loadEventStarted = true; - ModdedEngineBootstrap.loader().fireDynamicLevelLoad(server, level); + ModdedEngineBootstrap.loader().fireDynamicLevelLoad(level); return handle; } catch (Throwable error) { rollbackInjection(server, serverAccess, key, level, generator, loadEventStarted, error); @@ -769,67 +443,48 @@ public final class ModdedDimensionManager { } } - private static void rollbackInjection(MinecraftServer server, ModdedServerAccess serverAccess, - ResourceKey key, ServerLevel level, + private static void rollbackInjection(NativeModdedServer server, NativeDimensionRuntime serverAccess, + String key, NativeWorld level, IrisModdedChunkGenerator generator, boolean loadEventStarted, Throwable failure) { if (loadEventStarted) { try { - ModdedEngineBootstrap.loader().fireDynamicLevelUnload(server, level); + ModdedEngineBootstrap.loader().fireDynamicLevelUnload(level); } catch (Throwable cleanupError) { failure.addSuppressed(cleanupError); ModdedIrisLog.error("Iris failed to publish rollback unload for {}", - key.identifier(), cleanupError); + key, cleanupError); } } try { if (serverAccess.hasLevel(server, key)) { - ServerLevel removed = serverAccess.removeLevel(server, key); - if (removed != null && removed != level) { - serverAccess.putLevel(server, key, removed); + NativeWorld removed = serverAccess.remove(server, key); + if (removed != null && !NativeDimensionRuntime.sameWorld(removed, level)) { + serverAccess.restore(server, removed); throw new IllegalStateException("Iris injection rollback encountered another registered level for " - + key.identifier()); + + key); } } } catch (Throwable cleanupError) { failure.addSuppressed(cleanupError); - ModdedIrisLog.error("Iris failed to remove a partially injected level for {}", key.identifier(), cleanupError); + ModdedIrisLog.error("Iris failed to remove a partially injected level for {}", key, cleanupError); } try { generator.unbindEngine(level); } catch (Throwable cleanupError) { failure.addSuppressed(cleanupError); - ModdedIrisLog.error("Iris failed to close a partially bound engine for {}", key.identifier(), cleanupError); + ModdedIrisLog.error("Iris failed to close a partially bound engine for {}", key, cleanupError); } try { - level.close(); + serverAccess.close(level); } catch (Throwable cleanupError) { failure.addSuppressed(cleanupError); - ModdedIrisLog.error("Iris failed to close a partially injected level for {}", key.identifier(), cleanupError); + ModdedIrisLog.error("Iris failed to close a partially injected level for {}", key, cleanupError); } } - public static int evacuate(MinecraftServer server, ServerLevel from) { - ServerLevel fallback = server.overworld(); - if (fallback == from) { - return 0; - } - BlockPos spawn = fallback.getRespawnData().pos(); - int spawnY = fallback.getHeight(Heightmap.Types.MOTION_BLOCKING, spawn.getX(), spawn.getZ()); - List players = new ArrayList<>(from.players()); - for (ServerPlayer player : players) { - player.teleportTo(fallback, spawn.getX() + 0.5D, spawnY, spawn.getZ() + 0.5D, Set.of(), player.getYRot(), player.getXRot(), false); - } - return players.size(); - } - - private static ResourceKey levelKey(String dimensionId) { - Identifier identifier = Identifier.parse(dimensionId); - return ResourceKey.create(Registries.DIMENSION, identifier); - } - - private static ModdedServerAccess requireAccess() { - ModdedServerAccess bound = access; + private static NativeDimensionRuntime requireAccess() { + NativeDimensionRuntime bound = access; if (bound == null) { throw new IllegalStateException("Iris modded server access is not bound; the loader bootstrap must bind ModdedServerAccess before runtime dimension injection"); } @@ -887,7 +542,7 @@ public final class ModdedDimensionManager { } } - public record Handle(String dimensionId, String pack, String packDimensionKey, long seed, ServerLevel level, IrisModdedChunkGenerator generator) { + public record Handle(String dimensionId, String pack, String packDimensionKey, long seed, NativeWorld level, IrisModdedChunkGenerator generator) { } public record UpdateResult( diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionMetadata.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionMetadata.java index 2a39dbb2b..3fd14abab 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionMetadata.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionMetadata.java @@ -26,7 +26,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; -import net.minecraft.resources.Identifier; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeResourceKeys; import java.util.LinkedHashSet; import java.util.Locale; @@ -93,9 +93,9 @@ final class ModdedDimensionMetadata { if (irisBiome == null) { continue; } - Identifier derivative = Identifier.tryParse(irisBiome.getStructureDerivativeKey()); + String derivative = NativeResourceKeys.normalize(irisBiome.getStructureDerivativeKey()); if (derivative != null) { - keys.add(derivative.toString().toLowerCase(Locale.ROOT)); + keys.add(derivative); } if (!irisBiome.isCustom()) { continue; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionRegistryStore.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionRegistryStore.java index 8a846ef49..698006f83 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionRegistryStore.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionRegistryStore.java @@ -18,10 +18,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; + import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.level.storage.LevelResource; import java.io.IOException; import java.nio.channels.FileChannel; @@ -45,7 +45,7 @@ public final class ModdedDimensionRegistryStore { private ModdedDimensionRegistryStore() { } - public static List load(MinecraftServer server) { + public static List load(NativeModdedServer server) { return load(storeFile(server)); } @@ -61,7 +61,7 @@ public final class ModdedDimensionRegistryStore { * Boot-safe load: a corrupt registry must not abort server start. The broken file is renamed aside so the * next write starts clean, and every id we can still recognise in the raw text is reported as lost. */ - public static List loadForStartup(MinecraftServer server) { + public static List loadForStartup(NativeModdedServer server) { return loadForStartup(storeFile(server)); } @@ -153,11 +153,11 @@ public final class ModdedDimensionRegistryStore { } } - public static PersistentDimension get(MinecraftServer server, String id) { + public static PersistentDimension get(NativeModdedServer server, String id) { return index(load(server)).get(id); } - public static synchronized void put(MinecraftServer server, PersistentDimension dimension) { + public static synchronized void put(NativeModdedServer server, PersistentDimension dimension) { Path file = storeFile(server); Contents contents = contents(file); Map current = index(contents.dimensions()); @@ -165,7 +165,7 @@ public final class ModdedDimensionRegistryStore { write(file, new ArrayList<>(current.values()), contents.unparsed()); } - public static synchronized void remove(MinecraftServer server, String id) { + public static synchronized void remove(NativeModdedServer server, String id) { Path file = storeFile(server); Contents contents = contents(file); Map current = index(contents.dimensions()); @@ -235,8 +235,8 @@ public final class ModdedDimensionRegistryStore { } } - private static Path storeFile(MinecraftServer server) { - return storeFile(server.getWorldPath(LevelResource.ROOT)); + private static Path storeFile(NativeModdedServer server) { + return storeFile(server.root()); } private static Path storeFile(Path worldRoot) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionStorage.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionStorage.java index 8a8baa828..0bc83bf78 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionStorage.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedDimensionStorage.java @@ -18,13 +18,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; + import art.arcane.iris.generation.runtime.IrisEngineMantle; import art.arcane.iris.world.history.GenerationHistoryPaths; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelResource; import java.io.File; import java.io.IOException; @@ -46,11 +43,11 @@ public final class ModdedDimensionStorage { private ModdedDimensionStorage() { } - public static File storageFolder(MinecraftServer server, ResourceKey dimension) { - return DimensionType.getStorageFolder(dimension, server.getWorldPath(LevelResource.ROOT)).toFile(); + public static File storageFolder(NativeModdedServer server, String dimension) { + return server.dimensionFolder(dimension).toFile(); } - public static void wipe(MinecraftServer server, ResourceKey dimension) { + public static void wipe(NativeModdedServer server, String dimension) { File storageFolder = storageFolder(server, dimension); try { for (String folder : CHUNK_DATA_FOLDERS) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineBootstrap.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineBootstrap.java index 309eed744..831993b86 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineBootstrap.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineBootstrap.java @@ -18,6 +18,34 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeServerSpawn; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityLoot; + + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDimensionRuntime; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockDropHooks; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileReader; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolServer; +import art.arcane.volmlib.nativelib.modded.EntityBehaviorTags; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityBehavior; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStateMerger; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerAccess; + import art.arcane.iris.studio.view.GuiHost; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.generation.decoration.DecoratorPlatformHooks; @@ -49,12 +77,6 @@ import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.generation.concurrent.MultiBurst; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.storage.LevelData; import java.util.ArrayDeque; @@ -66,10 +88,10 @@ public final class ModdedEngineBootstrap { }; private static final Object LOCK = new Object(); private static final ModdedServiceManager UNBOUND_SERVICE_MANAGER = new ModdedServiceManager(); - private static volatile ModdedLoader loader; + private static volatile NativeModdedLoader loader; private static volatile BoundRuntime runtime; - private static volatile MinecraftServer currentServer; - private static volatile MinecraftServer spawnCaptureServer; + private static volatile NativeModdedServer currentServer; + private static volatile NativeModdedServer spawnCaptureServer; private static volatile boolean initialSpawnWasDefault; private ModdedEngineBootstrap() { @@ -85,16 +107,18 @@ public final class ModdedEngineBootstrap { return bound == null ? null : bound.platform().moddedScheduler(); } - public static void tick(MinecraftServer server) { - ModdedScheduler.tick(server); + public static void tick(NativeModdedServer server) { + // First thing in the Iris tick body: keep the off-thread level snapshot current for levels registered + // outside ModdedServerAccess (vanilla boot, other mods) before the rest of the tick reads it. + ModdedScheduler.tick(server.refreshWorlds()); ModdedStartup.runOnce(server); ModdedPrimaryWorldRouter.tick(server); services().tick(server); - ModdedPregenBossBar.tick(server); - ModdedProtocolHandler.tickDimensionSync(server); + ModdedPregenBossBar.tick(); + ModdedProtocolHandler.tickDimensionSync(); } - public static void start(MinecraftServer server) { + public static void start(NativeModdedServer server) { captureInitialSpawn(server); currentServer = server; bind(); @@ -113,47 +137,50 @@ public final class ModdedEngineBootstrap { IrisModdedChunkGenerator.startGenPool(); bindWorldGenerators(server); services().enableAll(); - ModdedProtocolHandler.start(server); + ModdedProtocolHandler.start(NativeProtocolServer.fromServer(server)); } - private static void bindWorldGenerators(MinecraftServer server) { - for (ServerLevel level : server.getAllLevels()) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator) { - generator.bindLevel(level); + private static void bindWorldGenerators(NativeModdedServer server) { + server.forEachLiveWorld((NativeWorld world) -> { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class); + if (generator != null) { + generator.bindLevel(world); } - } + }); } - public static void serverAboutToStart(MinecraftServer server) { + public static void serverAboutToStart(NativeModdedServer server) { captureInitialSpawn(server); ModdedStartup.prepareForStartup(); } - public static void serverStarted(MinecraftServer server) { + public static void serverStarted(NativeModdedServer server) { // Prime the off-thread level snapshot before anything can read it; the per-tick refresh in // ModdedScheduler.tick has not run yet at this point. - ModdedServerLevels.refreshIfStale(server); + server.refreshWorlds(); bindWorldGenerators(server); ModdedStartup.runOnce(server); reconcileSpawn(server); ModdedWorldCheck.serverStarted(server); } - public static void levelLoaded(ServerLevel level) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator) { - generator.bindLevel(level); + public static void levelLoaded(NativeWorld world) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class); + if (generator != null) { + generator.bindLevel(world); } } - public static void levelUnloaded(ServerLevel level) { - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + public static void levelUnloaded(NativeWorld world) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class); + if (generator == null) { return; } try { - generator.unbindEngine(level); + generator.unbindEngine(world); } catch (Throwable exception) { - ModdedIrisLog.error("Iris engine unload failed for {}", level.dimension().identifier(), exception); + ModdedIrisLog.error("Iris engine unload failed for {}", world.name(), exception); if (exception instanceof RuntimeException runtimeException) { throw runtimeException; } @@ -161,12 +188,12 @@ public final class ModdedEngineBootstrap { throw fatalError; } throw new IllegalStateException("Iris engine unload failed for " - + level.dimension().identifier(), exception); + + world.name(), exception); } } public static void stop() { - MinecraftServer stoppingServer = currentServer; + NativeModdedServer stoppingServer = currentServer; Throwable failure = null; failure = runStopStage(failure, "pack downloads", IrisModdedCommands::shutdownDownloads); failure = runStopStage(failure, "world check", () -> ModdedWorldCheck.serverStopped(stoppingServer)); @@ -222,48 +249,52 @@ public final class ModdedEngineBootstrap { } } - private static void captureInitialSpawn(MinecraftServer server) { - if (spawnCaptureServer == server) { + private static void captureInitialSpawn(NativeModdedServer server) { + if (spawnCaptureServer != null && spawnCaptureServer.sameServer(server)) { return; } - LevelData.RespawnData respawnData = server.getRespawnData(); - initialSpawnWasDefault = respawnData == null || LevelData.RespawnData.DEFAULT.equals(respawnData); + NativeServerSpawn.Respawn respawn = new NativeServerSpawn(server).current(); + initialSpawnWasDefault = respawn == null || respawn.initialDefault(); spawnCaptureServer = server; } - private static void reconcileSpawn(MinecraftServer server) { - LevelData.RespawnData current = server.getRespawnData(); - if (current == null) { + private static void reconcileSpawn(NativeModdedServer server) { + NativeServerSpawn spawn = new NativeServerSpawn(server); + NativeServerSpawn.Respawn current = spawn.current(); + if (current == null || current.world() == null) { return; } - ServerLevel level = server.getLevel(current.dimension()); - if (level == null || !(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator)) { + NativeWorld level = current.world(); + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) == null) { return; } - String dimensionId = level.dimension().identifier().toString(); + String dimensionId = level.name(); boolean studio = dimensionId.startsWith("irisworldgen:studio_"); - if (!shouldReconcileSpawn(initialSpawnWasDefault, studio, current.pos().getX(), current.pos().getZ())) { + if (!shouldReconcileSpawn(initialSpawnWasDefault, studio, current.position().x(), current.position().z())) { return; } - LevelChunk originChunk = level.getChunk(0, 0); - int surfaceY = originChunk.getHeight(Heightmap.Types.MOTION_BLOCKING, 0, 0) + 1; - BlockPos position = reconciledSpawnPosition(surfaceY, level.getMinY(), level.getHeight()); - server.setRespawnData(LevelData.RespawnData.of( - level.dimension(), position, current.yaw(), current.pitch())); + int surfaceY = spawn.surfaceAtOrigin(level); + NativeBlockPoint position = reconciledSpawnPosition(surfaceY, level.minHeight(), level.maxHeight() - level.minHeight()); + spawn.update(new NativeServerSpawn.Respawn(level, position, current.yaw(), current.pitch(), false)); ModdedIrisLog.info("Iris spawn reconciled for {} at {},{},{}", dimensionId, - position.getX(), position.getY(), position.getZ()); + position.x(), position.y(), position.z()); } static boolean shouldReconcileSpawn(boolean initialDefault, boolean studio, int currentX, int currentZ) { return initialDefault || studio || currentX == 0 && currentZ == 0; } - static BlockPos reconciledSpawnPosition(int surfaceY, int minY, int height) { + static NativeBlockPoint reconciledSpawnPosition(int surfaceY, int minY, int height) { int y = Math.max(minY + 1, Math.min(minY + height - 2, surfaceY)); - return new BlockPos(0, y, 0); + return new NativeBlockPoint(0, y, 0); } - public static void bootCommon(ModdedLoader moddedLoader, String loaderDescription, Runnable chunkGeneratorRegistration) { + public static void bootCommon(NativeModdedLoader moddedLoader, String loaderDescription, Runnable chunkGeneratorRegistration) { + NativeEntityLoot.bind(ModdedDeathLoot::replaceBaseLoot); + NativeEntityBehavior.bind(new EntityBehaviorTags("iris_non_persistent", "iris_unaware")); + NativeBlockDropHooks.bind(new NativeBlockDropHooks.Hooks( + ModdedBlockBreakHandler::clearPlacedProvenance, ModdedBlockBreakHandler::completePrepared, + ModdedBlockBreakHandler::complete)); loader = moddedLoader; ModdedIrisLog.info("Iris " + moddedLoader.modVersion() + " bootstrapping on Minecraft " + moddedLoader.minecraftVersion() + " (" + loaderDescription + ")"); selfTest(moddedLoader.getClass().getClassLoader()); @@ -296,16 +327,16 @@ public final class ModdedEngineBootstrap { ModdedWorldCheck.schedule(); } - public static ModdedLoader loader() { - ModdedLoader bound = loader; + public static NativeModdedLoader loader() { + NativeModdedLoader bound = loader; if (bound == null) { throw new IllegalStateException("Iris modded loader is not initialized; the loader bootstrap must call ModdedEngineBootstrap.bootCommon first"); } return bound; } - public static MinecraftServer currentServer() { - MinecraftServer tracked = currentServer; + public static NativeModdedServer currentServer() { + NativeModdedServer tracked = currentServer; return tracked != null ? tracked : loader().currentServer(); } @@ -337,7 +368,7 @@ public final class ModdedEngineBootstrap { if (synchronizedBound != null) { return synchronizedBound.platform(); } - ModdedLoader boundLoader = loader(); + NativeModdedLoader boundLoader = loader(); ModdedPlatform created = new ModdedPlatform(boundLoader); ModdedServiceManager createdServices = new ModdedServiceManager(); BindRollback rollback = new BindRollback(); @@ -350,17 +381,18 @@ public final class ModdedEngineBootstrap { IrisPlatforms.bind(created); rollback.add(() -> restorePlatform(previousPlatform)); - ModdedServerAccess previousServerAccess = ModdedDimensionManager.bindAccess( - new ModdedServerLevels(boundLoader::invalidateLevelCache)); + NativeDimensionRuntime previousServerAccess = ModdedDimensionManager.bindAccess( + boundLoader.serverAccess()); rollback.add(() -> ModdedDimensionManager.restoreAccess(previousServerAccess)); IrisObjectRotation.StateRotator previousRotator = IrisObjectRotation.bindPlatformRotator(new ModdedStateRotator()); rollback.add(() -> IrisObjectRotation.restorePlatformRotator(previousRotator)); - BlockDataMergeSupport.StateMerger previousMerger = BlockDataMergeSupport.bindPlatformMerger(new ModdedStateMerger()); + BlockDataMergeSupport.StateMerger previousMerger = BlockDataMergeSupport.bindPlatformMerger(new NativeStateMerger(ModdedBlockResolution.BLOCKS)::merge); rollback.add(() -> BlockDataMergeSupport.restorePlatformMerger(previousMerger)); - TileData.TileReader previousTileReader = TileData.bindPlatformReader(new ModdedTileReader(boundLoader::currentServer)); + NativeTileReader nativeTileReader = new NativeTileReader(boundLoader::currentServer); + TileData.TileReader previousTileReader = TileData.bindPlatformReader(in -> ModdedTileData.wrap(nativeTileReader.read(in))); rollback.add(() -> TileData.restorePlatformReader(previousTileReader)); TileData.TileFactory previousTileFactory = TileData.bindPlatformFactory(ModdedTileData::fromProperties); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineEffects.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineEffects.java index 8c6a7ec91..dd101010d 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineEffects.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEngineEffects.java @@ -18,6 +18,7 @@ package art.arcane.iris.modded; + import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.BiomeEnvironment; @@ -29,21 +30,9 @@ import art.arcane.iris.command.IrisCommandRegistry; import art.arcane.iris.world.entity.IrisEffect; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.Holder; -import net.minecraft.core.particles.ParticleType; -import net.minecraft.core.particles.SimpleParticleType; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.protocol.game.ClientboundSoundPacket; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.effect.MobEffect; -import net.minecraft.world.effect.MobEffectInstance; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.phys.Vec3; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativePlayerEffects; import java.util.ArrayList; import java.util.HashMap; @@ -110,7 +99,7 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement return; } - ServerLevel level = resolveLevel(); + NativeWorld level = resolveLevel(); if (level == null) { players.clear(); return; @@ -132,20 +121,19 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } } - private ServerLevel resolveLevel() { + private NativeWorld resolveLevel() { Engine engine = getEngine(); if (engine.isClosed() || !engine.getWorld().hasPlatformWorld()) { return null; } - Object nativeWorld = engine.getWorld().platformWorld().nativeHandle(); - return nativeWorld instanceof ServerLevel ? (ServerLevel) nativeWorld : null; + return engine.getWorld().platformWorld(); } - private void syncPlayers(ServerLevel level) { - List activePlayers = level.players(); + private void syncPlayers(NativeWorld level) { + List activePlayers = NativePlayerEffects.players(level); Set activeIds = new HashSet<>(Math.max(16, activePlayers.size() * 2)); - for (ServerPlayer player : activePlayers) { - UUID playerId = player.getUUID(); + for (NativeProtocolPlayer player : activePlayers) { + UUID playerId = player.id(); activeIds.add(playerId); PlayerState state = players.get(playerId); if (state == null) { @@ -164,7 +152,7 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } } - private void tickPlayers(ServerLevel level) { + private void tickPlayers(NativeWorld level) { if (players.isEmpty()) { syncPlayers(level); } else if (RNG.r.d() < PLAYER_REFRESH_CHANCE) { @@ -188,9 +176,9 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } } - private void tickPlayer(ServerLevel level, PlayerState state) { - ServerPlayer player = state.player(); - if (!player.isAlive() || player.isRemoved() || player.level() != level) { + private void tickPlayer(NativeWorld level, PlayerState state) { + NativeProtocolPlayer player = state.player(); + if (!NativePlayerEffects.alive(player) || player.removed() || !player.inWorld(level)) { return; } @@ -213,27 +201,27 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } private void samplePlayer(PlayerState state) { - ServerPlayer player = state.player(); - double deltaX = player.getX() - state.lastX(); - double deltaY = player.getY() - state.lastY(); - double deltaZ = player.getZ() - state.lastZ(); + NativeProtocolPlayer player = state.player(); + double deltaX = player.x() - state.lastX(); + double deltaY = player.y() - state.lastY(); + double deltaZ = player.z() - state.lastZ(); double distanceSquared = deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; long now = System.currentTimeMillis(); if (!needsSample(state.sampled(), now - state.lastSample(), distanceSquared)) { return; } - int blockX = floor(player.getX()); - int blockY = floor(player.getY()) - getEngine().getWorld().minHeight(); - int blockZ = floor(player.getZ()); + int blockX = floor(player.x()); + int blockY = floor(player.y()) - getEngine().getWorld().minHeight(); + int blockZ = floor(player.z()); BiomeEnvironment environment = getEngine().getBiomeEnvironment(blockX, blockY, blockZ); state.environment = environment; state.sampled(true); - state.lastPosition(player.getX(), player.getY(), player.getZ()); + state.lastPosition(player.x(), player.y(), player.z()); state.lastSample(now); } - private void applyEffects(ServerLevel level, ServerPlayer player, Iterable effects) { + private void applyEffects(NativeWorld level, NativeProtocolPlayer player, Iterable effects) { for (IrisEffect effect : effects) { try { applyEffect(level, player, effect); @@ -243,7 +231,7 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } } - private void applyEffect(ServerLevel level, ServerPlayer player, IrisEffect effect) { + private void applyEffect(NativeWorld level, NativeProtocolPlayer player, IrisEffect effect) { if (!effect.shouldApplyNow()) { return; } @@ -254,122 +242,112 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement applyPotion(player, effect); } - private void applySound(ServerPlayer player, IrisEffect effect) { + private void applySound(NativeProtocolPlayer player, IrisEffect effect) { String key = normalizeRegistryKey(effect.getSoundKey()); if (key == null) { return; } - Identifier identifier = Identifier.tryParse(key); - Optional> sound = identifier == null - ? Optional.empty() - : BuiltInRegistries.SOUND_EVENT.get(identifier); - if (sound.isEmpty()) { + NativePlayerEffects.Sound sound = NativePlayerEffects.sound(key); + if (sound == null) { warnUnknown(UNKNOWN_SOUNDS, "sound", key); return; } int distance = effect.getSoundDistance(); - double x = player.getX() + RNG.r.i(-distance, distance); - double y = player.getY() + RNG.r.i(-distance, distance); - double z = player.getZ() + RNG.r.i(-distance, distance); + double x = player.x() + RNG.r.i(-distance, distance); + double y = player.y() + RNG.r.i(-distance, distance); + double z = player.z() + RNG.r.i(-distance, distance); float volume = (float) effect.getVolume(); float pitch = (float) RNG.r.d(effect.getMinPitch(), effect.getMaxPitch()); - player.connection.send(new ClientboundSoundPacket( - sound.get(), SoundSource.MASTER, x, y, z, volume, pitch, ThreadLocalRandom.current().nextLong())); + NativePlayerEffects.sound(player, sound, new NativePlayerEffects.SoundEmission( + x, y, z, volume, pitch, ThreadLocalRandom.current().nextLong())); } - private void applyParticles(ServerLevel level, ServerPlayer player, IrisEffect effect) { + private void applyParticles(NativeWorld level, NativeProtocolPlayer player, IrisEffect effect) { String key = normalizeRegistryKey(effect.getParticleEffectKey()); if (key == null) { return; } - Identifier identifier = Identifier.tryParse(key); - ParticleType particleType = identifier == null - ? null - : BuiltInRegistries.PARTICLE_TYPE.getValue(identifier); + NativePlayerEffects.Particle particleType = NativePlayerEffects.particle(key); if (particleType == null) { warnUnknown(UNKNOWN_PARTICLES, "particle", key); return; } - if (!(particleType instanceof SimpleParticleType simpleParticle)) { + if (!particleType.simple()) { if (UNSUPPORTED_PARTICLES.add(key)) { IrisLogging.warn("Particle type \"" + key + "\" requires particle data and cannot be used by an Iris effect without data."); } return; } - Vec3 direction = player.getLookAngle(); + NativePlayerEffects.Direction direction = NativePlayerEffects.look(player); double forward = RNG.r.i(effect.getParticleDistance()) + effect.getParticleAway(); double sideways = RNG.r.d(-effect.getParticleDistanceWidth(), effect.getParticleDistanceWidth()); - double surfaceX = player.getX() + direction.x * forward + direction.z * sideways; - double surfaceZ = player.getZ() + direction.z * forward - direction.x * sideways; - if (level.getChunkSource().getChunkNow(floor(surfaceX) >> 4, floor(surfaceZ) >> 4) == null) { + double surfaceX = player.x() + direction.x() * forward + direction.z() * sideways; + double surfaceZ = player.z() + direction.z() * forward - direction.x() * sideways; + Integer surfaceY = NativePlayerEffects.loadedSurface(level, floor(surfaceX), floor(surfaceZ)); + if (surfaceY == null) { return; } - int surfaceY = level.getHeight(Heightmap.Types.OCEAN_FLOOR, floor(surfaceX), floor(surfaceZ)); double x = surfaceX + RNG.r.d(); double y = surfaceY + RNG.r.i(effect.getParticleOffset()); double z = surfaceZ + RNG.r.d(); double altX = randomized(effect.getParticleAltX(), effect.isRandomAltX()); double altY = randomized(effect.getParticleAltY(), effect.isRandomAltY()); double altZ = randomized(effect.getParticleAltZ(), effect.isRandomAltZ()); - level.sendParticles(player, simpleParticle, false, false, - x, y, z, effect.getParticleCount(), altX, altY, altZ, effect.getExtra()); + NativePlayerEffects.particles(player, particleType, new NativePlayerEffects.ParticleEmission( + x, y, z, effect.getParticleCount(), altX, altY, altZ, effect.getExtra())); } - private void applyCommands(ServerLevel level, ServerPlayer player, IrisCommandRegistry registry) { + private void applyCommands(NativeWorld level, NativeProtocolPlayer player, IrisCommandRegistry registry) { if (registry == null || registry.getRawCommands() == null || registry.getRawCommands().isEmpty()) { return; } - ModdedPlatformWorld world = new ModdedPlatformWorld(level); - double x = commandCoordinate(player.getX(), registry.getCommandOffsetX(), registry.isCommandRandomAltX()); - double y = commandCoordinate(player.getY(), registry.getCommandOffsetY(), registry.isCommandRandomAltY()); - double z = commandCoordinate(player.getZ(), registry.getCommandOffsetZ(), registry.isCommandRandomAltZ()); + double x = commandCoordinate(player.x(), registry.getCommandOffsetX(), registry.isCommandRandomAltX()); + double y = commandCoordinate(player.y(), registry.getCommandOffsetY(), registry.isCommandRandomAltY()); + double z = commandCoordinate(player.z(), registry.getCommandOffsetZ(), registry.isCommandRandomAltZ()); for (IrisCommand command : registry.getRawCommands()) { - command.run(world, floor(x), floor(y), floor(z)); + command.run(level, floor(x), floor(y), floor(z)); if (registry.isCommandAllRandomLocations()) { - x = commandCoordinate(player.getX(), registry.getCommandOffsetX(), registry.isCommandRandomAltX()); - y = commandCoordinate(player.getY(), registry.getCommandOffsetY(), registry.isCommandRandomAltY()); - z = commandCoordinate(player.getZ(), registry.getCommandOffsetZ(), registry.isCommandRandomAltZ()); + x = commandCoordinate(player.x(), registry.getCommandOffsetX(), registry.isCommandRandomAltX()); + y = commandCoordinate(player.y(), registry.getCommandOffsetY(), registry.isCommandRandomAltY()); + z = commandCoordinate(player.z(), registry.getCommandOffsetZ(), registry.isCommandRandomAltZ()); } } } - private void applyPotion(ServerPlayer player, IrisEffect effect) { + private void applyPotion(NativeProtocolPlayer player, IrisEffect effect) { int strength = effect.getPotionStrength(); if (strength < 0) { return; } - Holder type = resolvePotion(effect.getPotionEffect()); - MobEffectInstance current = player.getEffect(type); - if (current != null && !shouldReplacePotionEffect(current.getAmplifier(), strength)) { + NativePlayerEffects.Potion type = resolvePotion(effect.getPotionEffect()); + Integer current = NativePlayerEffects.amplifier(player, type); + if (current != null && !shouldReplacePotionEffect(current, strength)) { return; } if (current != null) { - player.removeEffect(type); + NativePlayerEffects.removePotion(player, type); } int minimum = Math.min(effect.getPotionTicksMin(), effect.getPotionTicksMax()); int maximum = Math.max(effect.getPotionTicksMin(), effect.getPotionTicksMax()); int duration = RNG.r.i(minimum, maximum); - player.addEffect(new MobEffectInstance(type, duration, strength, true, false, false)); + NativePlayerEffects.potion(player, type, new NativePlayerEffects.PotionEmission(duration, strength, true, false, false)); } - private Holder resolvePotion(String rawKey) { + private NativePlayerEffects.Potion resolvePotion(String rawKey) { String key = normalizePotionEffectKey(rawKey); - Identifier identifier = Identifier.tryParse(key); - if (identifier != null) { - Optional> effect = BuiltInRegistries.MOB_EFFECT.get(identifier); - if (effect.isPresent()) { - return effect.get(); - } + NativePlayerEffects.Potion potion = NativePlayerEffects.potion(key); + if (potion != null) { + return potion; } if (UNKNOWN_POTIONS.add(key)) { IrisLogging.warn("Unknown Potion Effect Type: \"" + rawKey + "\". Using LUCK instead."); } - return MobEffects.LUCK; + return NativePlayerEffects.potion("minecraft:luck"); } static String normalizeRegistryKey(String rawKey) { @@ -427,7 +405,7 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement } private static final class PlayerState { - private ServerPlayer player; + private NativeProtocolPlayer player; private BiomeEnvironment environment; private double lastX; private double lastY; @@ -435,20 +413,20 @@ public final class ModdedEngineEffects extends EngineAssignedComponent implement private long lastSample; private boolean sampled; - private PlayerState(ServerPlayer player) { + private PlayerState(NativeProtocolPlayer player) { this.player = player; - lastX = player.getX(); - lastY = player.getY(); - lastZ = player.getZ(); + lastX = player.x(); + lastY = player.y(); + lastZ = player.z(); lastSample = -1L; sampled = false; } - private ServerPlayer player() { + private NativeProtocolPlayer player() { return player; } - private void player(ServerPlayer player) { + private void player(NativeProtocolPlayer player) { this.player = player; } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityAwareness.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityAwareness.java deleted file mode 100644 index c72991497..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityAwareness.java +++ /dev/null @@ -1,39 +0,0 @@ -package art.arcane.iris.modded; - -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.world.entity.Mob; - -import java.util.Set; - -public final class ModdedEntityAwareness { - private static final String UNAWARE_TAG = "iris_unaware"; - - private ModdedEntityAwareness() { - } - - public static void configure(Mob mob, boolean aware) { - if (aware) { - mob.removeTag(UNAWARE_TAG); - return; - } - if (!mob.entityTags().contains(UNAWARE_TAG) && !mob.addTag(UNAWARE_TAG)) { - IrisLogging.warn("Iris could not mark generated mob '" + mob.getStringUUID() + "' as unaware"); - } - } - - public static boolean isAware(Mob mob) { - return isAware(mob.entityTags()); - } - - static boolean isAware(Set tags) { - return !tags.contains(UNAWARE_TAG); - } - - static void configureTags(Set tags, boolean aware) { - if (aware) { - tags.remove(UNAWARE_TAG); - } else { - tags.add(UNAWARE_TAG); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityCommandRunner.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityCommandRunner.java index 695730fd4..b7ef2537f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityCommandRunner.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityCommandRunner.java @@ -1,36 +1,36 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandExecutor; + import art.arcane.iris.command.IrisCommand; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; final class ModdedEntityCommandRunner { private ModdedEntityCommandRunner() { } - static void run(KList commands, ServerLevel level, int blockX, int blockY, int blockZ) { + static void run(KList commands, NativeWorld world, int blockX, int blockY, int blockZ) { if (commands.isEmpty()) { return; } - MinecraftServer server = level.getServer(); + NativeCommandExecutor executor = NativeCommandExecutor.fromWorld(world, IrisLogging::reportError); ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull(); - if (server == null || scheduler == null) { + if (executor == null || scheduler == null) { IrisLogging.error("Iris could not schedule entity commands because the modded server scheduler is unavailable."); return; } - ModdedPlatformWorld world = new ModdedPlatformWorld(level); for (IrisCommand command : commands) { if (command == null || !command.isValid(world)) { continue; } - schedule(command, server, scheduler, blockX, blockY, blockZ); + schedule(command, executor, scheduler, blockX, blockY, blockZ); } } - private static void schedule(IrisCommand command, MinecraftServer server, ModdedScheduler scheduler, int blockX, int blockY, int blockZ) { + private static void schedule(IrisCommand command, NativeCommandExecutor executor, ModdedScheduler scheduler, int blockX, int blockY, int blockZ) { int delay = clampDelay(command.getDelay(), 0); int repeatDelay = clampDelay(command.getRepeatDelay(), 1); for (String raw : command.getCommands()) { @@ -39,9 +39,9 @@ final class ModdedEntityCommandRunner { continue; } if (command.isRepeat()) { - scheduler.laterGlobal(() -> new RepeatingCommand(server, scheduler, prepared, repeatDelay).run(), delay); + scheduler.laterGlobal(() -> new RepeatingCommand(executor, scheduler, prepared, repeatDelay).run(), delay); } else { - scheduler.laterGlobal(() -> ModdedServerCommands.dispatch(server, prepared), delay); + scheduler.laterGlobal(() -> executor.dispatch(prepared), delay); } } } @@ -61,13 +61,13 @@ final class ModdedEntityCommandRunner { } private static final class RepeatingCommand implements Runnable { - private final MinecraftServer server; + private final NativeCommandExecutor executor; private final ModdedScheduler scheduler; private final String command; private final int interval; - private RepeatingCommand(MinecraftServer server, ModdedScheduler scheduler, String command, int interval) { - this.server = server; + private RepeatingCommand(NativeCommandExecutor executor, ModdedScheduler scheduler, String command, int interval) { + this.executor = executor; this.scheduler = scheduler; this.command = command; this.interval = interval; @@ -75,7 +75,7 @@ final class ModdedEntityCommandRunner { @Override public void run() { - ModdedServerCommands.dispatch(server, command); + executor.dispatch(command); scheduler.laterGlobal(this, interval); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityPersistence.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityPersistence.java deleted file mode 100644 index 362bed5e7..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityPersistence.java +++ /dev/null @@ -1,43 +0,0 @@ -package art.arcane.iris.modded; - -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.Mob; - -import java.util.Set; - -public final class ModdedEntityPersistence { - private static final String NON_PERSISTENT_TAG = "iris_non_persistent"; - - private ModdedEntityPersistence() { - } - - public static void configure(Entity entity, boolean persistent) { - if (persistent) { - entity.removeTag(NON_PERSISTENT_TAG); - if (entity instanceof Mob mob) { - mob.setPersistenceRequired(); - } - return; - } - if (!entity.entityTags().contains(NON_PERSISTENT_TAG) && !entity.addTag(NON_PERSISTENT_TAG)) { - IrisLogging.warn("Iris could not mark generated entity '" + entity.getStringUUID() + "' as non-persistent"); - } - } - - public static boolean shouldSave(Entity entity, boolean vanillaResult) { - return shouldSave(entity.entityTags(), vanillaResult); - } - - static boolean shouldSave(Set tags, boolean vanillaResult) { - return vanillaResult && !tags.contains(NON_PERSISTENT_TAG); - } - - static void configureTags(Set tags, boolean persistent) { - if (persistent) { - tags.remove(NON_PERSISTENT_TAG); - } else { - tags.add(NON_PERSISTENT_TAG); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntitySpawner.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntitySpawner.java index 93da76a3c..27c61977b 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntitySpawner.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntitySpawner.java @@ -19,54 +19,23 @@ package art.arcane.iris.modded; import art.arcane.iris.configuration.IrisSettings; +import art.arcane.iris.generation.decoration.IrisSurface; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.placement.LootResolver; -import art.arcane.iris.world.entity.IrisAttributeModifier; -import art.arcane.iris.world.entity.IrisEntity; -import art.arcane.iris.world.entity.IrisEffect; -import art.arcane.iris.world.loot.IrisLoot; import art.arcane.iris.modded.api.ModdedCustomContentRegistry; import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.structure.placement.LootResolver; +import art.arcane.iris.world.entity.IrisAttributeModifier; +import art.arcane.iris.world.entity.IrisEffect; +import art.arcane.iris.world.entity.IrisEntity; +import art.arcane.iris.world.loot.IrisLoot; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnedEntity; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.particles.ItemParticleOption; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.core.particles.ParticleType; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.core.particles.SimpleParticleType; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.Difficulty; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntitySpawnReason; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeInstance; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; -import net.minecraft.world.entity.animal.panda.Panda; -import net.minecraft.world.entity.npc.villager.Villager; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.AABB; - import java.util.Locale; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; public final class ModdedEntitySpawner { private static final int PASSENGER_RNG_BASE = 234858; @@ -83,7 +52,7 @@ public final class ModdedEntitySpawner { private ModdedEntitySpawner() { } - public static Entity spawn(Engine engine, IrisEntity irisEntity, ServerLevel level, int blockX, int blockY, int blockZ, RNG rng) { + public static NativeSpawnedEntity spawn(Engine engine, IrisEntity irisEntity, NativeEntityRuntime level, int blockX, int blockY, int blockZ, RNG rng) { if (engine == null || irisEntity == null || level == null) { return null; } @@ -94,11 +63,11 @@ public final class ModdedEntitySpawner { double x = blockX + 0.5; double y = blockY + 0.5; double z = blockZ + 0.5; - boolean riseEffectActive = irisEntity.isSpawnEffectRiseOutOfGround() && hasPlayersNearby(level, x, y, z); + boolean riseEffectActive = irisEntity.isSpawnEffectRiseOutOfGround() && level.hasPlayersNearby(new NativeEntityRuntime.Position(x, y, z), PLAYER_EFFECT_RADIUS); int spawnBlockY = riseEffectActive ? blockY - 5 : blockY; double spawnY = spawnBlockY + 0.5; - Entity created = create(irisEntity, level, x, spawnY, z); + NativeSpawnedEntity created = create(irisEntity, level, x, spawnY, z); if (created == null) { return null; } @@ -111,299 +80,175 @@ public final class ModdedEntitySpawner { return created; } - private static Entity create(IrisEntity irisEntity, ServerLevel level, double x, double y, double z) { + private static NativeSpawnedEntity create(IrisEntity irisEntity, NativeEntityRuntime level, double x, double y, double z) { if (irisEntity.isSpecialType()) { - return ModdedCustomContentRegistry.spawnMob(level, x, y, z, irisEntity.getSpecialType()); + return level.spawnCustom(irisEntity.getSpecialType(), new NativeEntityRuntime.Position(x, y, z), + ModdedCustomContentRegistry::spawnMob); } - EntityType type = resolveType(irisEntity.getType()); + NativeEntityRuntime.Type type = resolveType(irisEntity.getType()); if (type == null) { return null; } - Entity entity = spawnNative(type, level, BlockPos.containing(x, y, z), reasonFor(irisEntity.getReason())); - if (entity != null) { - entity.snapTo(x, y, z, 0F, 0F); - } - return entity; + return level.spawn(type, new NativeEntityRuntime.Position(x, y, z), irisEntity.getReason()); } - static Entity spawnNative(EntityType type, ServerLevel level, BlockPos position, EntitySpawnReason reason) { - if (level.getDifficulty() == Difficulty.PEACEFUL && !type.isAllowedInPeaceful()) { - return null; - } - return type.spawn(level, position, reason); - } - - private static void applyConfig(Engine engine, IrisEntity irisEntity, Entity entity, ServerLevel level, int blockX, int blockY, int blockZ, RNG rng, boolean riseEffectActive) { + private static void applyConfig(Engine engine, IrisEntity irisEntity, NativeSpawnedEntity entity, NativeEntityRuntime level, int blockX, int blockY, int blockZ, RNG rng, boolean riseEffectActive) { String customName = irisEntity.getCustomName(); - if (customName != null) { - entity.setCustomName(Component.literal(colorize(customName))); - } - entity.setCustomNameVisible(irisEntity.isCustomNameVisible()); - entity.setGlowingTag(irisEntity.isGlowing()); - entity.setNoGravity(!irisEntity.isGravity()); - entity.setInvulnerable(irisEntity.isInvulnerable()); - entity.setSilent(irisEntity.isSilent()); + entity.applyBase(irisEntity, customName == null ? null : colorize(customName)); boolean persistent = irisEntity.isKeepEntity() || forcePersist(); applyPersistence(entity, persistent); applyPassengers(engine, irisEntity, entity, level, blockX, blockY, blockZ, rng); - if (entity instanceof LivingEntity living) { - applyAttributes(irisEntity, living, level, rng); + if (entity.living()) { + applyAttributes(irisEntity, entity, level, rng); } if (!irisEntity.getLoot().getTables().isEmpty()) { ModdedDeathLoot.bind(engine, entity, irisEntity.getLoot().getTables(), blockX, blockY, blockZ, rng); } - if (entity instanceof Mob mob) { - mob.setNoAi(shouldDisableAi(irisEntity.isAi())); - ModdedEntityAwareness.configure(mob, irisEntity.isAware()); - mob.setCanPickUpLoot(irisEntity.isPickupItems()); - if (!irisEntity.isRemovable()) { - mob.setPersistenceRequired(); - } + if (entity.mob()) { + entity.configureMob(irisEntity); if (irisEntity.getLeashHolder() != null) { - Entity holder = spawn(engine, irisEntity.getLeashHolder(), level, blockX, blockY, blockZ, rng.nextParallelRNG(LEASH_RNG_SEED)); + NativeSpawnedEntity holder = spawn(engine, irisEntity.getLeashHolder(), level, blockX, blockY, blockZ, rng.nextParallelRNG(LEASH_RNG_SEED)); if (holder != null) { - mob.setLeashedTo(holder, true); + entity.leashTo(holder); } } } - if (entity instanceof LivingEntity equippable) { - applyEquipment(irisEntity, equippable, level, rng); + if (entity.living()) { + applyEquipment(irisEntity, entity, level, rng); } - if (irisEntity.isBaby()) { - applyBaby(entity); - } - - if (entity instanceof Panda panda) { - panda.setMainGene(gene(irisEntity.getPandaMainGene())); - panda.setHiddenGene(gene(irisEntity.getPandaHiddenGene())); - } - - if (entity instanceof Villager villager) { - ModdedEntityPersistence.configure(villager, true); - } + entity.configureAnimal(irisEntity); applySpawnEffect(irisEntity.getSpawnEffect(), entity, level); - ModdedEntityCommandRunner.run(irisEntity.getRawCommands(), level, blockX, blockY, blockZ); - if (riseEffectActive && entity instanceof LivingEntity living) { - startRiseEffect(engine, level, entity, living); + ModdedEntityCommandRunner.run(irisEntity.getRawCommands(), level.world(), blockX, blockY, blockZ); + if (riseEffectActive && entity.living()) { + startRiseEffect(engine, level, entity); } } - private static void applyPassengers(Engine engine, IrisEntity irisEntity, Entity entity, ServerLevel level, int blockX, int blockY, int blockZ, RNG rng) { + private static void applyPassengers(Engine engine, IrisEntity irisEntity, NativeSpawnedEntity entity, NativeEntityRuntime level, int blockX, int blockY, int blockZ, RNG rng) { int index = 0; for (IrisEntity passengerEntity : irisEntity.getPassengers()) { - Entity passenger = spawn(engine, passengerEntity, level, blockX, blockY, blockZ, rng.nextParallelRNG(PASSENGER_RNG_BASE + index++)); + NativeSpawnedEntity passenger = spawn(engine, passengerEntity, level, blockX, blockY, blockZ, rng.nextParallelRNG(PASSENGER_RNG_BASE + index++)); if (passenger != null) { - passenger.startRiding(entity); + passenger.ride(entity); } } } - private static void applyAttributes(IrisEntity irisEntity, LivingEntity living, ServerLevel level, RNG rng) { + private static void applyAttributes(IrisEntity irisEntity, NativeSpawnedEntity living, NativeEntityRuntime level, RNG rng) { KList modifiers = irisEntity.getAttributes(); if (modifiers.isEmpty()) { return; } - Registry registry = level.registryAccess().lookupOrThrow(Registries.ATTRIBUTE); int index = 0; for (IrisAttributeModifier modifier : modifiers) { index++; if (rng.nextDouble() >= modifier.getChance()) { continue; } - Holder holder = ModdedItemTranslator.resolveAttribute(registry, modifier.getAttribute()); - if (holder == null) { - if (WARNED_ATTRIBUTES.add(modifier.getAttribute())) { - IrisLogging.warn("Iris entity: unknown attribute '" + modifier.getAttribute() + "'"); - } - continue; + String id = "iris:" + normalizeName(modifier.getName()) + "_" + index; + if (!living.applyAttribute(level, modifier.getAttribute(), id, modifier.getOperation(), () -> modifier.getAmount(rng)) + && WARNED_ATTRIBUTES.add(modifier.getAttribute())) { + IrisLogging.warn("Iris entity: unknown attribute '" + modifier.getAttribute() + "'"); } - AttributeInstance instance = living.getAttributes().getInstance(holder); - if (instance == null) { - continue; - } - Identifier id = Identifier.tryParse("iris:" + normalizeName(modifier.getName()) + "_" + index); - if (id == null) { - continue; - } - instance.addOrReplacePermanentModifier(new AttributeModifier(id, modifier.getAmount(rng), ModdedItemTranslator.operationFor(modifier.getOperation()))); } } - private static void applyEquipment(IrisEntity irisEntity, LivingEntity living, ServerLevel level, RNG rng) { - setSlot(living, EquipmentSlot.HEAD, irisEntity.getHelmet(), level, rng); - setSlot(living, EquipmentSlot.CHEST, irisEntity.getChestplate(), level, rng); - setSlot(living, EquipmentSlot.LEGS, irisEntity.getLeggings(), level, rng); - setSlot(living, EquipmentSlot.FEET, irisEntity.getBoots(), level, rng); - setSlot(living, EquipmentSlot.MAINHAND, irisEntity.getMainHand(), level, rng); - setSlot(living, EquipmentSlot.OFFHAND, irisEntity.getOffHand(), level, rng); + private static void applyEquipment(IrisEntity irisEntity, NativeSpawnedEntity living, NativeEntityRuntime level, RNG rng) { + setSlot(living, NativeSpawnedEntity.Equipment.HEAD, irisEntity.getHelmet(), level, rng); + setSlot(living, NativeSpawnedEntity.Equipment.CHEST, irisEntity.getChestplate(), level, rng); + setSlot(living, NativeSpawnedEntity.Equipment.LEGS, irisEntity.getLeggings(), level, rng); + setSlot(living, NativeSpawnedEntity.Equipment.FEET, irisEntity.getBoots(), level, rng); + setSlot(living, NativeSpawnedEntity.Equipment.MAINHAND, irisEntity.getMainHand(), level, rng); + setSlot(living, NativeSpawnedEntity.Equipment.OFFHAND, irisEntity.getOffHand(), level, rng); } - private static void setSlot(LivingEntity living, EquipmentSlot slot, IrisLoot loot, ServerLevel level, RNG rng) { + private static void setSlot(NativeSpawnedEntity living, NativeSpawnedEntity.Equipment slot, IrisLoot loot, NativeEntityRuntime level, RNG rng) { if (loot == null || !LootResolver.oneIn(rng, loot.getRarity())) { return; } - ItemStack stack = ModdedItemTranslator.stack(loot, rng, level); - if (stack != null && !stack.isEmpty()) { - living.setItemSlot(slot, stack); - } + NativeItemStack nativeStack = ModdedItemTranslator.stack(loot, rng, ModdedItemTranslator.context(level.world())); + living.equip(slot, nativeStack); } - private static void applyBaby(Entity entity) { - if (entity instanceof Mob mob) { - mob.setBaby(true); - } - } - - static boolean isAreaClearForSpawn(ServerLevel level, IrisEntity irisEntity, int blockX, int blockY, int blockZ) { + static boolean isAreaClearForSpawn(NativeEntityRuntime level, IrisEntity irisEntity, int blockX, int blockY, int blockZ) { if (irisEntity.isSpecialType()) { return true; } - EntityType type = resolveType(irisEntity.getType()); + NativeEntityRuntime.Type type = resolveType(irisEntity.getType()); if (type == null) { return true; } if (irisEntity.getSurface().isFluid()) { - return isFluidAreaClearForSpawn(blockX, blockY, blockZ, type.getWidth(), type.getHeight(), - position -> ModdedWorldManager.matchesSurface(irisEntity.getSurface(), level.getBlockState(position))); + return NativeEntityRuntime.isFluidAreaClearForSpawn(blockX, blockY, blockZ, type.width(), type.height(), + (x, y, z) -> level.fluid(x, y, z, irisEntity.getSurface() == IrisSurface.LAVA)); } - return isAreaClearForSpawn(blockX, blockY, blockZ, type.getWidth(), type.getHeight(), - position -> level.getBlockState(position).is(Blocks.AIR)); + return NativeEntityRuntime.isAreaClearForSpawn(blockX, blockY, blockZ, type.width(), type.height(), level::air); } - static boolean isFluidAreaClearForSpawn(int blockX, int blockY, int blockZ, - float width, float height, Predicate matchesFluid) { - int startX = (int) Math.floor(blockX + 0.5 - width / 2D); - int endX = (int) Math.floor(Math.nextDown(blockX + 0.5 + width / 2D)); - int endY = (int) Math.floor(Math.nextDown(blockY + 0.5 + height)); - int startZ = (int) Math.floor(blockZ + 0.5 - width / 2D); - int endZ = (int) Math.floor(Math.nextDown(blockZ + 0.5 + width / 2D)); - for (int x = startX; x <= endX; x++) { - for (int y = blockY; y <= endY; y++) { - for (int z = startZ; z <= endZ; z++) { - if (!matchesFluid.test(new BlockPos(x, y, z))) { - return false; - } - } - } - } - return true; + static boolean chunksSafe(NativeEntityRuntime level, int chunkX, int chunkZ) { + return level.chunksSafe(chunkX, chunkZ); } - static boolean isAreaClearForSpawn(int blockX, int blockY, int blockZ, float width, float height, Predicate isAir) { - int radius = (int) (width / 2F); - int endY = blockY + (int) height; - for (int x = blockX - radius; x <= blockX + radius; x++) { - for (int y = blockY; y <= endY; y++) { - for (int z = blockZ - radius; z <= blockZ + radius; z++) { - if (!isAir.test(new BlockPos(x, y, z))) { - return false; - } - } - } - } - return true; - } - - static boolean chunksSafe(ServerLevel level, int chunkX, int chunkZ) { - return allNeighborChunksLoaded(chunkX, chunkZ, - (x, z) -> level.getChunkSource().getChunkNow(x, z) != null); - } - - static boolean allNeighborChunksLoaded(int chunkX, int chunkZ, ChunkLoadedLookup lookup) { - for (int x = chunkX - 1; x <= chunkX + 1; x++) { - for (int z = chunkZ - 1; z <= chunkZ + 1; z++) { - if (!lookup.loaded(x, z)) { - return false; - } - } - } - return true; - } - - static boolean shouldDisableAi(boolean ai) { - return !ai; - } - - private static boolean hasPlayersNearby(ServerLevel level, double x, double y, double z) { - AABB bounds = new AABB( - x - PLAYER_EFFECT_RADIUS, - y - PLAYER_EFFECT_RADIUS, - z - PLAYER_EFFECT_RADIUS, - x + PLAYER_EFFECT_RADIUS, - y + PLAYER_EFFECT_RADIUS, - z + PLAYER_EFFECT_RADIUS); - for (ServerPlayer player : level.players()) { - if (player.getBoundingBox().intersects(bounds)) { - return true; - } - } - return false; - } - - private static void applySpawnEffect(IrisEffect effect, Entity entity, ServerLevel level) { + private static void applySpawnEffect(IrisEffect effect, NativeSpawnedEntity entity, NativeEntityRuntime level) { if (effect == null || !effect.shouldApplyNow()) { return; } - SoundEvent sound = resolveSound(effect.getSoundKey()); + NativeEntityRuntime.Sound sound = resolveSound(effect.getSoundKey()); if (sound != null) { - double soundX = entity.getX() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); - double soundY = entity.getY() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); - double soundZ = entity.getZ() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); - level.playSound(null, soundX, soundY, soundZ, sound, SoundSource.MASTER, - (float) effect.getVolume(), (float) RNG.r.d(effect.getMinPitch(), effect.getMaxPitch())); + double soundX = entity.x() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); + double soundY = entity.y() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); + double soundZ = entity.z() + RNG.r.i(-effect.getSoundDistance(), effect.getSoundDistance()); + level.sound(sound, new NativeEntityRuntime.SoundEmission(soundX, soundY, soundZ, + (float) effect.getVolume(), (float) RNG.r.d(effect.getMinPitch(), effect.getMaxPitch()))); } - ParticleOptions particle = resolveParticle(effect.getParticleEffectKey()); + NativeEntityRuntime.Particle particle = resolveParticle(effect.getParticleEffectKey()); if (particle == null) { return; } double addition = RNG.r.d(); double subtraction = RNG.r.d(); - double particleX = entity.getX() + addition - subtraction + RNG.r.d(); - double particleY = entity.getY() + 0.25 + addition - subtraction + RNG.r.i(effect.getParticleOffset()); - double particleZ = entity.getZ() + addition - subtraction + RNG.r.d(); + double particleX = entity.x() + addition - subtraction + RNG.r.d(); + double particleY = entity.y() + 0.25 + addition - subtraction + RNG.r.i(effect.getParticleOffset()); + double particleZ = entity.z() + addition - subtraction + RNG.r.d(); double altX = effect.isRandomAltX() ? RNG.r.d(-effect.getParticleAltX(), effect.getParticleAltX()) : effect.getParticleAltX(); double altY = effect.isRandomAltY() ? RNG.r.d(-effect.getParticleAltY(), effect.getParticleAltY()) : effect.getParticleAltY(); double altZ = effect.isRandomAltZ() ? RNG.r.d(-effect.getParticleAltZ(), effect.getParticleAltZ()) : effect.getParticleAltZ(); - level.sendParticles(particle, particleX, particleY, particleZ, effect.getParticleCount(), - altX, altY, altZ, effect.getExtra()); + level.particle(particle, new NativeEntityRuntime.ParticleEmission(particleX, particleY, particleZ, + effect.getParticleCount(), altX, altY, altZ, effect.getExtra())); } - private static SoundEvent resolveSound(String key) { - Identifier id = identifierFor(key); - if (id == null || !BuiltInRegistries.SOUND_EVENT.containsKey(id)) { - if (key != null && WARNED_EFFECT_SOUNDS.add(key)) { - IrisLogging.warn("Iris entity effect: unknown sound '" + key + "'"); - } - return null; + private static NativeEntityRuntime.Sound resolveSound(String key) { + NativeEntityRuntime.Sound sound = NativeEntityRuntime.resolveSound(key); + if (sound == null && key != null && WARNED_EFFECT_SOUNDS.add(key)) { + IrisLogging.warn("Iris entity effect: unknown sound '" + key + "'"); } - return BuiltInRegistries.SOUND_EVENT.getValue(id); + return sound; } - private static ParticleOptions resolveParticle(String key) { - Identifier id = identifierFor(key); - if (id == null || !BuiltInRegistries.PARTICLE_TYPE.containsKey(id)) { + private static NativeEntityRuntime.Particle resolveParticle(String key) { + NativeEntityRuntime.Particle particle = NativeEntityRuntime.resolveParticle(key); + if (particle == null) { if (key != null && WARNED_EFFECT_PARTICLES.add(key)) { IrisLogging.warn("Iris entity effect: unknown particle '" + key + "'"); } return null; } - ParticleType type = BuiltInRegistries.PARTICLE_TYPE.getValue(id); - if (type instanceof SimpleParticleType simple) { - return simple; + if (particle.simple()) { + return particle; } if (WARNED_EFFECT_PARTICLES.add(key)) { IrisLogging.warn("Iris entity effect: particle '" + key + "' requires data that the effect does not define"); @@ -411,84 +256,26 @@ public final class ModdedEntitySpawner { return null; } - private static Identifier identifierFor(String key) { - if (key == null || key.isBlank()) { - return null; - } - String normalized = key.trim().toLowerCase(Locale.ROOT); - return Identifier.tryParse(normalized.indexOf(':') >= 0 ? normalized : "minecraft:" + normalized); - } - - private static void startRiseEffect(Engine engine, ServerLevel level, Entity entity, LivingEntity living) { - RiseTask task = new RiseTask(engine, level, entity, living); + private static void startRiseEffect(Engine engine, NativeEntityRuntime level, NativeSpawnedEntity entity) { + RiseTask task = new RiseTask(new RiseContext(engine, level, entity)); task.start(); } - static EntityType resolveType(String key) { - if (key == null || key.isBlank()) { - return null; - } - String normalized = key.trim().toLowerCase(Locale.ROOT); - Identifier id = identifierFor(normalized); - if (id == null || !BuiltInRegistries.ENTITY_TYPE.containsKey(id)) { - if (WARNED_TYPES.add(normalized)) { - IrisLogging.warn("Iris entity: unknown entity type '" + key + "'; skipping spawn"); - } - return null; - } - return BuiltInRegistries.ENTITY_TYPE.getValue(id); - } - - static EntitySpawnReason reasonFor(String reason) { - if (reason == null || reason.isBlank()) { - return EntitySpawnReason.NATURAL; - } - String value = reason.trim().toUpperCase(Locale.ROOT); - return switch (value) { - case "NATURAL", "DEFAULT" -> EntitySpawnReason.NATURAL; - case "JOCKEY", "MOUNT" -> EntitySpawnReason.JOCKEY; - case "CHUNK_GEN" -> EntitySpawnReason.CHUNK_GENERATION; - case "SPAWNER" -> EntitySpawnReason.SPAWNER; - case "TRIAL_SPAWNER" -> EntitySpawnReason.TRIAL_SPAWNER; - case "EGG", "BUILD_SNOWMAN", "BUILD_IRONGOLEM", "BUILD_COPPERGOLEM", "BUILD_WITHER", - "SLIME_SPLIT", "SILVERFISH_BLOCK", "TRAP", "ENDER_PEARL", "EXPLOSION", - "ENCHANTMENT", "OMINOUS_ITEM_SPAWNER", "POTION_EFFECT", "REANIMATE" -> EntitySpawnReason.TRIGGERED; - case "SPAWNER_EGG" -> EntitySpawnReason.SPAWN_ITEM_USE; - case "LIGHTNING", "VILLAGE_INVASION", "RAID", "CUSTOM" -> EntitySpawnReason.EVENT; - case "VILLAGE_DEFENSE", "SPELL" -> EntitySpawnReason.MOB_SUMMONED; - case "BREEDING", "OCELOT_BABY", "DUPLICATION", "REHYDRATION" -> EntitySpawnReason.BREEDING; - case "REINFORCEMENTS" -> EntitySpawnReason.REINFORCEMENT; - case "NETHER_PORTAL" -> EntitySpawnReason.STRUCTURE; - case "DISPENSE_EGG" -> EntitySpawnReason.DISPENSER; - case "INFECTION", "CURED", "DROWNED", "SHEARED", "PIGLIN_ZOMBIFIED", "FROZEN", - "METAMORPHOSIS" -> EntitySpawnReason.CONVERSION; - case "SHOULDER_ENTITY", "BEEHIVE" -> EntitySpawnReason.LOAD; - case "PATROL" -> EntitySpawnReason.PATROL; - case "COMMAND" -> EntitySpawnReason.COMMAND; - case "BUCKET" -> EntitySpawnReason.BUCKET; - default -> EntitySpawnReason.NATURAL; - }; - } - - private static Panda.Gene gene(String value) { - if (value == null || value.isBlank()) { - return Panda.Gene.NORMAL; - } - try { - return Panda.Gene.valueOf(value.trim().toUpperCase(Locale.ROOT)); - } catch (IllegalArgumentException ignored) { - return Panda.Gene.NORMAL; + private static NativeEntityRuntime.Type resolveType(String key) { + NativeEntityRuntime.Type type = NativeEntityRuntime.resolveType(key); + if (type == null && key != null && !key.isBlank() && WARNED_TYPES.add(key.trim().toLowerCase(Locale.ROOT))) { + IrisLogging.warn("Iris entity: unknown entity type '" + key + "'; skipping spawn"); } + return type; } private static boolean forcePersist() { return IrisSettings.get().getWorld().isForcePersistEntities(); } - private static void applyPersistence(Entity entity, boolean persistent) { - ModdedEntityPersistence.configure(entity, persistent); - if (persistent && (!entity.getType().canSerialize() || !entity.shouldBeSaved())) { - String type = String.valueOf(BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType())); + private static void applyPersistence(NativeSpawnedEntity entity, boolean persistent) { + if (!entity.persistence(persistent)) { + String type = entity.typeKey(); if (WARNED_PERSISTENCE_TYPES.add(type)) { IrisLogging.warn("Iris entity: vanilla cannot persist non-serializable entity type '" + type + "'"); } @@ -512,71 +299,57 @@ public final class ModdedEntitySpawner { return new String(chars); } - @FunctionalInterface - interface ChunkLoadedLookup { - boolean loaded(int chunkX, int chunkZ); + private record RiseContext(Engine engine, NativeEntityRuntime level, NativeSpawnedEntity entity) { } private static final class RiseTask implements Runnable { private final Engine engine; - private final ServerLevel level; - private final Entity entity; - private final LivingEntity living; + private final NativeEntityRuntime level; + private final NativeSpawnedEntity entity; + private final NativeEntityRuntime.Sound riseSound; private final double x; private final double z; - private final boolean originalInvulnerable; - private final boolean originalNoPhysics; - private final int originalInvulnerableTime; - private final boolean originalNoAi; + private NativeSpawnedEntity.MotionState original; private double y; private int moves; private boolean restored; - private RiseTask(Engine engine, ServerLevel level, Entity entity, LivingEntity living) { - this.engine = engine; - this.level = level; - this.entity = entity; - this.living = living; - this.x = entity.getX(); - this.y = entity.getY(); - this.z = entity.getZ(); - this.originalInvulnerable = entity.isInvulnerable(); - this.originalNoPhysics = entity.noPhysics; - this.originalInvulnerableTime = entity.invulnerableTime; - this.originalNoAi = entity instanceof Mob mob && mob.isNoAi(); + private RiseTask(RiseContext context) { + engine = context.engine(); + level = context.level(); + entity = context.entity(); + riseSound = NativeEntityRuntime.resolveSound("minecraft:block.chorus_flower.grow"); + x = entity.x(); + y = entity.y(); + z = entity.z(); } private void start() { - entity.setInvulnerable(true); - entity.noPhysics = true; - entity.invulnerableTime = 100_000; - if (entity instanceof Mob mob) { - mob.setNoAi(true); - } + original = entity.pausePhysics(100_000); run(); } @Override public void run() { - if (moves >= RISE_MAX_MOVES || !entity.isAlive() || engine.isClosed() || entity.level() != level) { + if (moves >= RISE_MAX_MOVES || !entity.alive() || engine.isClosed() || !entity.inWorld(level)) { restore(); return; } - BlockPos current = entity.blockPosition(); - if (level.getChunkSource().getChunkNow(current.getX() >> 4, current.getZ() >> 4) == null) { + int blockX = entity.blockX(); + int blockY = entity.blockY(); + int blockZ = entity.blockZ(); + if (!level.chunkLoaded(blockX >> 4, blockZ >> 4)) { restore(); return; } - BlockPos eye = BlockPos.containing(entity.getX(), living.getEyeY(), entity.getZ()); - if (!ModdedBlockResolution.isSolid(level.getBlockState(current)) - && !ModdedBlockResolution.isSolid(level.getBlockState(eye))) { + if (!level.solid(blockX, blockY, blockZ) + && !level.solid(blockX, (int) Math.floor(entity.eyeY()), blockZ)) { restore(); return; } - moves++; y += 0.1; - entity.setPos(x, y, z); + entity.position(x, y, z); emitEffects(); ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull(); @@ -589,27 +362,18 @@ public final class ModdedEntitySpawner { } private void emitEffects() { - BlockPos source = BlockPos.containing(entity.getX(), living.getEyeY() - 2, entity.getZ()); - BlockState state = level.getBlockState(source); - ItemParticleOption item = new ItemParticleOption(ParticleTypes.ITEM, state.getBlock().asItem()); - level.sendParticles(item, entity.getX(), living.getEyeY(), entity.getZ(), 6, - 0.2, 0.4, 0.2, 0.06); + level.blockItemParticles(new NativeEntityRuntime.Position(entity.x(), entity.eyeY() - 2, entity.z()), + new NativeEntityRuntime.ParticleEmission(entity.x(), entity.eyeY(), entity.z(), 6, 0.2, 0.4, 0.2, 0.06)); if (RNG.r.nextDouble() < 0.2) { - level.playSound(null, entity.getX(), entity.getY(), entity.getZ(), SoundEvents.CHORUS_FLOWER_GROW, - SoundSource.MASTER, 0.8F, 0.1F); + level.sound(riseSound, + new NativeEntityRuntime.SoundEmission(entity.x(), entity.y(), entity.z(), 0.8F, 0.1F)); } } private void restore() { - if (restored) { - return; - } - restored = true; - entity.invulnerableTime = originalInvulnerableTime; - entity.noPhysics = originalNoPhysics; - entity.setInvulnerable(originalInvulnerable); - if (entity instanceof Mob mob) { - mob.setNoAi(originalNoAi); + if (!restored) { + restored = true; + entity.restoreMotion(original); } } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityType.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityType.java deleted file mode 100644 index 7d4c28b35..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedEntityType.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.PlatformEntityType; -import net.minecraft.world.entity.EntityType; - -import java.util.Locale; -import java.util.concurrent.ConcurrentHashMap; - -public final class ModdedEntityType implements PlatformEntityType { - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); - - private final EntityType type; - private final String key; - private final String namespace; - - private ModdedEntityType(EntityType type, String key) { - this.type = type; - this.key = key; - int colon = key.indexOf(':'); - this.namespace = colon >= 0 ? key.substring(0, colon) : "minecraft"; - } - - public static ModdedEntityType of(EntityType type, String key) { - return CACHE.computeIfAbsent(key, (String k) -> new ModdedEntityType(type, k)); - } - - @Override - public String key() { - return key; - } - - @Override - public String namespace() { - return namespace; - } - - @Override - public String spawnCategory() { - return type.getCategory().getSerializedName().toLowerCase(Locale.ROOT); - } - - @Override - public Object nativeHandle() { - return type; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java index c43fdb63f..e5555f39b 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java @@ -18,12 +18,15 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDatapackSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldPaths; + import art.arcane.iris.pack.datapack.IrisDatapackCompiler; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.PackDirectoryResolver; import art.arcane.iris.pack.PackValidationResult; import art.arcane.iris.pack.PackValidator; @@ -41,21 +44,6 @@ import art.arcane.iris.generation.terrain.IrisDimensionType; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KSet; -import net.minecraft.network.chat.Component; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.packs.PackLocationInfo; -import net.minecraft.server.packs.PackSelectionConfig; -import net.minecraft.server.packs.PackType; -import net.minecraft.server.packs.PathPackResources; -import net.minecraft.server.packs.repository.Pack; -import net.minecraft.server.packs.repository.PackSource; -import net.minecraft.server.packs.repository.RepositorySource; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelResource; import java.io.File; import java.io.IOException; @@ -77,7 +65,6 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; import java.util.stream.Stream; public final class ModdedForcedDatapack { @@ -94,12 +81,8 @@ public final class ModdedForcedDatapack { private ModdedForcedDatapack() { } - public static RepositorySource repositorySource() { - return (Consumer consumer) -> { - Pack pack = servePack(); - consumer.accept(pack); - LOADED.set(true); - }; + public static NativeDatapackSource repositorySource() { + return new NativeDatapackSource(ModdedForcedDatapack::servePack, () -> LOADED.set(true)); } /** @@ -112,7 +95,7 @@ public final class ModdedForcedDatapack { * after an upgrade, and serving that directory hands Create World a pack without the current biome tags. * A published pack whose hash cannot be computed at all is still served, with one warning. */ - private static Pack servePack() { + private static NativeDatapackSource.PackHandle servePack() { String currentHash = packsHashOrEmpty(); PublishedState current = publishedState(); if (current != null && !currentHash.isEmpty() && current.packsHash().equals(currentHash)) { @@ -173,24 +156,13 @@ public final class ModdedForcedDatapack { return datapackRoot().resolve(PACK_FOLDER); } - private static Pack buildPack() { + private static NativeDatapackSource.PackHandle buildPack() { return requireReadablePack(regenerate()); } - private static Pack requireReadablePack(Path directory) { - PackLocationInfo location = new PackLocationInfo( - PACK_ID, - Component.literal(IrisLanguage.plain(RuntimeUiMessages.FORCED_DATAPACK_NAME)), - PackSource.BUILT_IN, - Optional.empty()); - PackSelectionConfig selection = new PackSelectionConfig(true, Pack.Position.TOP, true); - PathPackResources.PathResourcesSupplier supplier = new PathPackResources.PathResourcesSupplier(directory); - Pack pack = Pack.readMetaAndCreate(location, supplier, PackType.SERVER_DATA, selection); - if (pack == null) { - throw new IllegalStateException("Iris forced datapack at " + directory - + " produced no readable pack metadata"); - } - return pack; + private static NativeDatapackSource.PackHandle requireReadablePack(Path directory) { + return NativeDatapackSource.read(new NativeDatapackSource.Options(PACK_ID, + IrisLanguage.plain(RuntimeUiMessages.FORCED_DATAPACK_NAME), directory)); } public static Path regenerate() { @@ -782,9 +754,9 @@ public final class ModdedForcedDatapack { } private static Path worldRootOrNull() throws IOException { - MinecraftServer server = ModdedEngineBootstrap.currentServer(); - if (server != null) { - return server.getWorldPath(LevelResource.ROOT).toAbsolutePath().normalize(); + Optional root = NativeWorldPaths.root(ModdedEngineBootstrap.currentServer()); + if (root.isPresent()) { + return root.get(); } if (ModdedEngineBootstrap.loader().clientEnvironment()) { return null; @@ -799,12 +771,8 @@ public final class ModdedForcedDatapack { Set selections = new LinkedHashSet<>(); for (ModdedDimensionRegistryStore.PersistentDimension dimension : ModdedDimensionRegistryStore.loadWorldRoot(worldRoot)) { - Identifier identifier = Identifier.tryParse(dimension.id()); - if (identifier == null) { - throw new IOException("Invalid persistent Iris dimension ID '" + dimension.id() + "'."); - } - ResourceKey key = ResourceKey.create(Registries.DIMENSION, identifier); - Path dimensionRoot = DimensionType.getStorageFolder(key, worldRoot).toAbsolutePath().normalize(); + Path dimensionRoot = NativeWorldPaths.dimensionStorage(worldRoot, dimension.id()) + .orElseThrow(() -> new IOException("Invalid persistent Iris dimension ID '" + dimension.id() + "'.")); Path generationRoot = GenerationHistoryPaths.forDimension(dimensionRoot).generationRoot(); if (!Files.exists(generationRoot)) { selections.add(new PackSelection(dimension.pack(), dimension.dimension())); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenPool.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenPool.java index 7900d980e..f2375ed1d 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenPool.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenPool.java @@ -19,8 +19,8 @@ package art.arcane.iris.modded; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkSystemProbe; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkSystemProbe.ChunkSystem; import java.util.concurrent.ExecutorService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; @@ -41,19 +41,6 @@ import java.util.concurrent.atomic.AtomicReference; */ public final class ModdedGenPool { private static final long SHUTDOWN_DRAIN_MILLIS = 2_000L; - private static final String[] C2ME_MARKERS = { - "com.ishland.c2me.base.ModProperties", - "com.ishland.c2me.base.common.config.C2MEConfig", - "com.ishland.c2me.opts.chunkio.ModProperties" - }; - private static final String C2ME_CONFIG = "com.ishland.c2me.base.common.config.C2MEConfig"; - private static final String[] C2ME_SECTIONS = {"asyncScheduling", "asyncSchedulingConfig", "threadedWorldGen"}; - private static final String[] C2ME_FLAGS = {"enabled", "isEnabled", "shouldEnable"}; - private static final String MOONRISE_MARKER = "ca.spottedleaf.moonrise.common.util.MoonriseCommon"; - private static final String[] MOONRISE_WORKER_COUNTS = {"getWorkerThreads", "workerThreads"}; - private static final String[] MOONRISE_POOLS = {"WORKER_POOL", "workerPool"}; - private static final String[] MOONRISE_POOL_COUNTS = {"getCoreThreads", "getThreadCount", "getThreads", "coreThreads", "threadCount"}; - private static final AtomicInteger GEN_THREAD_SEQ = new AtomicInteger(); private static final ChunkSystem CHUNK_SYSTEM = detectChunkSystem(); private static final AtomicReference GEN_POOL = new AtomicReference<>(createGenPool()); @@ -121,13 +108,7 @@ public final class ModdedGenPool { } private static ChunkSystem detectChunkSystem() { - ChunkSystem detected = probeC2ME(); - if (detected == null) { - detected = probeMoonrise(); - } - if (detected == null) { - detected = new ChunkSystem(false, "vanilla"); - } + ChunkSystem detected = new NativeChunkSystemProbe(ModdedGenPool.class.getClassLoader(), ModdedIrisLog::debug).detect(); ModdedIrisLog.info("Iris chunk system: {} (parallel={}, generation on {})", detected.description(), detected.parallel() ? "yes" : "no", @@ -135,209 +116,6 @@ public final class ModdedGenPool { return detected; } - private static ChunkSystem probeC2ME() { - if (!anyPresent(C2ME_MARKERS)) { - return null; - } - Class config = loadOrNull(C2ME_CONFIG); - if (config == null) { - return new ChunkSystem(false, "c2me present, config class missing"); - } - Boolean enabled = readSectionFlag(config, C2ME_SECTIONS, C2ME_FLAGS); - if (enabled == null) { - return new ChunkSystem(false, "c2me present, async scheduling unreadable"); - } - return new ChunkSystem(enabled, enabled ? "c2me async scheduling on" : "c2me async scheduling off"); - } - - private static ChunkSystem probeMoonrise() { - Class marker = loadOrNull(MOONRISE_MARKER); - if (marker == null) { - return null; - } - Integer workers = readMoonriseWorkers(marker); - if (workers == null) { - return new ChunkSystem(false, "moonrise present, worker pool unreadable"); - } - if (workers <= 0) { - return new ChunkSystem(false, "moonrise worker pool empty"); - } - return new ChunkSystem(true, "moonrise workers=" + workers); - } - - /** - * Reads {@code .
.} where the section may itself be the boolean. - * Returns null when nothing along the path is readable. - */ - private static Boolean readSectionFlag(Class configClass, String[] sections, String[] flags) { - for (String section : sections) { - Field field = staticFieldOrNull(configClass, section); - if (field == null) { - continue; - } - Object value; - try { - value = field.get(null); - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not read {}.{}: {}", configClass.getName(), section, e.toString()); - continue; - } - if (value instanceof Boolean flag) { - return flag; - } - if (value == null) { - continue; - } - Boolean nested = readBooleanMember(value, flags); - if (nested != null) { - return nested; - } - } - return null; - } - - private static Boolean readBooleanMember(Object owner, String[] names) { - for (String name : names) { - for (Class type = owner.getClass(); type != null && type != Object.class; type = type.getSuperclass()) { - Field field = declaredFieldOrNull(type, name); - if (field != null && (field.getType() == boolean.class || field.getType() == Boolean.class)) { - try { - if (field.get(owner) instanceof Boolean flag) { - return flag; - } - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not read {}.{}: {}", type.getName(), name, e.toString()); - } - } - Method method = declaredMethodOrNull(type, name); - if (method != null && (method.getReturnType() == boolean.class || method.getReturnType() == Boolean.class)) { - try { - if (method.invoke(owner) instanceof Boolean flag) { - return flag; - } - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not call {}.{}(): {}", type.getName(), name, e.toString()); - } - } - } - } - return null; - } - - private static Integer readMoonriseWorkers(Class marker) { - for (String name : MOONRISE_WORKER_COUNTS) { - Integer direct = readIntMember(marker, null, name); - if (direct != null) { - return direct; - } - Field field = staticFieldOrNull(marker, name); - if (field != null) { - try { - Object value = field.get(null); - if (value instanceof Number number) { - return number.intValue(); - } - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not read {}.{}: {}", marker.getName(), name, e.toString()); - } - } - } - for (String poolName : MOONRISE_POOLS) { - Field field = staticFieldOrNull(marker, poolName); - if (field == null) { - continue; - } - Object pool; - try { - pool = field.get(null); - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not read {}.{}: {}", marker.getName(), poolName, e.toString()); - continue; - } - if (pool == null) { - continue; - } - for (String countName : MOONRISE_POOL_COUNTS) { - Integer count = readIntMember(pool.getClass(), pool, countName); - if (count != null) { - return count; - } - } - } - return null; - } - - private static Integer readIntMember(Class type, Object owner, String name) { - for (Class current = type; current != null && current != Object.class; current = current.getSuperclass()) { - Method method = declaredMethodOrNull(current, name); - if (method == null) { - continue; - } - try { - if (method.invoke(owner) instanceof Number number) { - return number.intValue(); - } - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not call {}.{}(): {}", current.getName(), name, e.toString()); - } - } - return null; - } - - private static Field staticFieldOrNull(Class type, String name) { - for (Class current = type; current != null && current != Object.class; current = current.getSuperclass()) { - Field field = declaredFieldOrNull(current, name); - if (field != null) { - return field; - } - } - return null; - } - - private static Field declaredFieldOrNull(Class type, String name) { - try { - Field field = type.getDeclaredField(name); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - return null; - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not access field {}.{}: {}", type.getName(), name, e.toString()); - return null; - } - } - - private static Method declaredMethodOrNull(Class type, String name) { - try { - Method method = type.getDeclaredMethod(name); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException e) { - return null; - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe could not access method {}.{}(): {}", type.getName(), name, e.toString()); - return null; - } - } - - private static boolean anyPresent(String[] markers) { - for (String marker : markers) { - if (loadOrNull(marker) != null) { - return true; - } - } - return false; - } - - private static Class loadOrNull(String name) { - try { - return Class.forName(name, false, ModdedGenPool.class.getClassLoader()); - } catch (Throwable e) { - ModdedIrisLog.debug("Iris chunk system probe: {} absent ({})", name, e.getClass().getSimpleName()); - return null; - } - } - private static ExecutorService createGenPool() { int threads = Math.max(2, Runtime.getRuntime().availableProcessors()); ThreadPoolExecutor pool = new ThreadPoolExecutor( @@ -352,6 +130,4 @@ public final class ModdedGenPool { return pool; } - private record ChunkSystem(boolean parallel, String description) { - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationHistoryStorage.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationHistoryStorage.java index 9667912bf..175efa222 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationHistoryStorage.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationHistoryStorage.java @@ -1,5 +1,8 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.BrokenPackException; @@ -15,10 +18,6 @@ import art.arcane.iris.world.history.GenerationRegistryContractFactory; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformGenerationRegistry; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; import java.io.File; import java.io.IOException; @@ -35,8 +34,8 @@ final class ModdedGenerationHistoryStorage { } static ActivePack createOrStage( - MinecraftServer server, - ResourceKey levelKey, + NativeModdedServer server, + String levelKey, String selectedPack, String selectedDimensionKey, long worldSeed @@ -60,8 +59,8 @@ final class ModdedGenerationHistoryStorage { } static ActivePack restoreOrAdopt( - MinecraftServer server, - ResourceKey levelKey, + NativeModdedServer server, + String levelKey, String selectedPack, String selectedDimensionKey, long worldSeed @@ -88,14 +87,14 @@ final class ModdedGenerationHistoryStorage { } static ActivePack openOrAdopt( - ServerLevel level, + NativeWorld level, String selectedPack, String selectedDimensionKey, long worldSeed ) { return restoreOrAdopt( - level.getServer(), - level.dimension(), + NativeModdedServer.forWorld(level), + level.name(), selectedPack, selectedDimensionKey, worldSeed @@ -303,7 +302,7 @@ final class ModdedGenerationHistoryStorage { return contracts; } - private static Path dimensionRoot(MinecraftServer server, ResourceKey levelKey) { + private static Path dimensionRoot(NativeModdedServer server, String levelKey) { return normalize(ModdedDimensionStorage.storageFolder(server, levelKey).toPath()); } @@ -311,9 +310,9 @@ final class ModdedGenerationHistoryStorage { return Objects.requireNonNull(path, "path").toAbsolutePath().normalize(); } - private static IllegalStateException historyFailure(ResourceKey levelKey, IOException failure) { + private static IllegalStateException historyFailure(String levelKey, IOException failure) { return new IllegalStateException("Iris generation history is unusable for '" - + levelKey.identifier() + "'.", failure); + + levelKey + "'.", failure); } record ActivePack( diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationRegistry.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationRegistry.java index 9511ead45..050d4b984 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationRegistry.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedGenerationRegistry.java @@ -1,43 +1,20 @@ package art.arcane.iris.modded; import art.arcane.iris.spi.PlatformGenerationRegistry; -import com.google.gson.JsonElement; -import com.google.gson.JsonParser; -import com.mojang.serialization.Codec; -import com.mojang.serialization.JsonOps; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.RegistryOps; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.dimension.DimensionType; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRegistry; import java.util.List; import java.util.Objects; -import java.util.function.Supplier; final class ModdedGenerationRegistry implements PlatformGenerationRegistry { - private static final String BIOME_REGISTRY = "minecraft:worldgen/biome"; - private static final String STRUCTURE_REGISTRY = "minecraft:worldgen/structure"; - private static final String DIMENSION_TYPE_REGISTRY = "minecraft:dimension_type"; - private static final String BLOCK_REGISTRY = "minecraft:block"; - private static final String ENTITY_TYPE_REGISTRY = "minecraft:entity_type"; - - private final Supplier server; + private final NativeGenerationRegistry nativeRegistry; private final String runtimeIdentity; private final String rendererIdentity; - ModdedGenerationRegistry( - Supplier server, - String runtimeIdentity, - String rendererIdentity - ) { - this.server = Objects.requireNonNull(server, "server"); - this.runtimeIdentity = requireText(runtimeIdentity, "runtimeIdentity"); - this.rendererIdentity = requireText(rendererIdentity, "rendererIdentity"); + ModdedGenerationRegistry(NativeGenerationRegistry nativeRegistry, Identity identity) { + this.nativeRegistry = Objects.requireNonNull(nativeRegistry, "nativeRegistry"); + this.runtimeIdentity = requireText(identity.runtime(), "runtimeIdentity"); + this.rendererIdentity = requireText(identity.renderer(), "rendererIdentity"); } @Override @@ -78,107 +55,17 @@ final class ModdedGenerationRegistry implements PlatformGenerationRegistry { @Override public Definition definition(String registryKey, String resourceKey) { - MinecraftServer instance = Objects.requireNonNull(server.get(), "Minecraft server"); - RegistryAccess access = instance.registryAccess(); - return switch (registryKey) { - case BIOME_REGISTRY -> presence(access, Registries.BIOME, registryKey, resourceKey); - case STRUCTURE_REGISTRY -> presence(access, Registries.STRUCTURE, registryKey, resourceKey); - case DIMENSION_TYPE_REGISTRY -> presence( - access, - Registries.DIMENSION_TYPE, - registryKey, - resourceKey - ); - case BLOCK_REGISTRY -> presence(access, Registries.BLOCK, registryKey, resourceKey); - case ENTITY_TYPE_REGISTRY -> presence(access, Registries.ENTITY_TYPE, registryKey, resourceKey); - default -> throw new IllegalArgumentException("Unsupported generation registry: " + registryKey); - }; + return nativeRegistry.definition(registryKey, resourceKey); } @Override public Definition canonicalDefinition(String registryKey, String resourceKey, String sourceJson) { - MinecraftServer instance = Objects.requireNonNull(server.get(), "Minecraft server"); - RegistryAccess access = instance.registryAccess(); - return switch (registryKey) { - case BIOME_REGISTRY -> canonicalize(access, Biome.DIRECT_CODEC, sourceJson); - case DIMENSION_TYPE_REGISTRY -> canonicalize(access, DimensionType.DIRECT_CODEC, sourceJson); - default -> throw new IllegalArgumentException( - "Registry does not accept generated JSON definitions: " + registryKey - ); - }; + return nativeRegistry.canonicalDefinition(registryKey, resourceKey, sourceJson); } @Override public Definition generatedDefinition(String registryKey, String resourceKey) { - MinecraftServer instance = Objects.requireNonNull(server.get(), "Minecraft server"); - RegistryAccess access = instance.registryAccess(); - return switch (registryKey) { - case BIOME_REGISTRY -> exactRegistered( - access, - Registries.BIOME, - Biome.DIRECT_CODEC, - resourceKey - ); - case DIMENSION_TYPE_REGISTRY -> exactRegistered( - access, - Registries.DIMENSION_TYPE, - DimensionType.DIRECT_CODEC, - resourceKey - ); - default -> throw new IllegalArgumentException( - "Registry does not contain Iris-generated definitions: " + registryKey - ); - }; - } - - private static Definition presence( - RegistryAccess access, - ResourceKey> registryKey, - String registryResourceKey, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing registry definition " + registryKey.identifier() - + " / " + resourceKey + "."); - } - return Definition.resourceIdentity(registryResourceKey, resourceKey); - } - - private static Definition canonicalize(RegistryAccess access, Codec codec, String sourceJson) { - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement source = JsonParser.parseString(Objects.requireNonNull(sourceJson, "sourceJson")); - T decoded = codec.parse(ops, source).getOrThrow(IllegalArgumentException::new); - JsonElement encoded = codec.encodeStart(ops, decoded).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Definition exactRegistered( - RegistryAccess access, - ResourceKey> registryKey, - Codec codec, - String resourceKey - ) { - Registry registry = access.lookupOrThrow(registryKey); - Identifier identifier = requireIdentifier(resourceKey); - T value = registry.getValue(identifier); - if (value == null) { - throw new IllegalStateException("Missing generated registry definition " - + registryKey.identifier() + " / " + resourceKey + "."); - } - RegistryOps ops = RegistryOps.create(JsonOps.INSTANCE, access); - JsonElement encoded = codec.encodeStart(ops, value).getOrThrow(IllegalStateException::new); - return Definition.exactJson(encoded.toString()); - } - - private static Identifier requireIdentifier(String resourceKey) { - Identifier identifier = Identifier.tryParse(resourceKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registry resource key: " + resourceKey); - } - return identifier; + return nativeRegistry.generatedDefinition(registryKey, resourceKey); } private static String requireText(String value, String label) { @@ -188,4 +75,7 @@ final class ModdedGenerationRegistry implements PlatformGenerationRegistry { } return required; } + + record Identity(String runtime, String renderer) { + } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedHeightmaps.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedHeightmaps.java deleted file mode 100644 index a9e4d1fbd..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedHeightmaps.java +++ /dev/null @@ -1,22 +0,0 @@ -package art.arcane.iris.modded; - -import net.minecraft.util.Mth; -import net.minecraft.util.SimpleBitStorage; - -import java.util.function.IntBinaryOperator; - -final class ModdedHeightmaps { - private ModdedHeightmaps() { - } - - static long[] terrainRawData(int height, IntBinaryOperator heightResolver) { - SimpleBitStorage storage = new SimpleBitStorage(Mth.ceillog2(height + 1), 256); - for (int z = 0; z < 16; z++) { - for (int x = 0; x < 16; x++) { - int value = Mth.clamp(heightResolver.applyAsInt(x, z), 0, height); - storage.set(x + z * 16, value); - } - } - return storage.getRaw(); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeaturePolicy.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeaturePolicy.java new file mode 100644 index 000000000..f9d3f64e3 --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeaturePolicy.java @@ -0,0 +1,21 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.generation.biome.IrisBiomeCustom; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.structure.nativegen.ImportedFeaturePolicy; + +final class ModdedImportedFeaturePolicy extends ImportedFeaturePolicy { + ModdedImportedFeaturePolicy(Engine engine) { + super(engine); + } + + @Override + protected boolean includeVanillaDerivative() { + return false; + } + + @Override + protected String customBiomeKey(IrisBiomeCustom biome) { + return ModdedWorldgenIds.biomeRef(engine, biome.getId()); + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeatureStage.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeatureStage.java index aee3a386a..0763209cc 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeatureStage.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedImportedFeatureStage.java @@ -18,51 +18,20 @@ package art.arcane.iris.modded; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionStackLayout; -import art.arcane.iris.nativegen.NativeGenerationWriteGuard; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeFeatureBiomeSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedGeneratorPolicy; + +import art.arcane.volmlib.nativelib.terrain.feature.NativeFeatureTable; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedImportedFeatures; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.structure.nativegen.NativeFeatureGenerationPolicy; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.decoration.IrisDecorationStep; import art.arcane.iris.structure.nativegen.IrisImportedFeatureControl; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; import art.arcane.iris.generation.context.IrisContext; -import it.unimi.dsi.fastutil.ints.IntArraySet; -import it.unimi.dsi.fastutil.ints.IntSet; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.FeatureSorter; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.placement.PlacedFeature; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.IntBinaryOperator; /** * Native placed-feature passthrough for one Iris dimension, gated on {@code importedFeatures.enabled}. @@ -72,24 +41,35 @@ import java.util.function.IntBinaryOperator; * would place every structure a second time. The feature half is reproduced here off the same decoration and * feature seeds vanilla derives, so an imported feature lands where vanilla would have put it. * - *

Threading: {@link #run} runs on the worldgen thread that is generating the chunk, never on + *

Threading: {@link NativeModdedImportedFeatures#run} runs on the worldgen thread that is generating the chunk, never on * {@link ModdedGenPool}. The FEATURES chunk step is not parallel-safe - it writes into the eight neighbouring * chunks through {@code WorldGenLevel}, and vanilla and every threaded chunk system serialize it. Terrain is * the only Iris step that may fan out. * *

Everything here is inert while the control is disabled: no table is built, no registry is walked, and - * {@link #generationSettings} answers exactly what vanilla's default getter answers. + * {@link NativeModdedImportedFeatures#generationSettings} answers exactly what vanilla's default getter answers. */ -final class ModdedImportedFeatureStage { - private static final String CYCLE_MARKER = "Feature order cycle found"; - private final IrisModdedBiomeSource biomeSource; +final class ModdedImportedFeatureStage implements NativeModdedGeneratorPolicy.FeatureStage { + private final NativeFeatureBiomeSource biomeSource; + private final NativeModdedImportedFeatures nativeFeatures; private final ReentrantLock buildLock = new ReentrantLock(); - private final ConcurrentHashMap featureTables = new ConcurrentHashMap<>(); + private final ConcurrentHashMap featureTables = new ConcurrentHashMap<>(); private final ConcurrentHashMap inertGenerations = new ConcurrentHashMap<>(); private volatile IrisModdedChunkGenerator generator; - ModdedImportedFeatureStage(IrisModdedBiomeSource biomeSource) { + ModdedImportedFeatureStage(NativeFeatureBiomeSource biomeSource) { this.biomeSource = biomeSource; + nativeFeatures = new NativeModdedImportedFeatures(biomeSource, new NativeModdedImportedFeatures.PlacementObserver() { + @Override + public void completed() { + WorldCheckFeaturePlacement.recordPlacementPass(); + } + + @Override + public void failed(int chunkX, int chunkZ, Throwable error) { + WorldCheckFeaturePlacement.recordPlacementFailure(chunkX, chunkZ, error); + } + }); } void bind(IrisModdedChunkGenerator generator) { @@ -117,13 +97,13 @@ final class ModdedImportedFeatureStage { * *

With {@code importedFeatures} disabled there is no table and this is vanilla's default getter. */ - BiomeGenerationSettings generationSettings(Holder biome) { + public NativeFeatureTable currentTable() { Engine engine = generator == null ? null : generator.engineOrNull(); - FeatureTable table = engine == null ? null : featureTables.get(engine.getCacheID()); - if (table == null) { - return biome.value().getGenerationSettings(); - } - return settingsFor(biome, table.derivatives()); + return engine == null ? null : featureTables.get(engine.getCacheID()); + } + + public NativeModdedImportedFeatures nativeFeatures() { + return nativeFeatures; } /** @@ -132,7 +112,7 @@ final class ModdedImportedFeatureStage { * thread, and two threads that both found the stage unprepared would each run {@code FeatureSorter}, whose * cycle detection is the expensive part. Waiting here is safe: this caller holds no generator monitor. */ - void prepare(Engine engine) { + public void prepare(Engine engine) { prepare(engine, true); } @@ -171,7 +151,7 @@ final class ModdedImportedFeatureStage { } private boolean settled(int runtimeIdentity, long generation) { - FeatureTable current = featureTables.get(runtimeIdentity); + NativeFeatureTable current = featureTables.get(runtimeIdentity); if (current != null && current.generation() == generation) { return true; } @@ -184,7 +164,7 @@ final class ModdedImportedFeatureStage { control = NativeFeatureGenerationPolicy.control(engine); } catch (RuntimeException error) { ModdedIrisLog.error("Iris could not read importedFeatures for this dimension; features off: {}", - error.toString()); + error.toString(), error); markInert(runtimeIdentity, generation); return; } @@ -192,13 +172,13 @@ final class ModdedImportedFeatureStage { markInert(runtimeIdentity, generation); return; } - FeatureTable built; + NativeFeatureTable built; try (GenerationSessionLease lease = engine.acquireGenerationLease("modded_imported_features"); IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { - built = buildTable(engine, control, generation); + built = nativeFeatures.buildTable(new ModdedImportedFeaturePolicy(engine), control, generation); } catch (Throwable error) { ModdedIrisLog.error("Iris importedFeatures is off for {}: feature table construction failed: {}", - dimensionKey(engine), error.toString()); + dimensionKey(engine), error.toString(), error); markInert(runtimeIdentity, generation); return; } @@ -212,8 +192,8 @@ final class ModdedImportedFeatureStage { // missed every far-chunk write the first chunk made. No-op unless -Diris.worldcheck is set. WorldCheckFeaturePlacement.arm(); ModdedIrisLog.info("Iris importedFeatures on for {}: {} biomes, {} steps, {} custom-biome derivative maps", - dimensionKey(engine), built.biomes().size(), built.steps().size(), - built.derivatives().size()); + dimensionKey(engine), built.biomeCount(), built.stepCount(), + built.derivativeCount()); } private void markInert(int runtimeIdentity, long generation) { @@ -221,184 +201,19 @@ final class ModdedImportedFeatureStage { inertGenerations.put(runtimeIdentity, generation); } - private FeatureTable buildTable(Engine engine, IrisImportedFeatureControl control, long generation) { - // Registry-ordered biome list. FeatureSorter's cycle detection walks it, so an unordered list makes - // detection depend on JVM hash order and turns a real cycle into an intermittent one. - Set hostBiomeKeys = hostBiomeKeys(engine); - List> biomes = new ArrayList<>(); - for (Holder biome : biomeSource.orderedPossibleBiomes()) { - String key = holderKey(biome); - if (key != null && hostBiomeKeys.contains(key)) { - biomes.add(biome); - } - } - if (biomes.isEmpty()) { - ModdedIrisLog.error("Iris importedFeatures is on but {} exposes no biomes; features off", - dimensionKey(engine)); - return null; - } - Map> byKey = new HashMap<>(biomes.size()); - for (Holder biome : biomes) { - String key = holderKey(biome); - if (key != null) { - byKey.put(key, biome); - } - } - Map> derivatives = customBiomeDerivatives(engine, byKey); - List steps; - try { - // Same inputs as vanilla's own memo, built here so it can be keyed on the Iris pack generation and - // so the cycle failure lands at bind time. - steps = FeatureSorter.buildFeaturesPerStep(biomes, - (Holder biome) -> settingsFor(biome, derivatives).features(), true); - } catch (IllegalStateException error) { - String message = error.getMessage(); - if (message == null || !message.contains(CYCLE_MARKER)) { - throw error; - } - ModdedIrisLog.error("Iris importedFeatures is off for {}: the registered placed features cannot be ordered." - + " {}. Remove or reorder the conflicting content, or leave" - + " importedFeatures.enabled false.", - dimensionKey(engine), message); - return null; - } - boolean filtered = control.getDisabled() != null && !control.getDisabled().isEmpty(); - return new FeatureTable(generation, control, List.copyOf(biomes), Set.copyOf(biomes), - Map.copyOf(byKey), steps, Map.copyOf(derivatives), filtered); - } - - /** - * Maps every generated Iris custom biome key onto the registry holder of its Iris biome's vanilla - * derivative. The custom biome's own datapack JSON carries no features by design; this is the only place - * the vanilla feature set enters. - */ - private Map> customBiomeDerivatives(Engine engine, Map> byKey) { - Map> derivatives = new HashMap<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - if (!irisBiome.isCustom()) { - continue; - } - String derivativeKey = normalizeKey(irisBiome.getVanillaDerivativeKey()); - // Resolve from the registry, not only from this source's own biome set: a sea or shore biome's - // structure derivative is rewritten away from its vanilla derivative, so the derivative whose - // features we want is not always a biome this source can emit. - Holder derivative = byKey.get(derivativeKey); - if (derivative == null) { - derivative = biomeSource.registeredBiome(derivativeKey); - } - if (derivative == null) { - ModdedIrisLog.warn("Iris importedFeatures: vanilla derivative {} of biome {} is not registered;" - + " its custom biomes generate no imported features", - derivativeKey, irisBiome.getLoadKey()); - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - derivatives.put(ModdedWorldgenIds.biomeRef(engine, customBiome.getId()), derivative); - } - } - return derivatives; - } - - private static BiomeGenerationSettings settingsFor(Holder biome, - Map> derivatives) { - Holder mapped = derivatives.get(holderKey(biome)); - return mapped == null - ? biome.value().getGenerationSettings() - : mapped.value().getGenerationSettings(); - } - - /** - * Runs the vanilla placed-feature pass for one chunk, on the calling worldgen thread. A no-op while the - * control is disabled or the table degraded. - */ - void run(WorldGenLevel level, ChunkAccess chunk, Engine engine) { + public NativeFeatureTable placementTable(Engine engine) { int runtimeIdentity = engine.getCacheID(); - FeatureTable table = featureTables.get(runtimeIdentity); + NativeFeatureTable table = featureTables.get(runtimeIdentity); if (table == null) { WorldCheckFeaturePlacement.recordFeaturesOff(); - return; + return null; } if (table.generation() != biomeSource.packGeneration()) { - // A repoint landed between the prepare above and this chunk. Refuse stale content outright; the - // next chunk's prepare rebuilds against the new pack. featureTables.remove(runtimeIdentity, table); inertGenerations.remove(runtimeIdentity); - return; - } - IrisModdedChunkGenerator owner = generator; - if (owner == null) { - return; - } - ChunkPos centerPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(centerPos, level.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry featureRegistry = level.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); - List steps = table.steps(); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decorationSeed = random.setDecorationSeed(level.getSeed(), origin.getX(), origin.getZ()); - IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); - int staticMinY = engine.getMinHeight(); - DimensionStackContext stackContext = engine.getDimensionStackContext(); - Set> chunkBiomes = chunkBiomes(level, sectionPos, table, stackContext); - IntBinaryOperator surfaceFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, false) + staticMinY + 1; - IntBinaryOperator floorFirstFreeY = stackContext == null - ? (x, z) -> level.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, x, z) - : (x, z) -> Engine.hostHeight(engine, x, z, true) + staticMinY + 1; - WorldGenLevel placementLevel = staticObjects.isEmpty() && stackContext == null - ? level - : ModdedNativeStructureWorldgenAccess.create( - level, centerPos, - surfaceFirstFreeY, - floorFirstFreeY, - stackContext != null, - NativeGenerationWriteGuard.protectedPositions(staticObjects, stackContext, staticMinY)); - - try { - for (int stepIndex = 0; stepIndex < steps.size(); stepIndex++) { - if (!table.control().shouldGenerateStep(IrisDecorationStep.byOrdinal(stepIndex))) { - continue; - } - placeStep(placementLevel, table, steps.get(stepIndex), featureRegistry, chunkBiomes, owner, - random, decorationSeed, origin, stepIndex); - } - } catch (Throwable error) { - WorldCheckFeaturePlacement.recordPlacementFailure(centerPos, error); - throw new IllegalStateException("Iris imported feature placement failed for chunk " - + centerPos.x() + "," + centerPos.z(), error); - } finally { - level.setCurrentlyGenerating(null); - } - WorldCheckFeaturePlacement.recordPlacementPass(); - } - - private static Set hostBiomeKeys(Engine engine) { - Set keys = new LinkedHashSet<>(); - for (IrisBiome irisBiome : engine.getDimension().getReachableBiomes(engine)) { - addKey(keys, irisBiome.getStructureDerivativeKey()); - addKey(keys, irisBiome.getDerivativeKey()); - for (String scatter : irisBiome.getBiomeScatter()) { - addKey(keys, scatter); - } - for (String scatter : irisBiome.getBiomeSkyScatter()) { - addKey(keys, scatter); - } - if (!irisBiome.isCustom()) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - addKey(keys, ModdedWorldgenIds.biomeRef(engine, customBiome.getId())); - } - } - return keys; - } - - private static void addKey(Set keys, String key) { - String normalized = normalizeKey(key); - if (normalized != null) { - keys.add(normalized); + return null; } + return generator == null ? null : table; } void evictRuntime(int runtimeIdentity) { @@ -406,118 +221,7 @@ final class ModdedImportedFeatureStage { inertGenerations.remove(runtimeIdentity); } - private void placeStep(WorldGenLevel level, FeatureTable table, FeatureSorter.StepFeatureData stepData, - Registry featureRegistry, Set> chunkBiomes, - IrisModdedChunkGenerator owner, WorldgenRandom random, long decorationSeed, - BlockPos origin, int stepIndex) { - IntSet stepFeatures = new IntArraySet(); - for (Holder biome : chunkBiomes) { - List> biomeFeatures = settingsFor(biome, table.derivatives()).features(); - if (stepIndex >= biomeFeatures.size()) { - continue; - } - for (Holder feature : biomeFeatures.get(stepIndex)) { - stepFeatures.add(stepData.indexMapping().applyAsInt(feature.value())); - } - } - if (stepFeatures.isEmpty()) { - return; - } - // Sorted global indices: identical ordering to vanilla, and each feature's seed comes from its own - // global index, so denying one feature never shifts another. - int[] featureIndices = stepFeatures.toIntArray(); - Arrays.sort(featureIndices); - for (int globalIndex : featureIndices) { - PlacedFeature feature = stepData.features().get(globalIndex); - if (table.filtered()) { - Identifier featureId = featureRegistry.getKey(feature); - if (featureId != null && !table.control().shouldGenerate(featureId.toString())) { - continue; - } - } - random.setFeatureSeed(decorationSeed, globalIndex, stepIndex); - level.setCurrentlyGenerating(() -> describeFeature(featureRegistry, feature)); - feature.placeWithBiomeCheck(level, owner, random, origin); - } - } - - private static String describeFeature(Registry registry, PlacedFeature feature) { - Identifier id = registry.getKey(feature); - return id == null ? feature.toString() : id.toString(); - } - - /** - * Biomes actually present in the 3x3 chunk neighbourhood, intersected with the dimension's biome set. Same - * shape as vanilla, which drops any section biome its biome source does not claim. Holders are canonicalised - * back onto the table's own holders so the index mapping cannot be handed a holder it never saw. - */ - private Set> chunkBiomes( - WorldGenLevel level, - SectionPos sectionPos, - FeatureTable table, - DimensionStackContext stackContext - ) { - List> collected = new ArrayList<>(); - ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach((ChunkPos chunkPos) -> { - ChunkAccess neighbour = level.getChunk(chunkPos.x(), chunkPos.z()); - LevelChunkSection[] sections = neighbour.getSections(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - if (stackContext == null) { - section.getBiomes().getAll(collected::add); - continue; - } - int sectionMinimumY = neighbour.getSectionYFromSectionIndex(sectionIndex) << 4; - for (int quartY = 0; quartY < 4; quartY++) { - int internalY = sectionMinimumY + (quartY << 2) - level.getMinY(); - for (int quartX = 0; quartX < 4; quartX++) { - int blockX = chunkPos.getMinBlockX() + (quartX << 2); - for (int quartZ = 0; quartZ < 4; quartZ++) { - int blockZ = chunkPos.getMinBlockZ() + (quartZ << 2); - DimensionStackLayout.Layer layer = stackContext.getLayerAt( - blockX, internalY, blockZ); - if (layer.terrainContext().isSelfReferencing()) { - collected.add(section.getBiomes().get(quartX, quartY, quartZ)); - } - } - } - } - } - }); - Set> present = new LinkedHashSet<>(); - for (Holder biome : collected) { - if (table.biomeSet().contains(biome)) { - present.add(biome); - continue; - } - String key = holderKey(biome); - Holder canonical = key == null ? null : table.byKey().get(key); - if (canonical != null) { - present.add(canonical); - } - } - return present; - } - private static String dimensionKey(Engine engine) { return engine.getDimension() == null ? "" : engine.getDimension().getLoadKey(); } - - private static String holderKey(Holder holder) { - return holder.unwrapKey() - .map(key -> key.identifier().toString().toLowerCase(Locale.ROOT)) - .orElse(null); - } - - private static String normalizeKey(String key) { - Identifier identifier = key == null ? null : Identifier.tryParse(key); - return identifier == null ? null : identifier.toString().toLowerCase(Locale.ROOT); - } - - private record FeatureTable(long generation, IrisImportedFeatureControl control, - List> biomes, Set> biomeSet, - Map> byKey, - List steps, - Map> derivatives, boolean filtered) { - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisPayload.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisPayload.java deleted file mode 100644 index 85c43af8d..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisPayload.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.protocol.IrisProtocol; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.Identifier; - -public record ModdedIrisPayload(byte[] data) implements CustomPacketPayload { - public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>(Identifier.parse(IrisProtocol.CHANNEL)); - public static final StreamCodec STREAM_CODEC = CustomPacketPayload.codec(ModdedIrisPayload::write, ModdedIrisPayload::read); - - @Override - public CustomPacketPayload.Type type() { - return TYPE; - } - - private static ModdedIrisPayload read(RegistryFriendlyByteBuf buffer) { - return new ModdedIrisPayload(buffer.readByteArray(IrisProtocol.MAX_FRAME_BYTES)); - } - - private void write(RegistryFriendlyByteBuf buffer) { - buffer.writeByteArray(data); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisSplash.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisSplash.java index 62d46eeba..0179fc25e 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisSplash.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedIrisSplash.java @@ -18,6 +18,8 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; + import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.diagnostics.splash.IrisSplashComposer; @@ -31,20 +33,20 @@ public final class ModdedIrisSplash { private ModdedIrisSplash() { } - public static void print(ModdedLoader loader) { + public static void print(NativeModdedLoader loader) { printPacks(loader); if (isLogoEnabled()) { printLogo(loader); } } - private static void printPacks(ModdedLoader loader) { + private static void printPacks(NativeModdedLoader loader) { File packFolder = IrisPlatforms.get().packsFolderNoCreate(); IrisSplashComposer.composePackLines(packFolder, IrisLogging::reportError) .thenAccept(lines -> lines.forEach(IrisLogging::info)); } - private static void printLogo(ModdedLoader loader) { + private static void printLogo(NativeModdedLoader loader) { String serverLine = loader.platformName() + " / Minecraft " + loader.minecraftVersion(); String[] splash = IrisSplashRenderer.renderPlain(); String[] info = IrisSplashComposer.composeInfo(loader.modVersion(), serverLine, IrisSplashComposer.InfoStyle.PLAIN); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItem.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItem.java deleted file mode 100644 index b2f5d7414..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItem.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.PlatformItem; -import net.minecraft.world.item.Item; - -import java.util.concurrent.ConcurrentHashMap; - -public final class ModdedItem implements PlatformItem { - private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); - - private final Item item; - private final String key; - private final String namespace; - - private ModdedItem(Item item, String key) { - this.item = item; - this.key = key; - int colon = key.indexOf(':'); - this.namespace = colon >= 0 ? key.substring(0, colon) : "minecraft"; - } - - public static ModdedItem of(Item item, String key) { - return CACHE.computeIfAbsent(key, (String k) -> new ModdedItem(item, k)); - } - - @Override - public String key() { - return key; - } - - @Override - public String namespace() { - return namespace; - } - - @Override - public Object nativeHandle() { - return item; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItemTranslator.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItemTranslator.java index ee0ffd264..7b8b3e583 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItemTranslator.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedItemTranslator.java @@ -18,63 +18,31 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import art.arcane.iris.structure.placement.LootResolver; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemContext; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemTranslator; import art.arcane.iris.world.loot.InventorySlotType; -import art.arcane.iris.world.entity.IrisAttributeModifier; -import art.arcane.iris.world.entity.IrisEnchantment; import art.arcane.iris.world.loot.IrisLoot; import art.arcane.iris.world.loot.IrisLootTable; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; -import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.format.Form; -import art.arcane.volmlib.util.json.JSONObject; import art.arcane.volmlib.util.math.RNG; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.component.DataComponentType; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.TagParser; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.Unit; -import net.minecraft.world.entity.EquipmentSlotGroup; -import net.minecraft.world.entity.ai.attributes.Attribute; -import net.minecraft.world.entity.ai.attributes.AttributeModifier; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.CustomData; -import net.minecraft.world.item.component.CustomModelData; -import net.minecraft.world.item.component.DyedItemColor; -import net.minecraft.world.item.component.ItemAttributeModifiers; -import net.minecraft.world.item.component.ItemLore; -import net.minecraft.world.item.component.TooltipDisplay; -import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.item.enchantment.ItemEnchantments; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; public final class ModdedItemTranslator { private static final Set WARNED = ConcurrentHashMap.newKeySet(); - private static final String COLOR_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoRrXx"; + private static final NativeItemContext.Options OPTIONS = new NativeItemContext.Options("iris", ModdedItemTranslator::warnOnce); private ModdedItemTranslator() { } - public static KList loot(IrisLootTable table, long lootSeed, InventorySlotType slot, ServerLevel level, int x, int y, int z) { - KList out = new KList<>(); + public static KList loot(IrisLootTable table, long lootSeed, InventorySlotType slot, NativeItemContext context, int x, int y, int z) { + KList out = new KList<>(); KList entries = table.getLoot(); if (entries.isEmpty() || table.getMaxTries() <= 0) { return out; @@ -91,7 +59,7 @@ public final class ModdedItemTranslator { if (entry == null || entry.getSlotTypes() != slot) { continue; } - ItemStack item = item(entry, table, rng, lootSeed, entryIndex, level, x, y, z); + NativeItemStack item = item(entry, table, rng, lootSeed, entryIndex, context, x, y, z); if (item != null && !item.isEmpty()) { out.add(item); m++; @@ -101,307 +69,36 @@ public final class ModdedItemTranslator { return out; } - public static ItemStack stack(IrisLoot loot, RNG rng, ServerLevel level) { + public static NativeItemStack stack(IrisLoot loot, RNG rng, NativeItemContext context) { try { - ItemStack stack = baseStack(loot, rng); - if (stack == null || stack.isEmpty()) { - return null; - } - applyComponents(loot, stack, rng, level); - applyCustomNbt(stack, loot.getCustomNbt()); - return stack; + return NativeItemTranslator.stack(new IrisItemRecipe(loot, rng), context); } catch (Throwable e) { IrisLogging.reportError(e); return null; } } - public static ItemStack item(IrisLoot loot, IrisLootTable table, RNG rng, long lootSeed, int entryIndex, ServerLevel level, int x, int y, int z) { + public static NativeItemStack item(IrisLoot loot, IrisLootTable table, RNG rng, long lootSeed, int entryIndex, NativeItemContext context, int x, int y, int z) { long combinedRarity = LootResolver.combinedRarity(loot.getRarity(), table.getRarity()); if (!LootResolver.spatialOneIn(lootSeed, table, entryIndex, x, y, z, combinedRarity)) { return null; } try { - ItemStack stack = baseStack(loot, rng); - if (stack == null || stack.isEmpty()) { - return null; - } - applyComponents(loot, stack, rng, level); - applyCustomNbt(stack, loot.getCustomNbt()); - return stack; + return NativeItemTranslator.stack(new IrisItemRecipe(loot, rng), context); } catch (Throwable e) { IrisLogging.reportError(e); return null; } } - private static ItemStack baseStack(IrisLoot loot, RNG rng) { - String raw = loot.getTypeKey(); - if (raw == null || raw.isBlank()) { - return null; - } - - String key = raw.toLowerCase(Locale.ROOT); - Identifier id = Identifier.tryParse(key.contains(":") ? key : "minecraft:" + key); - if (id == null || !BuiltInRegistries.ITEM.containsKey(id)) { - warnOnce("item:" + key, "Iris loot: unknown item '" + raw + "'"); - return null; - } - - Item item = BuiltInRegistries.ITEM.getValue(id); - return new ItemStack(item, Math.max(1, LootResolver.inclusive(rng, loot.getMinAmount(), loot.getMaxAmount()))); - } - - private static void applyComponents(IrisLoot loot, ItemStack stack, RNG rng, ServerLevel level) { - applyEnchantments(loot, stack, rng, level); - applyAttributes(loot, stack, rng, level); - - if (loot.isUnbreakable()) { - stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE); - } - - if (loot.getItemFlags().isNotEmpty()) { - applyItemFlags(loot, stack); - } - - if (loot.getCustomModel() != null) { - stack.set(DataComponents.CUSTOM_MODEL_DATA, new CustomModelData(List.of(loot.getCustomModel().floatValue()), List.of(), List.of(), List.of())); - } - - if (stack.getMaxDamage() > 0) { - int max = stack.getMaxDamage(); - int damage = (int) Math.round(Math.max(0, Math.min(max, (1D - rng.d(loot.getMinDurability(), loot.getMaxDurability())) * max))); - stack.set(DataComponents.DAMAGE, damage); - } - - if (loot.getLeatherColor() != null) { - try { - int rgb = Integer.decode(loot.getLeatherColor()) & 0xFFFFFF; - stack.set(DataComponents.DYED_COLOR, new DyedItemColor(rgb)); - } catch (NumberFormatException e) { - warnOnce("leatherColor:" + loot.getLeatherColor(), "Iris loot: invalid leatherColor '" + loot.getLeatherColor() + "'"); - } - } - - String dye = loot.getDyeColorKey(); - if (dye != null) { - applyDyeColor(stack, dye); - } - - if (loot.getDisplayName() != null) { - stack.set(DataComponents.CUSTOM_NAME, Component.literal(colorize(loot.getDisplayName()))); - } - - applyLore(loot, stack); - } - - private static void applyEnchantments(IrisLoot loot, ItemStack stack, RNG rng, ServerLevel level) { - if (loot.getEnchantments().isEmpty()) { - return; - } - - Registry registry = level.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); - DataComponentType component = stack.is(Items.ENCHANTED_BOOK) ? DataComponents.STORED_ENCHANTMENTS : DataComponents.ENCHANTMENTS; - ItemEnchantments.Mutable mutable = new ItemEnchantments.Mutable(stack.getOrDefault(component, ItemEnchantments.EMPTY)); - boolean changed = false; - - for (IrisEnchantment enchantment : loot.getEnchantments()) { - String name = enchantment.getEnchantment(); - if (name == null || name.isBlank()) { - continue; - } - // Same normalization as IrisEnchantment.resolve() on Bukkit: trim, lowercase, spaces to underscores. - // Without it 'Fire Aspect' resolves on Bukkit and warns as unknown here, for the same pack. - String key = name.trim().toLowerCase(Locale.ROOT).replace(' ', '_'); - Identifier id = Identifier.tryParse(key.contains(":") ? key : "minecraft:" + key); - Optional> holder = id == null ? Optional.empty() : registry.get(id); - if (holder.isEmpty()) { - warnOnce("enchantment:" + key, "Iris loot: unknown enchantment '" + name + "'"); - continue; - } - if (rng.nextDouble() < enchantment.getChance()) { - mutable.set(holder.get(), enchantment.getLevel(rng)); - changed = true; - } - } - - if (changed) { - stack.set(component, mutable.toImmutable()); - } - } - - private static void applyAttributes(IrisLoot loot, ItemStack stack, RNG rng, ServerLevel level) { - if (loot.getAttributes().isEmpty()) { - return; - } - - Registry registry = level.registryAccess().lookupOrThrow(Registries.ATTRIBUTE); - ItemAttributeModifiers.Builder builder = ItemAttributeModifiers.builder(); - boolean changed = false; - - for (IrisAttributeModifier attribute : loot.getAttributes()) { - if (rng.nextDouble() >= attribute.getChance()) { - continue; - } - double amount = attribute.getAmount(rng); - Holder holder = resolveAttribute(registry, attribute.getAttribute()); - if (holder == null) { - warnOnce("attribute:" + attribute.getAttribute(), "Iris loot: unknown attribute '" + attribute.getAttribute() + "'"); - continue; - } - Identifier modifierId = attributeModifierId(attribute.getName()); - if (modifierId == null) { - continue; - } - AttributeModifier modifier = new AttributeModifier(modifierId, amount, operationFor(attribute.getOperation())); - builder.add(holder, modifier, EquipmentSlotGroup.ANY); - changed = true; - } - - if (changed) { - stack.set(DataComponents.ATTRIBUTE_MODIFIERS, builder.build()); - } - } - - public static Holder resolveAttribute(Registry registry, String key) { - if (key == null || key.isBlank()) { - return null; - } - String value = key.trim().toLowerCase(Locale.ROOT); - Holder direct = lookupAttribute(registry, Identifier.tryParse(value.indexOf(':') >= 0 ? value : "minecraft:" + value)); - if (direct != null) { - return direct; - } - if (value.startsWith("generic_")) { - return lookupAttribute(registry, Identifier.tryParse("minecraft:" + value.substring("generic_".length()))); - } - if (value.startsWith("generic.")) { - return lookupAttribute(registry, Identifier.tryParse("minecraft:" + value.substring("generic.".length()))); - } - return null; - } - - private static Holder lookupAttribute(Registry registry, Identifier id) { - if (id == null) { - return null; - } - Optional> holder = registry.get(id); - return holder.isPresent() ? holder.get() : null; - } - - public static AttributeModifier.Operation operationFor(String operation) { - if (operation == null || operation.isBlank()) { - return AttributeModifier.Operation.ADD_VALUE; - } - return switch (operation.trim().toUpperCase(Locale.ROOT)) { - case "ADD_SCALAR", "ADD_MULTIPLIED_BASE" -> AttributeModifier.Operation.ADD_MULTIPLIED_BASE; - case "MULTIPLY_SCALAR_1", "ADD_MULTIPLIED_TOTAL" -> AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL; - default -> AttributeModifier.Operation.ADD_VALUE; - }; - } - - private static Identifier attributeModifierId(String name) { - String source = name == null || name.isBlank() ? "modifier" : name; - String normalized = source.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9/._-]", "_"); - if (normalized.isBlank()) { - normalized = "modifier"; - } - return Identifier.tryParse("iris:" + normalized); - } - - private static void applyItemFlags(IrisLoot loot, ItemStack stack) { - TooltipDisplay display = stack.getOrDefault(DataComponents.TOOLTIP_DISPLAY, TooltipDisplay.DEFAULT); - boolean changed = false; - - for (String flag : loot.getItemFlags()) { - if (flag == null || flag.isBlank()) { - continue; - } - DataComponentType hidden = tooltipComponentFor(flag.trim().toUpperCase(Locale.ROOT)); - if (hidden == null) { - warnOnce("itemFlag:" + flag, "Iris loot: item flag '" + flag + "' has no modded tooltip equivalent; skipping"); - continue; - } - display = display.withHidden(hidden, true); - changed = true; - } - - if (changed) { - stack.set(DataComponents.TOOLTIP_DISPLAY, display); - } - } - - private static DataComponentType tooltipComponentFor(String flag) { - return switch (flag) { - case "HIDE_ENCHANTS" -> DataComponents.ENCHANTMENTS; - case "HIDE_STORED_ENCHANTS" -> DataComponents.STORED_ENCHANTMENTS; - case "HIDE_ATTRIBUTES" -> DataComponents.ATTRIBUTE_MODIFIERS; - case "HIDE_UNBREAKABLE" -> DataComponents.UNBREAKABLE; - case "HIDE_DESTROYS" -> DataComponents.CAN_BREAK; - case "HIDE_PLACED_ON" -> DataComponents.CAN_PLACE_ON; - case "HIDE_DYE" -> DataComponents.DYED_COLOR; - case "HIDE_ARMOR_TRIM" -> DataComponents.TRIM; - default -> null; - }; - } - - private static void applyDyeColor(ItemStack stack, String dye) { - DyeColor color = DyeColor.byName(dye.trim().toLowerCase(Locale.ROOT), null); - if (color == null) { - warnOnce("dyeColor:" + dye, "Iris loot: unknown dyeColor '" + dye + "'"); - return; - } - stack.set(DataComponents.BASE_COLOR, color); - } - - private static void applyLore(IrisLoot loot, ItemStack stack) { - if (loot.getLore().isEmpty()) { - return; - } - - List lines = new ArrayList<>(); - for (String line : loot.getLore()) { - String colored = colorize(line); - if (colored.length() > 24) { - for (String wrapped : Form.wrapWords(colored, 24).split("\\Q\n\\E")) { - lines.add(Component.literal(wrapped.trim())); - } - } else { - lines.add(Component.literal(colored)); - } - } - stack.set(DataComponents.LORE, new ItemLore(lines)); - } - - private static void applyCustomNbt(ItemStack stack, KMap customNbt) { - if (customNbt == null || customNbt.isEmpty()) { - return; - } - try { - CompoundTag tag = TagParser.parseCompoundFully(new JSONObject(customNbt).toString()); - tag.merge(stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag()); - stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); - } catch (CommandSyntaxException e) { - warnOnce("customNbt:" + e.getMessage(), "Iris loot: invalid customNbt: " + e.getMessage()); - } - } - - private static String colorize(String text) { - char[] chars = text.toCharArray(); - for (int i = 0; i < chars.length - 1; i++) { - if (chars[i] == '&' && COLOR_CODES.indexOf(chars[i + 1]) > -1) { - chars[i] = '§'; - chars[i + 1] = Character.toLowerCase(chars[i + 1]); - } - } - return new String(chars); + public static NativeItemContext context(NativeWorld world) { + return new NativeItemContext(world, OPTIONS); } private static void warnOnce(String key, String message) { if (WARNED.add(key)) { - IrisLogging.warn(message); + IrisLogging.warn("Iris loot: " + message); } } - } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLifecycleCallbacks.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLifecycleCallbacks.java new file mode 100644 index 000000000..84c8d12b4 --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLifecycleCallbacks.java @@ -0,0 +1,35 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.modded.command.IrisModdedCommands; +import art.arcane.iris.modded.command.ModdedWandService; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedCallbacks; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; + +public final class ModdedLifecycleCallbacks { + private ModdedLifecycleCallbacks() { + } + + public static NativeModdedCallbacks create() { + return new NativeModdedCallbacks( + ModdedEngineBootstrap::serverAboutToStart, + ModdedEngineBootstrap::start, + ModdedEngineBootstrap::serverStarted, + ModdedEngineBootstrap::stop, + ModdedEngineBootstrap::levelLoaded, + ModdedEngineBootstrap::levelUnloaded, + ModdedProtocolHandler::onPlayerJoin, + ModdedProtocolHandler::onPlayerDisconnect, + ModdedForcedDatapack::repositorySource, + IrisModdedCommands::register, + ModdedBlockBreakHandler::prepare, + ModdedBlockBreakHandler::cancel, + ModdedBlockBreakHandler::clearPlacedProvenance, + ModdedWandService::attackBlock, + ModdedWandService::useBlock, + server -> { + ModdedEngineBootstrap.tick(server); + ModdedWandService.serverTick(server); + }); + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLoader.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLoader.java deleted file mode 100644 index 4ffa2b8b3..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLoader.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.block.state.BlockState; - -import java.io.File; -import java.nio.file.Path; - -public interface ModdedLoader { - String platformName(); - - String minecraftVersion(); - - String modVersion(); - - MinecraftServer currentServer(); - - void invalidateLevelCache(MinecraftServer server); - - void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level); - - void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level); - - boolean clientEnvironment(); - - Path configDir(); - - File modJar(); - - boolean hasTreeFellerPermission(ServerPlayer player); - - boolean canTreeFellerBreak(ServerLevel level, ServerPlayer player, BlockPos position, BlockState state); -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLootApplier.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLootApplier.java index 2886d4cc9..7129371bc 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLootApplier.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedLootApplier.java @@ -19,6 +19,9 @@ package art.arcane.iris.modded; import art.arcane.iris.generation.runtime.Engine; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeContainerLoot; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import art.arcane.iris.structure.placement.LootResolver; import art.arcane.iris.structure.placement.PlacedObject; import art.arcane.iris.structure.object.IObjectLoot; @@ -28,91 +31,60 @@ import art.arcane.iris.structure.object.IrisObjectLoot; import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.structure.object.IrisObjectVanillaLoot; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.matter.Matter; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.Container; -import net.minecraft.world.RandomizableContainer; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.ChestBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.ChestType; -import net.minecraft.world.level.storage.loot.LootParams; -import net.minecraft.world.level.storage.loot.LootTable; -import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; -import net.minecraft.world.level.storage.loot.parameters.LootContextParams; -import net.minecraft.world.phys.Vec3; import java.util.ArrayList; import java.util.List; -import java.util.function.Predicate; public final class ModdedLootApplier { private ModdedLootApplier() { } - public static void apply(Engine engine, ServerLevel level, BlockPos pos, BlockState state, MantleChunk mc) { - if (!isCanonicalContainer(pos, state)) { + public static void apply(Engine engine, NativeWorld world, NativeBlockPoint pos, NativeBlockState state, MantleChunk mc) { + NativeContainerLoot access = new NativeContainerLoot(new NativeContainerLoot.Target(world, pos, state)); + if (!access.canonical()) { return; } - RNG rng = LootResolver.containerRng(engine.getSeedManager().getLoot(), pos.getX(), pos.getY(), pos.getZ()); - boolean nativeLootDefined = hasNativeLootTable(level, pos, state); - List sources = resolveSources(engine, level, rng, pos, state, mc, nativeLootDefined); + RNG rng = LootResolver.containerRng(engine.getSeedManager().getLoot(), pos.x(), pos.y(), pos.z()); + List sources = resolveSources(engine, access, rng, pos, state, mc, access.hasNativeTable()); if (sources.isEmpty()) { return; } - - Container container = resolveContainer(level, pos, state); - if (container == null) { + NativeContainerLoot.Session session = access.open(); + if (session == null) { IrisLogging.debug("Iris loot: no container at " + pos); return; } - - KList items = new KList<>(); - LootParams nativeParams = null; for (LootSource source : sources) { if (source instanceof IrisLootSource irisSource) { IrisLootTable table = irisSource.table(); - if (table == null) { - continue; + if (table != null) { + session.add(ModdedItemTranslator.loot(table, engine.getSeedManager().getLoot(), InventorySlotType.STORAGE, + ModdedItemTranslator.context(world), pos.x(), pos.y(), pos.z())); } - items.addAll(ModdedItemTranslator.loot(table, engine.getSeedManager().getLoot(), InventorySlotType.STORAGE, level, pos.getX(), pos.getY(), pos.getZ())); - continue; - } - if (source instanceof NativeLootSource nativeSource) { - if (nativeParams == null) { - nativeParams = new LootParams.Builder(level) - .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) - .create(LootContextParamSets.CHEST); - } - items.addAll(nativeSource.table().getRandomItems(nativeParams, rng.nextLong())); + } else if (source instanceof NativeLootSource nativeSource) { + session.generate(nativeSource.table(), rng.nextLong()); } } - - fillContainer(container, items, rng); + session.fill(rng, message -> IrisLogging.debug("Iris loot: " + message)); } - private static List resolveSources(Engine engine, ServerLevel level, RNG rng, BlockPos pos, BlockState state, MantleChunk mc, boolean nativeLootDefined) { - int rx = pos.getX(); - int rz = pos.getZ(); - int ry = pos.getY() - engine.getWorld().minHeight(); + private static List resolveSources(Engine engine, NativeContainerLoot access, RNG rng, NativeBlockPoint pos, NativeBlockState state, MantleChunk mc, boolean nativeLootDefined) { + int rx = pos.x(); + int rz = pos.z(); + int ry = pos.y() - engine.getWorld().minHeight(); List sources = new ArrayList<>(); boolean objectLootDefined = nativeLootDefined; PlacedObject po = engine.getObjectPlacement(rx, ry, rz, mc); - if (po != null && po.getPlacement() != null && ModdedBlockResolution.isStorageChest(state)) { + if (po != null && po.getPlacement() != null && state.isStorageChest()) { objectLootDefined = objectLootDefined || po.getPlacement().getLoot().isNotEmpty() || po.getPlacement().getVanillaLoot().isNotEmpty(); - Candidate picked = pickPlacementTable(engine, level, po.getPlacement(), state, rng); + Candidate picked = pickPlacementTable(engine, access, po.getPlacement(), rng); if (picked != null) { sources.add(picked.source()); if (po.getPlacement().isOverrideGlobalLoot()) { @@ -134,7 +106,7 @@ public final class ModdedLootApplier { return sources; } - private static Candidate pickPlacementTable(Engine engine, ServerLevel level, IrisObjectPlacement placement, BlockState state, RNG rng) { + private static Candidate pickPlacementTable(Engine engine, NativeContainerLoot access, IrisObjectPlacement placement, RNG rng) { List exact = new ArrayList<>(); List basic = new ArrayList<>(); List global = new ArrayList<>(); @@ -148,188 +120,46 @@ public final class ModdedLootApplier { IrisLogging.warn("Couldn't find loot table " + loot.getName()); continue; } - bucket(engine, loot, new Candidate(new IrisLootSource(table), loot.getWeight()), state, exact, basic, global); + bucket(engine, loot, new Candidate(new IrisLootSource(table), loot.getWeight()), access, exact, basic, global); } for (IrisObjectVanillaLoot loot : placement.getVanillaLoot()) { if (loot == null || loot.getWeight() <= 0) { continue; } - ResourceKey key = resolveNativeKey(loot.getName(), candidate -> hasNativeTable(level, candidate)); - if (key == null) { + NativeContainerLoot.Table table = access.table(loot.getName()); + if (table == null) { IrisLogging.warn("Couldn't find vanilla loot table " + loot.getName()); continue; } - LootTable table = level.getServer().reloadableRegistries().lookup() - .lookupOrThrow(Registries.LOOT_TABLE) - .getOrThrow(key) - .value(); - bucket(engine, loot, new Candidate(new NativeLootSource(table), loot.getWeight()), state, exact, basic, global); + bucket(engine, loot, new Candidate(new NativeLootSource(table), loot.getWeight()), access, exact, basic, global); } List pool = !exact.isEmpty() ? exact : !basic.isEmpty() ? basic : global; return LootResolver.pickWeighted(pool, Candidate::weight, rng); } - static ResourceKey resolveNativeKey(String name, Predicate> registryContains) { - Identifier id = name == null ? null : Identifier.tryParse(name); - if (id == null) { - return null; - } - ResourceKey key = ResourceKey.create(Registries.LOOT_TABLE, id); - return registryContains.test(key) ? key : null; - } - - static void fillContainer(Container container, List items, RNG rng) { - for (ItemStack item : items) { - addItem(container, item); - } - scramble(container, rng); - container.setChanged(); - } - - private static boolean hasNativeTable(ServerLevel level, ResourceKey key) { - return level.getServer().reloadableRegistries().lookup() - .lookup(Registries.LOOT_TABLE) - .flatMap(registry -> registry.get(key)) - .isPresent(); - } - - private static void bucket(Engine engine, IObjectLoot loot, Candidate candidate, BlockState state, List exact, List basic, List global) { + private static void bucket(Engine engine, IObjectLoot loot, Candidate candidate, NativeContainerLoot access, List exact, List basic, List global) { if (loot.getFilter().isEmpty()) { global.add(candidate); return; } - for (PlatformBlockState filterState : loot.getFilter(engine.getData())) { - BlockState filter = (BlockState) filterState.nativeHandle(); - if (loot.isExact() ? filter == state : filter.getBlock() == state.getBlock()) { + for (NativeBlockState filterState : loot.getFilter(engine.getData())) { + if (access.matches(filterState, loot.isExact())) { (loot.isExact() ? exact : basic).add(candidate); return; } } } - static boolean isCanonicalContainer(BlockPos pos, BlockState state) { - if (!(state.getBlock() instanceof ChestBlock) || state.getValue(ChestBlock.TYPE) == ChestType.SINGLE) { - return true; - } - BlockPos connected = ChestBlock.getConnectedBlockPos(pos, state); - if (connected.equals(pos)) { - return true; - } - return isCanonicalPair(pos, connected); - } - - static boolean isCanonicalPair(BlockPos pos, BlockPos connected) { - return pos.getX() < connected.getX() - || pos.getX() == connected.getX() && pos.getZ() <= connected.getZ(); - } - - static boolean hasNativeLootTable(ServerLevel level, BlockPos pos, BlockState state) { - if (hasNativeLootTableAt(level, pos)) { - return true; - } - if (!(state.getBlock() instanceof ChestBlock) || state.getValue(ChestBlock.TYPE) == ChestType.SINGLE) { - return false; - } - BlockPos connected = ChestBlock.getConnectedBlockPos(pos, state); - return level.hasChunkAt(connected) && hasNativeLootTableAt(level, connected); - } - - private static boolean hasNativeLootTableAt(ServerLevel level, BlockPos pos) { - return hasNativeLootTable(level.getBlockEntity(pos)); - } - - static boolean hasNativeLootTable(BlockEntity blockEntity) { - return blockEntity instanceof RandomizableContainer container && hasNativeLootTable(container); - } - - static boolean hasNativeLootTable(RandomizableContainer container) { - return container != null && container.getLootTable() != null; - } - - private static Container resolveContainer(ServerLevel level, BlockPos pos, BlockState state) { - if (state.getBlock() instanceof ChestBlock chestBlock) { - Container combined = ChestBlock.getContainer(chestBlock, state, level, pos, true); - if (combined != null) { - return combined; - } - } - BlockEntity blockEntity = level.getBlockEntity(pos); - return blockEntity instanceof Container container ? container : null; - } - - private static void addItem(Container container, ItemStack stack) { - if (stack == null || stack.isEmpty()) { - return; - } - for (int i = 0; i < container.getContainerSize() && !stack.isEmpty(); i++) { - ItemStack existing = container.getItem(i); - if (existing.isEmpty()) { - container.setItem(i, stack.split(container.getMaxStackSize(stack))); - continue; - } - if (ItemStack.isSameItemSameComponents(existing, stack)) { - int limit = container.getMaxStackSize(existing); - if (existing.getCount() < limit) { - int move = Math.min(stack.getCount(), limit - existing.getCount()); - existing.grow(move); - stack.shrink(move); - } - } - } - if (!stack.isEmpty()) { - IrisLogging.debug("Iris loot: container full, dropped " + stack.getCount() + "x " + stack.getItem()); - } - } - - private static void scramble(Container container, RNG rng) { - int size = container.getContainerSize(); - ItemStack[] items = new ItemStack[size]; - for (int i = 0; i < size; i++) { - items[i] = container.getItem(i); - } - boolean packedFull = false; - - splitting: - for (int i = 0; i < items.length; i++) { - ItemStack is = items[i]; - - if (!is.isEmpty() && is.getCount() > 1 && !packedFull) { - for (int j = 0; j < items.length; j++) { - if (items[j].isEmpty()) { - int take = rng.nextInt(is.getCount()); - take = take == 0 ? 1 : take; - is.setCount(is.getCount() - take); - items[j] = is.copyWithCount(take); - continue splitting; - } - } - - packedFull = true; - } - } - - for (int i = items.length; i > 1; i--) { - int j = rng.nextInt(i); - ItemStack tmp = items[i - 1]; - items[i - 1] = items[j]; - items[j] = tmp; - } - - for (int i = 0; i < size; i++) { - container.setItem(i, items[i]); - } - } - private interface LootSource { } private record IrisLootSource(IrisLootTable table) implements LootSource { } - private record NativeLootSource(LootTable table) implements LootSource { + private record NativeLootSource(NativeContainerLoot.Table table) implements LootSource { } private record Candidate(LootSource source, int weight) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinAudit.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinAudit.java index 9aa48318d..0e41da5f3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinAudit.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinAudit.java @@ -18,8 +18,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.modded.NativeMixinFlags; -import java.lang.reflect.Method; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeMixinTarget; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -42,26 +44,26 @@ public final class ModdedMixinAudit { private static final List EXPECTED = List.of( new ExpectedMixin("EntityPersistenceMixin", "entity", - "net.minecraft.world.entity.Entity", "iris$applyGeneratedPersistence", - false, ModdedMixinFlags::entityPersistenceRan), + NativeMixinTarget.ENTITY, "iris$applyGeneratedPersistence", + false, NativeMixinFlags::entityPersistenceRan), new ExpectedMixin("LivingEntityLootMixin", "entity", - "net.minecraft.world.entity.LivingEntity", "iris$replaceBaseLoot", - false, ModdedMixinFlags::livingEntityLootRan), + NativeMixinTarget.LIVING_ENTITY, "iris$replaceBaseLoot", + false, NativeMixinFlags::livingEntityLootRan), new ExpectedMixin("MobAwarenessMixin", "entity", - "net.minecraft.world.entity.Mob", "iris$tickUnawareMob", - false, ModdedMixinFlags::mobAwarenessRan), + NativeMixinTarget.MOB, "iris$tickUnawareMob", + false, NativeMixinFlags::mobAwarenessRan), new ExpectedMixin("StructureTemplatePaletteConcurrencyMixin", "common", - "net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate$Palette", + NativeMixinTarget.STRUCTURE_PALETTE, "iris$installConcurrentBlockCache", - false, ModdedMixinFlags::structureTemplatePaletteRan), - new ExpectedMixin("IrisWorldOpenFlowsMixin", "client", - "net.minecraft.client.gui.screens.worldselection.WorldOpenFlows", + false, NativeMixinFlags::structureTemplatePaletteRan), + new ExpectedMixin("NativeWorldOpenFlowsMixin", "client", + NativeMixinTarget.WORLD_OPEN_FLOWS, "iris$openWorldCheckWorldStemCompatibility", - true, ModdedMixinFlags::worldOpenFlowsRan), - new ExpectedMixin("IrisWorldTypeEntryMixin", "client", - "net.minecraft.client.gui.screens.worldselection.WorldCreationUiState$WorldTypeEntry", + true, NativeMixinFlags::worldOpenFlowsRan), + new ExpectedMixin("NativeWorldTypeEntryMixin", "client", + NativeMixinTarget.WORLD_TYPE_ENTRY, "iris$describePreset", - true, ModdedMixinFlags::worldTypeEntryRan)); + true, NativeMixinFlags::worldTypeEntryRan)); private ModdedMixinAudit() { } @@ -110,25 +112,14 @@ public final class ModdedMixinAudit { private static boolean isApplied(ExpectedMixin expected) { try { - Class target = Class.forName(expected.targetClass(), false, - ModdedMixinAudit.class.getClassLoader()); - for (Method method : target.getDeclaredMethods()) { - // Mixin 0.8.7 renames applied @Inject handlers to handler$$, so an exact - // name match alone reports every applied mixin as missing. - String name = method.getName(); - if (name.equals(expected.handlerMethod()) - || name.endsWith('$' + expected.handlerMethod())) { - return true; - } - } - return false; + return expected.targetClass().hasInjectedHandler(expected.handlerMethod()); } catch (ClassNotFoundException | LinkageError unavailable) { ModdedIrisLog.warn("Iris mixin audit could not inspect {}", expected.targetClass(), unavailable); return true; } } - private record ExpectedMixin(String mixinName, String config, String targetClass, String handlerMethod, + private record ExpectedMixin(String mixinName, String config, NativeMixinTarget targetClass, String handlerMethod, boolean clientOnly, BooleanSupplier ran) { } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinFlags.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinFlags.java deleted file mode 100644 index 3695d2080..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedMixinFlags.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -/** - * Plain flag holder every Iris mixin marks from its injected body. It is deliberately free of any - * net.minecraft, client or loader reference so the client-dist mixins in art.arcane.iris.client.mixin can - * write to it without dragging client types into modded-common. - * - *

These flags say "the injected code ran at least once", which is weaker than "the mixin was applied": - * a hook only fires when its target path executes. {@link ModdedMixinAudit} is what proves application at - * boot; these are the runtime confirmation reported alongside it. - */ -public final class ModdedMixinFlags { - private static volatile boolean entityPersistenceRan; - private static volatile boolean livingEntityLootRan; - private static volatile boolean mobAwarenessRan; - private static volatile boolean structureTemplatePaletteRan; - private static volatile boolean worldOpenFlowsRan; - private static volatile boolean worldTypeEntryRan; - - private ModdedMixinFlags() { - } - - // Read before write on every marker: the entity hooks run per entity save and per mob tick, and a - // volatile store is a cache-line invalidation on every core that reads the flag. The flags are one-way, - // so the guarded write costs one plain-ish read once set and races only ever re-store the same value. - public static void markEntityPersistence() { - if (!entityPersistenceRan) { - entityPersistenceRan = true; - } - } - - public static void markLivingEntityLoot() { - if (!livingEntityLootRan) { - livingEntityLootRan = true; - } - } - - public static void markMobAwareness() { - if (!mobAwarenessRan) { - mobAwarenessRan = true; - } - } - - public static void markStructureTemplatePalette() { - if (!structureTemplatePaletteRan) { - structureTemplatePaletteRan = true; - } - } - - public static void markWorldOpenFlows() { - if (!worldOpenFlowsRan) { - worldOpenFlowsRan = true; - } - } - - public static void markWorldTypeEntry() { - if (!worldTypeEntryRan) { - worldTypeEntryRan = true; - } - } - - public static boolean entityPersistenceRan() { - return entityPersistenceRan; - } - - public static boolean livingEntityLootRan() { - return livingEntityLootRan; - } - - public static boolean mobAwarenessRan() { - return mobAwarenessRan; - } - - public static boolean structureTemplatePaletteRan() { - return structureTemplatePaletteRan; - } - - public static boolean worldOpenFlowsRan() { - return worldOpenFlowsRan; - } - - public static boolean worldTypeEntryRan() { - return worldTypeEntryRan; - } - - static void reset() { - entityPersistenceRan = false; - livingEntityLootRan = false; - mobAwarenessRan = false; - structureTemplatePaletteRan = false; - worldOpenFlowsRan = false; - worldTypeEntryRan = false; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructurePolicy.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructurePolicy.java new file mode 100644 index 000000000..39b1342bf --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructurePolicy.java @@ -0,0 +1,163 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureStagePolicy; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.terrain.IrisMaterialPalette; +import art.arcane.iris.structure.nativegen.GenerationWritePolicy; +import art.arcane.iris.structure.nativegen.IrisStructurePolicy; +import art.arcane.iris.structure.nativegen.IrisStructureVolumePolicy; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; +import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.structure.nativegen.StructureLocateSearch; +import art.arcane.iris.structure.placement.IrisStructureLocator; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedStructureStage; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVolumeSource; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateSearchAccess; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePalette; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePlacementDecision; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferencePolicy; +import art.arcane.volmlib.nativelib.terrain.structure.StructureVolumePolicy; +import art.arcane.volmlib.util.math.RNG; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntBinaryOperator; + +final class ModdedNativeStructurePolicy implements NativeModdedStructureStage.Policy< + Engine, NativeStructureStartPlan, NativeStructureOwnershipRecord> { + private static final int WORLD_CHECK_SHIFT_RECORD_LIMIT = 4096; + private static final boolean WORLD_CHECK_ENABLED = Boolean.getBoolean("iris.worldcheck"); + + private final IrisModdedChunkGenerator generator; + private final ConcurrentHashMap worldCheckStructureShifts = new ConcurrentHashMap<>(); + + ModdedNativeStructurePolicy(IrisModdedChunkGenerator generator) { + this.generator = generator; + } + + @Override + public Engine current() { + return generator.engine(); + } + + @Override + public StructureReferencePolicy structurePolicy(Engine context) { + return new IrisStructurePolicy(context); + } + + @Override + public StructureVolumePolicy volumePolicy() { + return IrisStructureVolumePolicy.INSTANCE; + } + + @Override + public void installVolumeSource(Engine context, NativeStructureVolumeSource source) { + NativeStructureVolumeIndex.install(context, source::volumesAt); + } + + @Override + public boolean hasPlacement(Engine context, String key) { + return IrisStructureLocator.hasNativePlacement(context, key); + } + + @Override + public StructureLocateSearchAccess locateSearch( + Engine context, StructureStagePolicy.LocateRequest request) { + return new StructureLocateSearch<>(new StructureLocateSearch.Options<>( + context, request.key(), request.x(), request.z(), request.radius(), request.probe(), request.ownership())); + } + + @Override + public int locatorY(NativeStructureOwnershipRecord ownership) { + return ownership.locatorY(); + } + + @Override + public StructurePlacementDecision restoredDecision(NativeStructureOwnershipRecord ownership) { + return ownership.restoredDecision(); + } + + @Override + public boolean allowsFootprint(Engine context, StructureStagePolicy.Footprint footprint) { + return context.getComplex().allowsNewGenerationFootprint( + footprint.minX(), footprint.minZ(), footprint.maxX(), footprint.maxZ()); + } + + @Override + public boolean allowsChunkWrite(Engine context, int chunkX, int chunkZ) { + return context.getComplex().allowsMantleChunkWrite(chunkX, chunkZ); + } + + @Override + public boolean historicalStructure(Engine context, long activation) { + return GenerationWritePolicy.isHistoricalStructure(context, activation); + } + + @Override + public boolean stacked(Engine context) { + return context.getDimensionStackContext() != null; + } + + @Override + public NativeBlockPositionPredicate protectedPositions(Engine context) { + return GenerationWritePolicy.protectedPositions(context.getDimension().getStaticObjectLayer(context.getData()), + context.getDimensionStackContext(), context.getMinHeight()); + } + + @Override + public IntBinaryOperator surfaceHeight(Engine context) { + return (x, z) -> Engine.hostHeight(context, x, z, true) + context.getMinHeight(); + } + + @Override + public IntBinaryOperator worldgenHeight(Engine context, int runtimeMinY, boolean floor) { + return (x, z) -> Engine.hostHeight(context, x, z, floor) + runtimeMinY + 1; + } + + @Override + public NativeBlockState paletteBlock(StructurePalette source, RNG rng, int x, int y, int z) { + return ((IrisMaterialPalette) source).get(rng, x, y, z, generator.engine().getData()); + } + + @Override + public RuntimeException structuresDisabled(int chunkX, int chunkZ) { + return new IllegalStateException("Iris cannot generate native structures in chunk " + + chunkX + "," + chunkZ + + " because generate-structures=false disables them outside the pack. That flag is fixed when " + + "the world is created (server.properties generate-structures, or the Generate Structures " + + "toggle in singleplayer), so it cannot be changed for this world: create a new world with " + + "structures enabled, then deny families through importedStructures.disabled or complete keys " + + "through importedStructures.disabledExact"); + } + + @Override + public String generationLabel(String structureId) { + return "Iris native structure " + structureId; + } + + @Override + public void recordShift(String structureId, long startChunk, int offsetY) { + if (!WORLD_CHECK_ENABLED || structureId == null) { + return; + } + if (worldCheckStructureShifts.size() >= WORLD_CHECK_SHIFT_RECORD_LIMIT) { + worldCheckStructureShifts.clear(); + } + worldCheckStructureShifts.put(new StructureStartKey(structureId, startChunk), offsetY); + } + + @Override + public Integer recordedShift(String structureId, long startChunk) { + return worldCheckStructureShifts.get(new StructureStartKey(structureId, startChunk)); + } + + @Override + public void clearShifts() { + worldCheckStructureShifts.clear(); + } + + private record StructureStartKey(String structureId, long chunkPosition) { + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureStage.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureStage.java deleted file mode 100644 index 36d15997f..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureStage.java +++ /dev/null @@ -1,598 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import net.minecraft.world.level.chunk.status.ChunkStatus; - -import art.arcane.iris.nativegen.NativeGenerationWriteGuard; - -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.placement.IrisStructureLocator; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.structure.object.IrisStaticObjectLayer; -import art.arcane.iris.nativegen.NativeStructureGenerationException; -import art.arcane.iris.nativegen.NativeStructureLocatePersistence; -import art.arcane.iris.nativegen.NativeStructureLocateResults; -import art.arcane.iris.nativegen.NativeStructureOwnershipRecovery; -import art.arcane.iris.nativegen.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.NativeStructureReferenceEnvelope; -import art.arcane.iris.nativegen.NativeStructureSurfaceFitter; -import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator; -import art.arcane.iris.nativegen.NativeStructureVegetationClearer; -import art.arcane.iris.nativegen.NativeStructureVerticalPlacer; -import art.arcane.iris.nativegen.NativeStructureVanillaLocator; -import art.arcane.iris.nativegen.NativeStructureVolumeIndex; -import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.volmlib.util.math.RNG; -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiTypes; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.GenerationStep; -import net.minecraft.world.level.levelgen.RandomSupport; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.BiConsumer; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; - -/** - * Native (vanilla registry) structure stage for {@link IrisModdedChunkGenerator}. The generator keeps the - * {@link net.minecraft.world.level.chunk.ChunkGenerator} overrides because they issue {@code super} calls; - * everything they do beyond that lives here. - */ -final class ModdedNativeStructureStage { - private static final int WORLD_CHECK_SHIFT_RECORD_LIMIT = 4096; - private static final boolean WORLD_CHECK_ENABLED = Boolean.getBoolean("iris.worldcheck"); - - private final IrisModdedChunkGenerator generator; - private final ConcurrentHashMap worldCheckStructureShifts = new ConcurrentHashMap<>(); - private volatile StructureStepCache structureStepCache; - - ModdedNativeStructureStage(IrisModdedChunkGenerator generator) { - this.generator = generator; - } - - void installVolumeIndex(ServerLevel level, Engine engine) { - WeakReference levelReference = new WeakReference<>(level); - WeakReference generatorReference = new WeakReference<>(generator); - WeakReference biomeSourceReference = new WeakReference<>(generator.structureBiomeSource); - NativeStructureVolumeIndex.install(engine, new NativeStructureVolumeIndex.Context( - level.registryAccess(), - level.getServer().getStructureManager(), - level.dimension(), - LevelHeightAccessor.create(level.getMinY(), level.getHeight()), - generatorReference::get, - biomeSourceReference::get, - () -> { - ServerLevel active = levelReference.get(); - return active == null ? null : active.getChunkSource().getGeneratorState(); - })); - } - - Pair> findNearestIrisStructure(ServerLevel level, - HolderSet holders, - BlockPos pos, int radius, boolean findUnexplored, - Engine current, - NativeStructureVanillaLocator.Candidate nativeCandidate) { - Pair> nativeLocated = - nativeCandidate == null ? null : nativeCandidate.result(); - Runnable nativeReference = () -> { - if (nativeCandidate != null) { - nativeCandidate.reference(level.structureManager()); - } - }; - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List searches = new ArrayList<>(holders.size()); - NativeStructureLocatePersistence.ProbeBudget budget = NativeStructureLocatePersistence.probeBudget(); - for (Holder holder : holders) { - Identifier id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure locate received an unregistered structure holder"); - } - String structureId = id.toString(); - if (!IrisStructureLocator.hasNativePlacement(current, structureId)) { - continue; - } - NativeStructureLocatePersistence.Probe probe = NativeStructureLocatePersistence.probe( - level, holder.value(), findUnexplored, budget); - searches.add(new IrisNativeLocateSearch( - holder, structureId, NativeStructureLocatePersistence.search( - current, structureId, pos.getX(), pos.getZ(), radius, probe))); - } - searches.sort(Comparator.comparing(IrisNativeLocateSearch::structureId)); - for (int attempt = 0; attempt < NativeStructureLocatePersistence.MAX_SELECTED_CANDIDATE_RETRIES; attempt++) { - IrisNativeLocateSearch bestSearch = null; - IrisStructureLocator.LocateResult bestResult = null; - long bestDistance = Long.MAX_VALUE; - for (IrisNativeLocateSearch search : searches) { - IrisStructureLocator.LocateResult result = search.search().predict(); - if (result.status() == IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED) { - throw new IllegalStateException("Iris structure locate reached its safety limit for " - + search.structureId() + " within " + radius + " placement rings"); - } - if (!result.found()) { - continue; - } - long dx = (long) result.originX() - pos.getX(); - long dz = (long) result.originZ() - pos.getZ(); - long distance = dx * dx + dz * dz; - if (distance < bestDistance) { - bestDistance = distance; - bestSearch = search; - bestResult = result; - } - } - if (bestSearch == null) { - return NativeStructureLocateResults.selectAndReference( - pos, null, () -> { }, nativeLocated, nativeReference); - } - Pair> predicted = Pair.of( - new BlockPos(bestResult.originX(), bestResult.baseY(), bestResult.originZ()), - bestSearch.holder()); - if (NativeStructureLocateResults.nearest(pos, predicted, nativeLocated) != predicted) { - return NativeStructureLocateResults.selectAndReference( - pos, predicted, () -> { }, nativeLocated, nativeReference); - } - NativeStructureLocatePersistence.VerifiedStart verified = - bestSearch.search().verify(bestResult); - if (verified == null) { - bestSearch.search().reject(bestResult); - continue; - } - BlockPos located = new BlockPos( - bestResult.originX(), verified.ownership().locatorY(), - bestResult.originZ()); - Pair> irisLocated = Pair.of(located, bestSearch.holder()); - IrisNativeLocateSearch selectedSearch = bestSearch; - NativeStructureLocatePersistence.VerifiedStart selectedStart = verified; - return NativeStructureLocateResults.selectAndReference( - pos, irisLocated, () -> selectedSearch.search().reference(selectedStart), - nativeLocated, nativeReference); - } - throw new IllegalStateException("Iris structure locate rejected too many selected candidates within " - + radius + " placement rings"); - } - - HolderSet filterReachableNativeStructures(ServerLevel level, HolderSet holders, - Engine current) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List> kept = new ArrayList<>(holders.size()); - for (Holder holder : holders) { - Identifier id = registry.getKey(holder.value()); - if (id == null) { - throw new IllegalStateException("Native structure filtering received an unregistered structure holder"); - } - String key = id.toString(); - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(current, - key, NativeStructureVegetationClearer.isUndergroundStep(holder.value().step())); - if (!decision.generate() || !generator.structureBiomeSource.isStructureReachable(holder)) { - continue; - } - kept.add(holder); - } - return kept.size() == holders.size() ? holders : HolderSet.direct(kept); - } - - void adjustGeneratedStructures(RegistryAccess registryAccess, ChunkAccess chunk, - Map previousStarts, - Map configuredStarts, - Engine current, - StructureTemplateManager templateManager) { - Registry registry = registryAccess.lookupOrThrow(Registries.STRUCTURE); - ChunkPos chunkPos = chunk.getPos(); - for (Map.Entry entry : chunk.getAllStarts().entrySet()) { - Structure structure = entry.getKey(); - StructureStart start = entry.getValue(); - if (!start.isValid() || previousStarts.get(structure) == start) { - continue; - } - Identifier id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - BoundingBox footprint = start.getBoundingBox(); - if (!current.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - chunk.setStartForStructure(structure, StructureStart.INVALID_START); - if (configuredStarts.containsKey(structure)) { - NativeStructureOwnershipStore.discard( - current, structureId, chunkPos.x(), chunkPos.z()); - } - continue; - } - if (configuredStarts.containsKey(structure)) { - recordWorldCheckStructureShift( - configuredStarts.get(structure).source().getStructure(), start.getChunkPos(), 0); - continue; - } - boolean undergroundStep = NativeStructureVegetationClearer.isUndergroundStep(structure.step()); - IrisNativeStructureDecision decision; - try { - decision = NativeStructureGenerationPolicy.resolve(current, - structureId, undergroundStep); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "policy resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - if (!decision.generate()) { - chunk.setStartForStructure(structure, StructureStart.INVALID_START); - continue; - } - int offsetY; - try { - offsetY = NativeStructureVerticalPlacer.applyVerticalPlacement( - start, - structureId, - decision.yShift(), - generator.getSeaLevel(), - chunk.getMinY(), - chunk.getMinY() + chunk.getHeight(), - undergroundStep, - decision.preserveSourceY(), - decision.yBand(), - (x, z) -> Engine.hostHeight(current, x, z, true) + current.getMinHeight()); - StructureStart wrapped = NativeStructureReferenceEnvelope.wrapForPublication( - start, structure, start.getReferences(), - NativeStructureTerrainIntegrator.resolveNativeTerrain(start, decision.terrain()), - structureId); - chunk.setStartForStructure(structure, wrapped); - if (!wrapped.isValid()) { - continue; - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vertical adjustment", structureId, chunkPos.x(), chunkPos.z(), error); - } - recordWorldCheckStructureShift(structureId, start.getChunkPos(), offsetY); - } - } - - void placeVanillaStructures(WorldGenLevel world, ChunkAccess chunk, StructureManager structureManager) { - if (!structureManager.shouldGenerateStructures()) { - ChunkPos disabledChunk = chunk.getPos(); - throw new IllegalStateException("Iris cannot generate native structures in chunk " - + disabledChunk.x() + "," + disabledChunk.z() - + " because generate-structures=false disables them outside the pack. That flag is fixed when " - + "the world is created (server.properties generate-structures, or the Generate Structures " - + "toggle in singleplayer), so it cannot be changed for this world: create a new world with " - + "structures enabled, then deny families through importedStructures.disabled or complete keys " - + "through importedStructures.disabledExact"); - } - ChunkPos chunkPos = chunk.getPos(); - SectionPos sectionPos = SectionPos.of(chunkPos, world.getMinSectionY()); - BlockPos origin = sectionPos.origin(); - Registry registry = world.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List> byStep = structuresByStep(registry); - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); - long decorationSeed = random.setDecorationSeed(world.getSeed(), origin.getX(), origin.getZ()); - BoundingBox area = writableArea(chunk); - int steps = GenerationStep.Decoration.values().length; - Engine current = generator.engine(); - List placementGroups = new ArrayList<>(); - List heightmapStarts = new ArrayList<>(); - List vegetationTargets = new ArrayList<>(); - List terrainTargets = new ArrayList<>(); - for (int step = 0; step < steps; step++) { - int index = 0; - for (Structure structure : byStep.get(step)) { - Identifier id = registry.getKey(structure); - String structureId = id == null ? null : id.toString(); - if (structureId == null) { - throw NativeStructureGenerationException.failure( - "resolution", null, chunkPos.x(), chunkPos.z()); - } - try { - IrisNativeStructureDecision sourceDecision = NativeStructureGenerationPolicy.resolve(current, - structureId, NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - List starts = structureManager.startsForStructure(sectionPos, structure); - List resolvedPlacements = new ArrayList<>(starts.size()); - for (StructureStart start : starts) { - BoundingBox footprint = start.getBoundingBox(); - if (!isHistoricalStructureStart(current, world, start) - && !current.getComplex().allowsNewGenerationFootprint( - footprint.minX(), - footprint.minZ(), - footprint.maxX(), - footprint.maxZ())) { - continue; - } - NativeStructureOwnershipRecord ownership = - NativeStructureOwnershipRecovery.resolve( - current, world.getLevel(), structureId, structure, start); - IrisNativeStructureDecision decision = - ownership == null ? sourceDecision : ownership.restoredDecision(); - if (!decision.generate()) { - continue; - } - resolvedPlacements.add(new NativePlacement(start, decision)); - heightmapStarts.add(start); - terrainTargets.add(new NativeStructureTerrainIntegrator.TerrainTarget( - structureId, start, - NativeStructureTerrainIntegrator.resolveNativeTerrain( - start, decision.terrain()))); - vegetationTargets.add(start); - } - if (!resolvedPlacements.isEmpty()) { - placementGroups.add(new NativePlacementGroup( - structureId, index, step, List.copyOf(resolvedPlacements))); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "resolution", structureId, chunkPos.x(), chunkPos.z(), error); - } - index++; - } - } - if (!placementGroups.isEmpty()) { - ServerLevel level = world.getLevel(); - visitExistingPois(chunk, (position, state) -> level.updatePOIOnBlockStateChange( - position, Blocks.AIR.defaultBlockState(), state)); - } - try { - int runtimeMinY = world.getMinY(); - WorldgenTerrainHeightmaps.primeStructurePlacement( - world, chunkPos, heightmapStarts, - worldgenSurfaceHeight(current, runtimeMinY), - worldgenFloorHeight(current, runtimeMinY)); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "heightmap priming", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - IrisStaticObjectLayer staticObjects = current.getDimension().getStaticObjectLayer(current.getData()); - Predicate protectedPosition = NativeGenerationWriteGuard.protectedPositions( - staticObjects, current.getDimensionStackContext(), current.getMinHeight()); - WorldGenLevel boundedWorld = ModdedNativeStructureWorldgenAccess.create( - world, chunkPos, worldgenSurfaceHeight(current, world.getMinY()), worldgenFloorHeight(current, world.getMinY()), - current.getDimensionStackContext() != null, - protectedPosition); - try { - NativeStructureVegetationClearer.clearIntersectingVegetation( - boundedWorld, chunk, area, vegetationTargets); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "vegetation cleanup", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - NativeStructureSurfaceFitter.SurfaceTerrainPlan surfaceTerrainPlan; - try { - surfaceTerrainPlan = NativeStructureSurfaceFitter.prepareSurfaceStructures( - boundedWorld, area, terrainTargets, - (x, z) -> Engine.hostHeight(current, x, z, true) + current.getMinHeight()); - surfaceTerrainPlan.primeHeightmaps(chunk); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain integration", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - try { - NativeStructurePostProcessor.prepareTerrain( - boundedWorld, area, terrainTargets, this::resolvePaletteBlock); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "terrain preparation", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - for (NativePlacementGroup group : placementGroups) { - random.setFeatureSeed(decorationSeed, group.featureIndex(), group.step()); - try { - for (NativePlacement placement : group.placements()) { - placeVanillaStructure(boundedWorld, structureManager, random, area, chunkPos, - group.structureId(), placement.start(), placement.decision()); - } - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "placement", group.structureId(), chunkPos.x(), chunkPos.z(), error); - } - } - try { - NativeStructureSurfaceFitter.repairVacuumFoundations( - boundedWorld, area, surfaceTerrainPlan); - } catch (Throwable error) { - throw NativeStructureGenerationException.failure( - "foundation repair", nativeStructureBatchContext(placementGroups), - chunkPos.x(), chunkPos.z(), error); - } - } - - static void visitExistingPois(ChunkAccess chunk, BiConsumer visitor) { - chunk.findBlocks(PoiTypes::hasPoi, visitor); - } - - private static String nativeStructureBatchContext(List placementGroups) { - if (placementGroups.isEmpty()) { - return ""; - } - StringBuilder context = new StringBuilder("["); - for (int i = 0; i < placementGroups.size(); i++) { - if (i > 0) { - context.append(", "); - } - context.append(placementGroups.get(i).structureId()); - } - return context.append(']').toString(); - } - - private void placeVanillaStructure(WorldGenLevel world, StructureManager structureManager, - WorldgenRandom random, BoundingBox area, ChunkPos chunkPos, - String structureId, StructureStart start, - IrisNativeStructureDecision decision) { - Engine current = generator.engine(); - WorldGenLevel boundedWorld = world instanceof ModdedNativeStructureWorldgenAccess ? world : ModdedNativeStructureWorldgenAccess.create( - world, - chunkPos, - worldgenSurfaceHeight(current, world.getMinY()), - worldgenFloorHeight(current, world.getMinY()), - current.getDimensionStackContext() != null, - NativeGenerationWriteGuard.protectedPositions( - current.getDimension().getStaticObjectLayer(current.getData()), - current.getDimensionStackContext(), current.getMinHeight())); - world.setCurrentlyGenerating(() -> "Iris native structure " + structureId); - try { - NativeStructurePostProcessor.place( - boundedWorld, structureManager, generator, random, area, chunkPos, - structureId, start, decision, this::resolvePaletteBlock, - (x, z) -> Engine.hostHeight(current, x, z, true) + current.getMinHeight()); - } finally { - world.setCurrentlyGenerating(null); - } - } - - private List> structuresByStep(Registry registry) { - StructureStepCache cached = structureStepCache; - if (cached != null && cached.registry() == registry) { - return cached.structures(); - } - synchronized (generator) { - cached = structureStepCache; - if (cached != null && cached.registry() == registry) { - return cached.structures(); - } - int steps = GenerationStep.Decoration.values().length; - List> grouped = new ArrayList<>(steps); - for (int step = 0; step < steps; step++) { - grouped.add(new ArrayList<>()); - } - for (Structure structure : registry) { - grouped.get(structure.step().ordinal()).add(structure); - } - for (int step = 0; step < steps; step++) { - grouped.set(step, List.copyOf(grouped.get(step))); - } - List> resolved = List.copyOf(grouped); - structureStepCache = new StructureStepCache(registry, resolved); - return resolved; - } - } - - private void recordWorldCheckStructureShift(String structureId, ChunkPos startChunk, int offsetY) { - if (!WORLD_CHECK_ENABLED || structureId == null) { - return; - } - if (worldCheckStructureShifts.size() >= WORLD_CHECK_SHIFT_RECORD_LIMIT) { - worldCheckStructureShifts.clear(); - } - worldCheckStructureShifts.put(new NativeStructureStartKey(structureId, startChunk.pack()), offsetY); - } - - Integer worldCheckStructureShift(String structureId, ChunkPos startChunk) { - if (structureId == null || startChunk == null) { - return null; - } - return worldCheckStructureShifts.get(new NativeStructureStartKey(structureId, startChunk.pack())); - } - - void clearWorldCheckStructureShifts() { - worldCheckStructureShifts.clear(); - } - - private BlockState resolvePaletteBlock(IrisMaterialPalette palette, RNG rng, - int x, int y, int z) { - PlatformBlockState platformState = palette.get(rng, x, y, z, generator.engine().getData()); - if (platformState == null || !(platformState.nativeHandle() instanceof BlockState blockState)) { - throw new IllegalStateException("Configured native structure palette did not resolve a Minecraft block at " - + x + "," + y + "," + z); - } - return blockState; - } - - private boolean isHistoricalStructureStart(Engine current, WorldGenLevel world, StructureStart start) { - ChunkPos origin = start.getChunkPos(); - if (!current.getComplex().allowsMantleChunkWrite(origin.x(), origin.z())) { - return true; - } - ChunkAccess source = world.getChunk(origin.x(), origin.z(), ChunkStatus.EMPTY, false); - return source != null && NativeGenerationWriteGuard.isHistoricalStructure( - current, ModdedNativeTerrainReceipts.structureActivation(source)); - } - - private BoundingBox writableArea(ChunkAccess chunk) { - ChunkPos chunkPos = chunk.getPos(); - int minX = chunkPos.getMinBlockX(); - int minZ = chunkPos.getMinBlockZ(); - int minY = chunk.getMinY() + 1; - int maxY = chunk.getMinY() + chunk.getHeight() - 1; - return new BoundingBox(minX, minY, minZ, minX + 15, maxY, minZ + 15); - } - - private IntBinaryOperator worldgenSurfaceHeight(Engine generationEngine, int runtimeMinY) { - return (x, z) -> Engine.hostHeight(generationEngine, x, z, false) + runtimeMinY + 1; - } - - private IntBinaryOperator worldgenFloorHeight(Engine generationEngine, int runtimeMinY) { - return (x, z) -> Engine.hostHeight(generationEngine, x, z, true) + runtimeMinY + 1; - } - - private record NativeStructureStartKey(String structureId, long chunkPosition) { - } - - private record NativePlacement(StructureStart start, IrisNativeStructureDecision decision) { - } - - private record NativePlacementGroup(String structureId, int featureIndex, int step, - List placements) { - } - - private record IrisNativeLocateSearch(Holder holder, String structureId, - NativeStructureLocatePersistence.Search search) { - } - - private record StructureStepCache(Registry registry, List> structures) { - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccess.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccess.java deleted file mode 100644 index c575f4dff..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccess.java +++ /dev/null @@ -1,567 +0,0 @@ -package art.arcane.iris.modded; - -import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.QuartPos; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.particles.ParticleOptions; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.sounds.SoundEvent; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Mth; -import net.minecraft.util.RandomSource; -import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.attribute.EnvironmentAttributeReader; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.flag.FeatureFlagSet; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.WorldGenLevel; -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.BlockEntityType; -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.EmptyLevelChunk; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.entity.EntityTypeTest; -import net.minecraft.world.level.gameevent.GameEvent; -import net.minecraft.world.level.levelgen.Heightmap; -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.storage.LevelData; -import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.ticks.LevelTickAccess; -import net.minecraft.world.ticks.ScheduledTick; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.function.IntBinaryOperator; -import java.util.function.Predicate; -import java.util.function.Supplier; - -final class ModdedNativeStructureWorldgenAccess implements WorldGenLevel { - private static final int WRITE_RADIUS = 1; - - private final WorldGenLevel delegate; - private final ChunkPos generationCenter; - private final IntBinaryOperator surfaceFirstFreeY; - private final IntBinaryOperator floorFirstFreeY; - private final boolean virtualizeHeights; - private final Predicate protectedPosition; - private final Holder fallbackBiome; - private final BiomeManager biomeManager; - private final AABB generationBounds; - private final LevelTickAccess blockTicks; - private final LevelTickAccess fluidTicks; - private final Long2LongOpenHashMap terrainHeights; - private final Long2ObjectOpenHashMap outsideChunks; - - private ModdedNativeStructureWorldgenAccess(WorldGenLevel delegate, Boundary boundary) { - this.delegate = Objects.requireNonNull(delegate, "Native structure world access requires a delegate"); - this.generationCenter = Objects.requireNonNull( - boundary.generationCenter(), "Native structure world access requires a generation center"); - this.surfaceFirstFreeY = Objects.requireNonNull( - boundary.surfaceFirstFreeY(), "Native structure world access requires a surface resolver"); - this.floorFirstFreeY = Objects.requireNonNull( - boundary.floorFirstFreeY(), "Native structure world access requires an ocean-floor resolver"); - this.virtualizeHeights = boundary.virtualizeHeights(); - this.protectedPosition = Objects.requireNonNull( - boundary.protectedPosition(), "World access requires a protected-position predicate"); - BlockPos biomeSample = generationCenter.getMiddleBlockPosition(delegate.getSeaLevel()); - this.fallbackBiome = delegate.getBiome(biomeSample); - this.biomeManager = delegate.getBiomeManager().withDifferentSource(this); - this.generationBounds = new AABB( - generationCenter.getMinBlockX() - 16, - delegate.getMinY(), - generationCenter.getMinBlockZ() - 16, - generationCenter.getMaxBlockX() + 17, - delegate.getMinY() + delegate.getHeight(), - generationCenter.getMaxBlockZ() + 17); - this.blockTicks = new BoundedTickAccess<>(delegate.getBlockTicks(), this::isWritable); - this.fluidTicks = new BoundedTickAccess<>(delegate.getFluidTicks(), this::isWritable); - this.terrainHeights = new Long2LongOpenHashMap(); - this.terrainHeights.defaultReturnValue(Long.MIN_VALUE); - this.outsideChunks = new Long2ObjectOpenHashMap<>(); - } - - static ModdedNativeStructureWorldgenAccess create(WorldGenLevel delegate, ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - return new ModdedNativeStructureWorldgenAccess(delegate, new Boundary( - generationCenter, surfaceFirstFreeY, floorFirstFreeY, virtualizeHeights, protectedPosition)); - } - - @Override - public long getSeed() { - return delegate.getSeed(); - } - - @Override - public boolean ensureCanWrite(BlockPos position) { - return isWritable(position) && delegate.ensureCanWrite(position); - } - - @Override - public void setCurrentlyGenerating(Supplier description) { - delegate.setCurrentlyGenerating(description); - } - - @Override - public ServerLevel getLevel() { - return delegate.getLevel(); - } - - @Override - public DifficultyInstance getCurrentDifficultyAt(BlockPos position) { - if (isReadable(position)) { - return delegate.getCurrentDifficultyAt(position); - } - return delegate.getCurrentDifficultyAt(generationCenter.getMiddleBlockPosition( - Mth.clamp(position.getY(), getMinY(), getMaxY() - 1))); - } - - @Override - public long nextSubTickCount() { - return delegate.nextSubTickCount(); - } - - @Override - public LevelTickAccess getBlockTicks() { - return blockTicks; - } - - @Override - public LevelTickAccess getFluidTicks() { - return fluidTicks; - } - - @Override - public LevelData getLevelData() { - return delegate.getLevelData(); - } - - @Override - public MinecraftServer getServer() { - return delegate.getServer(); - } - - @Override - public ChunkSource getChunkSource() { - return delegate.getChunkSource(); - } - - @Override - public RandomSource getRandom() { - return delegate.getRandom(); - } - - @Override - public void updateNeighborsAt(BlockPos position, Block block) { - if (isWritableNeighbourhood(position)) { - delegate.updateNeighborsAt(position, block); - } - } - - @Override - public void neighborShapeChanged(Direction direction, BlockPos position, - BlockPos neighbourPosition, BlockState neighbourState, - int updateFlags, int updateLimit) { - if (isWritable(position) && isWritable(neighbourPosition)) { - delegate.neighborShapeChanged( - direction, position, neighbourPosition, neighbourState, updateFlags, updateLimit); - } - } - - @Override - public void playSound(Entity source, BlockPos position, SoundEvent sound, - SoundSource soundSource, float volume, float pitch) { - if (isWritable(position)) { - delegate.playSound(source, position, sound, soundSource, volume, pitch); - } - } - - @Override - public void addParticle(ParticleOptions particle, double x, double y, double z, - double velocityX, double velocityY, double velocityZ) { - if (isWritable(BlockPos.containing(x, y, z))) { - delegate.addParticle(particle, x, y, z, velocityX, velocityY, velocityZ); - } - } - - @Override - public void levelEvent(Entity source, int eventId, BlockPos position, int data) { - if (isWritable(position)) { - delegate.levelEvent(source, eventId, position, data); - } - } - - @Override - public void gameEvent(Holder event, Vec3 position, GameEvent.Context context) { - if (isWritable(BlockPos.containing(position))) { - delegate.gameEvent(event, position, context); - } - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public ChunkAccess getChunk(int chunkX, int chunkZ, ChunkStatus status, boolean create) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunk(chunkX, chunkZ, status, create); - } - return create ? outsideChunk(chunkX, chunkZ) : null; - } - - @Override - public boolean hasChunk(int chunkX, int chunkZ) { - return isInsideGenerationRegion(chunkX, chunkZ) && delegate.hasChunk(chunkX, chunkZ); - } - - @Override - public int getHeight(Heightmap.Types type, int x, int z) { - if (!virtualizeHeights && isInsideGenerationRegion(x >> 4, z >> 4)) { - return delegate.getHeight(type, x, z); - } - return height(type, x, z); - } - - @Override - public int getSkyDarken() { - return delegate.getSkyDarken(); - } - - @Override - public BiomeManager getBiomeManager() { - return biomeManager; - } - - @Override - public Holder getNoiseBiome(int quartX, int quartY, int quartZ) { - return getUncachedNoiseBiome(quartX, quartY, quartZ); - } - - @Override - public Holder getUncachedNoiseBiome(int quartX, int quartY, int quartZ) { - if (isInsideGenerationRegion(QuartPos.toSection(quartX), QuartPos.toSection(quartZ))) { - return delegate.getUncachedNoiseBiome(quartX, quartY, quartZ); - } - return fallbackBiome; - } - - @Override - public boolean isClientSide() { - return delegate.isClientSide(); - } - - @Override - public int getSeaLevel() { - return delegate.getSeaLevel(); - } - - @Override - public DimensionType dimensionType() { - return delegate.dimensionType(); - } - - @Override - public int getMinY() { - return delegate.getMinY(); - } - - @Override - public int getHeight() { - return delegate.getHeight(); - } - - @Override - public RegistryAccess registryAccess() { - return delegate.registryAccess(); - } - - @Override - public FeatureFlagSet enabledFeatures() { - return delegate.enabledFeatures(); - } - - @Override - public EnvironmentAttributeReader environmentAttributes() { - return delegate.environmentAttributes(); - } - - @Override - public LevelLightEngine getLightEngine() { - return delegate.getLightEngine(); - } - - @Override - public int getBrightness(LightLayer layer, BlockPos position) { - if (isReadable(position)) { - return delegate.getBrightness(layer, position); - } - return layer == LightLayer.SKY && canSeeSky(position) ? 15 : 0; - } - - @Override - public int getRawBrightness(BlockPos position, int ambientDarkening) { - if (isReadable(position)) { - return delegate.getRawBrightness(position, ambientDarkening); - } - return Math.max(0, getBrightness(LightLayer.SKY, position) - ambientDarkening); - } - - @Override - public boolean canSeeSky(BlockPos position) { - if (isReadable(position)) { - return delegate.canSeeSky(position); - } - return position.getY() >= height(Heightmap.Types.WORLD_SURFACE_WG, position.getX(), position.getZ()); - } - - @Override - public WorldBorder getWorldBorder() { - return delegate.getWorldBorder(); - } - - @Override - public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) { - if (isInsideGenerationRegion(chunkX, chunkZ)) { - return delegate.getChunkForCollisions(chunkX, chunkZ); - } - return outsideChunk(chunkX, chunkZ); - } - - @Override - public BlockEntity getBlockEntity(BlockPos position) { - return isWritable(position) ? delegate.getBlockEntity(position) : null; - } - - @Override - public Optional getBlockEntity( - BlockPos position, BlockEntityType type) { - return isWritable(position) ? delegate.getBlockEntity(position, type) : Optional.empty(); - } - - @Override - public BlockState getBlockState(BlockPos position) { - return isReadable(position) ? delegate.getBlockState(position) : terrainState(position); - } - - @Override - public FluidState getFluidState(BlockPos position) { - return isReadable(position) ? delegate.getFluidState(position) : terrainState(position).getFluidState(); - } - - @Override - public List getEntities(Entity source, AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(source, boundedArea, predicate); - } - - @Override - public List getEntities( - EntityTypeTest type, - AABB area, Predicate predicate) { - AABB boundedArea = boundedArea(area); - return boundedArea == null ? List.of() : delegate.getEntities(type, boundedArea, predicate); - } - - @Override - public List players() { - return delegate.players(); - } - - @Override - public boolean isStateAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getBlockState(position)); - } - - @Override - public boolean isFluidAtPosition(BlockPos position, Predicate predicate) { - return predicate.test(getFluidState(position)); - } - - @Override - public BlockPos getHeightmapPos(Heightmap.Types type, BlockPos position) { - return position.atY(getHeight(type, position.getX(), position.getZ())); - } - - @Override - public boolean setBlock(BlockPos position, BlockState state, int updateFlags, int updateLimit) { - return isWritable(position) && delegate.setBlock(position, state, updateFlags, updateLimit); - } - - @Override - public boolean removeBlock(BlockPos position, boolean move) { - return isWritable(position) && delegate.removeBlock(position, move); - } - - @Override - public boolean destroyBlock(BlockPos position, boolean drop, Entity source, int updateLimit) { - return isWritable(position) && delegate.destroyBlock(position, drop, source, updateLimit); - } - - @Override - public boolean addFreshEntity(Entity entity) { - return isWritable(entity.blockPosition()) && delegate.addFreshEntity(entity); - } - - - private boolean isReadable(BlockPos position) { - return isInsideGenerationRegion(position.getX() >> 4, position.getZ() >> 4) - && !isOutsideBuildHeight(position); - } - - private boolean isWritable(BlockPos position) { - return isReadable(position) && !protectedPosition.test(position); - } - - private boolean isWritableNeighbourhood(BlockPos position) { - return isWritable(position) - && isWritable(position.north()) - && isWritable(position.south()) - && isWritable(position.east()) - && isWritable(position.west()) - && isWritable(position.above()) - && isWritable(position.below()); - } - - private AABB boundedArea(AABB area) { - double minX = Math.max(area.minX, generationBounds.minX); - double minY = Math.max(area.minY, generationBounds.minY); - double minZ = Math.max(area.minZ, generationBounds.minZ); - double maxX = Math.min(area.maxX, generationBounds.maxX); - double maxY = Math.min(area.maxY, generationBounds.maxY); - double maxZ = Math.min(area.maxZ, generationBounds.maxZ); - if (minX >= maxX || minY >= maxY || minZ >= maxZ) { - return null; - } - if (minX == area.minX && minY == area.minY && minZ == area.minZ - && maxX == area.maxX && maxY == area.maxY && maxZ == area.maxZ) { - return area; - } - return new AABB(minX, minY, minZ, maxX, maxY, maxZ); - } - - boolean isInsideGenerationRegion(int chunkX, int chunkZ) { - return Math.abs(chunkX - generationCenter.x()) <= WRITE_RADIUS - && Math.abs(chunkZ - generationCenter.z()) <= WRITE_RADIUS; - } - - @Override - public boolean isOutsideBuildHeight(BlockPos position) { - return position.getY() < getMinY() || position.getY() >= getMaxY(); - } - - private BlockState terrainState(BlockPos position) { - if (isOutsideBuildHeight(position)) { - return Blocks.VOID_AIR.defaultBlockState(); - } - long heights = heights(position.getX(), position.getZ()); - int surface = (int) (heights >> 32); - int floor = (int) heights; - if (position.getY() < floor) { - return Blocks.STONE.defaultBlockState(); - } - if (position.getY() < surface) { - return Blocks.WATER.defaultBlockState(); - } - return Blocks.AIR.defaultBlockState(); - } - - private int height(Heightmap.Types type, int x, int z) { - long heights = heights(x, z); - if (type == Heightmap.Types.OCEAN_FLOOR || type == Heightmap.Types.OCEAN_FLOOR_WG) { - return (int) heights; - } - return (int) (heights >> 32); - } - - private long heights(int x, int z) { - long key = ((long) x << 32) ^ (z & 0xffffffffL); - long cached = terrainHeights.get(key); - if (cached != Long.MIN_VALUE) { - return cached; - } - int floor = Mth.clamp(floorFirstFreeY.applyAsInt(x, z), getMinY(), getMaxY()); - int surface = Mth.clamp(surfaceFirstFreeY.applyAsInt(x, z), floor, getMaxY()); - long heights = ((long) surface << 32) | (floor & 0xffffffffL); - terrainHeights.put(key, heights); - return heights; - } - - private ChunkAccess outsideChunk(int chunkX, int chunkZ) { - long key = ChunkPos.pack(chunkX, chunkZ); - ChunkAccess cached = outsideChunks.get(key); - if (cached != null) { - return cached; - } - EmptyLevelChunk chunk = new EmptyLevelChunk(getLevel(), new ChunkPos(chunkX, chunkZ), fallbackBiome); - WorldgenTerrainHeightmaps.primeTerrain(chunk, surfaceFirstFreeY, floorFirstFreeY); - outsideChunks.put(key, chunk); - return chunk; - } - - private record Boundary(ChunkPos generationCenter, - IntBinaryOperator surfaceFirstFreeY, - IntBinaryOperator floorFirstFreeY, - boolean virtualizeHeights, - Predicate protectedPosition) { - } - - private static final class BoundedTickAccess implements LevelTickAccess { - private final LevelTickAccess delegate; - private final Predicate writable; - - private BoundedTickAccess(LevelTickAccess delegate, Predicate writable) { - this.delegate = delegate; - this.writable = writable; - } - - @Override - public void schedule(ScheduledTick tick) { - if (writable.test(tick.pos())) { - delegate.schedule(tick); - } - } - - @Override - public boolean hasScheduledTick(BlockPos position, T type) { - return writable.test(position) && delegate.hasScheduledTick(position, type); - } - - @Override - public int count() { - return delegate.count(); - } - - @Override - public boolean willTickThisTick(BlockPos position, T type) { - return writable.test(position) && delegate.willTickThisTick(position, type); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeTerrainReceipts.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeTerrainReceipts.java index 01bd31b01..bd24fcabf 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeTerrainReceipts.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedNativeTerrainReceipts.java @@ -2,8 +2,6 @@ package art.arcane.iris.modded; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; import art.arcane.iris.world.history.NativeTerrainReceipt; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ImposterProtoChunk; import java.io.IOException; @@ -11,43 +9,9 @@ public final class ModdedNativeTerrainReceipts { private ModdedNativeTerrainReceipts() { } - public static void persist(ChunkAccess chunk, GenerationHistoryRuntimeRouter.RuntimeRoute route) throws IOException { - if (route == null || route.naturalTerrain().isEmpty()) { - return; - } - set(chunk, NativeTerrainReceipt.encode(route.naturalTerrain().orElseThrow(), - route.activation().activationId(), route.epoch().epochId())); - chunk.markUnsaved(); - } - - public static long structureActivation(ChunkAccess chunk) { - return holder(chunk).iris$getStructureActivation(); - } - - public static void setStructureActivation(ChunkAccess chunk, long activation) { - holder(chunk).iris$setStructureActivation(activation); - } - - public static void persistStructureActivation(ChunkAccess chunk, GenerationHistoryRuntimeRouter.RuntimeRoute route) { - if (route != null) { - setStructureActivation(chunk, route.activation().activationId()); - chunk.markUnsaved(); - } - } - - public static byte[] get(ChunkAccess chunk) { - return holder(chunk).iris$getNaturalTerrain(); - } - - public static void set(ChunkAccess chunk, byte[] receipt) { - holder(chunk).iris$setNaturalTerrain(receipt); - } - - private static NativeTerrainReceiptHolder holder(ChunkAccess chunk) { - ChunkAccess target = chunk instanceof ImposterProtoChunk imposter ? imposter.getWrapped() : chunk; - if (!(target instanceof NativeTerrainReceiptHolder holder)) { - throw new IllegalStateException("Native terrain receipt storage is unavailable for " + chunk.getPos()); - } - return holder; + public static byte[] encode(GenerationHistoryRuntimeRouter.RuntimeRoute route) throws IOException { + return route == null || route.naturalTerrain().isEmpty() ? null + : NativeTerrainReceipt.encode(route.naturalTerrain().orElseThrow(), + route.activation().activationId(), route.epoch().epochId()); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedParityProbe.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedParityProbe.java index 08b971eed..9399ecfd0 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedParityProbe.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedParityProbe.java @@ -25,10 +25,10 @@ import art.arcane.iris.generation.runtime.EngineTarget; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.world.IrisWorld; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.io.File; import java.nio.charset.StandardCharsets; @@ -63,9 +63,9 @@ public final class ModdedParityProbe { private static void waitAndRun(String config) { long start = System.currentTimeMillis(); - MinecraftServer server = null; + NativeModdedServer server = null; while (System.currentTimeMillis() - start < 600000L) { - MinecraftServer candidate = ModdedEngineBootstrap.currentServer(); + NativeModdedServer candidate = ModdedEngineBootstrap.currentServer(); if (candidate != null && candidate.isReady()) { server = candidate; break; @@ -94,7 +94,7 @@ public final class ModdedParityProbe { server.halt(false); } - private static boolean run(MinecraftServer server, String config) throws Exception { + private static boolean run(NativeModdedServer server, String config) throws Exception { String packPath = config; int radius = 8; int lastColon = config.lastIndexOf(':'); @@ -173,7 +173,7 @@ public final class ModdedParityProbe { ModdedIrisLog.info("[parity] golden: {} ({} chunks, combined={})", goldenPath, goldenChunks.size(), goldenCombined); } - PlatformBlockState airState = IrisPlatforms.get().registries().air(); + NativeBlockState airState = IrisPlatforms.get().registries().air(); List targets = orderedTargets(0, 0, radius); Map lines = new TreeMap<>(); List mismatches = new ArrayList<>(); @@ -184,7 +184,7 @@ public final class ModdedParityProbe { int cz = at[1]; drainReported(); ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, airState); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); List failures = new ArrayList<>(); try { engine.generate(cx << 4, cz << 4, blocks, biomes, false); @@ -291,18 +291,18 @@ public final class ModdedParityProbe { return targets; } - private static String hashChunk(int chunkX, int chunkZ, ModdedBlockBuffer blocks, Hunk biomes, int height) { + private static String hashChunk(int chunkX, int chunkZ, ModdedBlockBuffer blocks, Hunk biomes, int height) { MessageDigest blockDigest = sha256(); MessageDigest biomeDigest = sha256(); - Map blockCache = new HashMap<>(); - Map biomeCache = new HashMap<>(); + Map blockCache = new HashMap<>(); + Map biomeCache = new HashMap<>(); byte[] plains = "minecraft:plains\n".getBytes(StandardCharsets.UTF_8); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < height; y++) { - PlatformBlockState state = blocks.get(x, y, z); - byte[] bytes = blockCache.computeIfAbsent(state, (PlatformBlockState s) -> (s.key() + "\n").getBytes(StandardCharsets.UTF_8)); + NativeBlockState state = blocks.get(x, y, z); + byte[] bytes = blockCache.computeIfAbsent(state, (NativeBlockState s) -> (s.key() + "\n").getBytes(StandardCharsets.UTF_8)); blockDigest.update(bytes); } } @@ -311,10 +311,10 @@ public final class ModdedParityProbe { for (int x = 0; x < 16; x += BIOME_STEP) { for (int z = 0; z < 16; z += BIOME_STEP) { for (int y = 0; y < height; y += BIOME_STEP) { - PlatformBiome biome = biomes.get(x, y, z); + NativeBiome biome = biomes.get(x, y, z); byte[] bytes = biome == null ? plains - : biomeCache.computeIfAbsent(biome, (PlatformBiome b) -> (b.key() + "\n").getBytes(StandardCharsets.UTF_8)); + : biomeCache.computeIfAbsent(biome, (NativeBiome b) -> (b.key() + "\n").getBytes(StandardCharsets.UTF_8)); biomeDigest.update(bytes); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatform.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatform.java index e546898b6..0874bc97f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatform.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatform.java @@ -18,21 +18,25 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeRegistryAccess; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeRegistryDefinitions; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntitySpawns; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBiomeRegistry; import art.arcane.iris.BuildConstants; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandExecutor; +import art.arcane.iris.spi.PlatformGenerationRegistry; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.LogLevel; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformEntityType; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.EntitySpawnReason; -import net.minecraft.world.entity.EntityType; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.io.File; import java.util.Map; @@ -48,28 +52,26 @@ public final class ModdedPlatform implements IrisPlatform { private static volatile Consumer ERROR_SINK = null; - private final ModdedLoader loader; + private final NativeModdedLoader loader; private final ModdedRegistries registries; private final ModdedScheduler scheduler; private final ModdedStructureHooks structureHooks; private final ModdedBiomeWriter biomeWriter; - public ModdedPlatform(ModdedLoader loader) { + public ModdedPlatform(NativeModdedLoader loader) { this.loader = loader; - this.registries = new ModdedRegistries(ModdedEngineBootstrap::currentServer); + this.registries = new ModdedRegistries( + new NativeRegistryAccess(ModdedEngineBootstrap::currentServer, ModdedRegistries::warnNotReady), + ModdedPlatform::generationRegistry); this.scheduler = new ModdedScheduler(); this.structureHooks = new ModdedStructureHooks(ModdedEngineBootstrap::currentServer); - this.biomeWriter = new ModdedBiomeWriter(ModdedEngineBootstrap::currentServer); + this.biomeWriter = new ModdedBiomeWriter(new NativeBiomeRegistry(ModdedEngineBootstrap::currentServer, "minecraft:plains")); } public static void errorSink(Consumer sink) { ERROR_SINK = sink; } - public MinecraftServer server() { - return ModdedEngineBootstrap.currentServer(); - } - public ModdedScheduler moddedScheduler() { return scheduler; } @@ -196,22 +198,19 @@ public final class ModdedPlatform implements IrisPlatform { @Override public void dispatchConsoleCommand(String command) { - ModdedServerCommands.dispatch(ModdedEngineBootstrap.currentServer(), command); + NativeCommandExecutor.dispatch(ModdedEngineBootstrap.currentServer(), command, IrisLogging::reportError); } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { - if (world == null || entityKey == null || !(world.nativeHandle() instanceof ServerLevel level)) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { + if (world == null || entityKey == null) { return false; } - PlatformEntityType resolved = registries.entity(entityKey); + NativeEntityType resolved = registries.entity(entityKey); if (resolved == null) { return false; } - EntityType type = (EntityType) resolved.nativeHandle(); - BlockPos pos = BlockPos.containing(x, y, z); - Entity entity = ModdedEntitySpawner.spawnNative(type, level, pos, EntitySpawnReason.COMMAND); - return entity != null; + return NativeEntitySpawns.command(world, resolved, x, y, z); } @Override @@ -353,4 +352,14 @@ public final class ModdedPlatform implements IrisPlatform { return -1; } } + + private static PlatformGenerationRegistry generationRegistry() { + if (ModdedEngineBootstrap.currentServer() == null) { + throw new IllegalStateException("Minecraft server is not ready for generation registry capture."); + } + return new ModdedGenerationRegistry(NativeModdedServer.registryDefinitions(ModdedEngineBootstrap::currentServer), + new ModdedGenerationRegistry.Identity("modded-generation-registry-v1", + "modded-generated-registry-json-v1|minecraft:" + BuildConstants.MINECRAFT_VERSION)); + } + } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatformWorld.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatformWorld.java deleted file mode 100644 index 4c7ef31c3..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPlatformWorld.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformWorld; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.state.BlockState; - -public final class ModdedPlatformWorld implements PlatformWorld { - private final ServerLevel level; - - public ModdedPlatformWorld(ServerLevel level) { - this.level = level; - } - - @Override - public String name() { - return level.dimension().identifier().toString(); - } - - @Override - public long seed() { - return level.getSeed(); - } - - @Override - public int minHeight() { - return level.getMinY(); - } - - @Override - public int maxHeight() { - return exclusiveMaxHeight(level.getMinY(), level.getHeight()); - } - - @Override - public PlatformBlockState getBlock(int x, int y, int z) { - return ModdedBlockState.of(level.getBlockState(new BlockPos(x, y, z)), null); - } - - @Override - public void setBlock(int x, int y, int z, PlatformBlockState block, int flags) { - level.setBlock(new BlockPos(x, y, z), (BlockState) block.nativeHandle(), flags); - } - - @Override - public PlatformBiome getBiome(int x, int y, int z) { - Holder biome = level.getBiome(new BlockPos(x, y, z)); - String key = biome.unwrapKey() - .map(resourceKey -> resourceKey.identifier().toString()) - .orElse("minecraft:plains"); - return ModdedBiome.of(biome.value(), key); - } - - @Override - public boolean isChunkLoaded(int chunkX, int chunkZ) { - return level.getChunkSource().getChunkNow(chunkX, chunkZ) != null; - } - - @Override - public long getTime() { - return level.getDefaultClockTime(); - } - - @Override - public boolean isStorming() { - return level.isRaining(); - } - - @Override - public boolean isThundering() { - return level.isThundering(); - } - - @Override - public Object nativeHandle() { - return level; - } - - ServerLevel level() { - return level; - } - - static int exclusiveMaxHeight(int minHeight, int height) { - return minHeight + height; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPrimaryWorldRouter.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPrimaryWorldRouter.java index 057489797..0f0ab2b27 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPrimaryWorldRouter.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedPrimaryWorldRouter.java @@ -18,14 +18,15 @@ package art.arcane.iris.modded; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldTeleport; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDimensionRuntime; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -55,7 +56,7 @@ public final class ModdedPrimaryWorldRouter { } } - public static void tick(MinecraftServer server) { + public static void tick(NativeModdedServer server) { if (server == null) { return; } @@ -74,34 +75,29 @@ public final class ModdedPrimaryWorldRouter { return; } - ServerLevel target = ModdedDimensionManager.level(server, primary); + NativeWorld target = ModdedDimensionManager.level(server, primary); if (target == null) { return; } - ServerLevel overworld = server.overworld(); - if (target == overworld) { + NativeWorld overworld = server.overworld(); + if (NativeDimensionRuntime.sameWorld(target, overworld)) { return; } - List players = new ArrayList<>(server.getPlayerList().getPlayers()); - for (ServerPlayer player : players) { - UUID id = player.getUUID(); + server.forEachPlayer(player -> { + UUID id = player.id(); if (routed.contains(id) || !inFlight.add(id)) { - continue; + return; } - if (player.level() != overworld) { + if (!player.isInWorld(overworld)) { inFlight.remove(id); routed.add(id); - continue; + return; } try { - CompletableFuture teleport = ModdedDimensionManager.teleportAsync( - player, - server, - primary, - player.getX(), - Double.MIN_VALUE, - player.getZ()); + CompletableFuture teleport = NativeWorldTeleport.teleport(player, + new NativeWorldTeleport.Destination(server, target, player.x(), Double.MIN_VALUE, player.z(), + System.nanoTime() + TimeUnit.SECONDS.toNanos(10))); teleport.whenComplete((success, failure) -> { inFlight.remove(id); if (Boolean.TRUE.equals(success) && failure == null) { @@ -117,6 +113,6 @@ public final class ModdedPrimaryWorldRouter { inFlight.remove(id); ModdedIrisLog.error("Iris failed to route player {} to primary world '{}'", id, primary, e); } - } + }); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolChannel.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolChannel.java deleted file mode 100644 index f21db9250..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolChannel.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import net.minecraft.server.level.ServerPlayer; - -public interface ModdedProtocolChannel { - boolean canReceive(ServerPlayer player); - - void send(ServerPlayer player, ModdedIrisPayload payload); -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolDefinition.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolDefinition.java new file mode 100644 index 000000000..9e1877a9c --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolDefinition.java @@ -0,0 +1,11 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.spi.protocol.IrisProtocol; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativePayloadProtocol; + +public final class ModdedProtocolDefinition { + public static final NativePayloadProtocol PROTOCOL = new NativePayloadProtocol(IrisProtocol.CHANNEL, IrisProtocol.MAX_FRAME_BYTES); + + private ModdedProtocolDefinition() { + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolHandler.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolHandler.java index 4e8e03bad..06e425ab2 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolHandler.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolHandler.java @@ -18,6 +18,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolChannel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolWorld; import art.arcane.iris.platform.protocol.EngineResolver; import art.arcane.iris.platform.protocol.IrisCursorRequestService; import art.arcane.iris.platform.protocol.IrisProtocolServer; @@ -27,10 +31,6 @@ import art.arcane.iris.platform.protocol.IrisVisionRequestService; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.spi.protocol.IrisProtocol; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.chunk.ChunkGenerator; import java.util.Objects; import java.util.UUID; @@ -47,7 +47,8 @@ public final class ModdedProtocolHandler { private static final ConcurrentHashMap SESSION_LEVELS = new ConcurrentHashMap<>(); private static final ConcurrentHashMap SESSION_IDS = new ConcurrentHashMap<>(); - private static volatile ModdedProtocolChannel channel; + private static volatile NativeProtocolChannel channel; + private static NativeProtocolServer serverContext; private static volatile IrisSessionRegistry registry; private static volatile IrisProtocolServer protocolServer; private static volatile ModdedProtocolTransport transport; @@ -58,12 +59,12 @@ public final class ModdedProtocolHandler { private ModdedProtocolHandler() { } - public static void bindChannel(ModdedProtocolChannel boundChannel) { + public static void bindChannel(NativeProtocolChannel boundChannel) { channel = Objects.requireNonNull(boundChannel, "protocol channel"); } - public static void start(MinecraftServer server) { - ModdedProtocolChannel boundChannel = channel; + public static void start(NativeProtocolServer server) { + NativeProtocolChannel boundChannel = channel; if (server == null || boundChannel == null) { return; } @@ -72,7 +73,7 @@ public final class ModdedProtocolHandler { SESSION_IDS.clear(); dimensionSyncTicks = 0; IrisSessionRegistry sessionRegistry = new IrisSessionRegistry(); - ModdedProtocolTransport serverTransport = new ModdedProtocolTransport(server, boundChannel); + ModdedProtocolTransport serverTransport = new ModdedProtocolTransport(server.delivery(boundChannel)); IrisProtocolServer protocol = new IrisProtocolServer(sessionRegistry, SERVER_CAPABILITIES, brand(), true); EngineResolver engineResolver = (String sessionId) -> { Engine engine = SESSION_ENGINES.get(sessionId); @@ -89,12 +90,8 @@ public final class ModdedProtocolHandler { cursorRequests = cursorService; visionRequests = visionService; IrisServices.register(IrisProtocolServer.class, protocol); - if (server.getPlayerList() == null) { - return; - } - for (ServerPlayer player : server.getPlayerList().getPlayers()) { - sessionRegistry.register(new IrisSession(sessionId(player), serverTransport)); - } + serverContext = server; + server.forEachPlayer(player -> sessionRegistry.register(new IrisSession(sessionId(player), serverTransport))); } public static void stop() { @@ -117,6 +114,7 @@ public final class ModdedProtocolHandler { SESSION_LEVELS.clear(); SESSION_IDS.clear(); dimensionSyncTicks = 0; + serverContext = null; registry = null; protocolServer = null; transport = null; @@ -124,7 +122,7 @@ public final class ModdedProtocolHandler { visionRequests = null; } - public static void onPlayerJoin(ServerPlayer player) { + public static void onPlayerJoin(NativeProtocolPlayer player) { if (player == null) { return; } @@ -138,16 +136,20 @@ public final class ModdedProtocolHandler { current.register(new IrisSession(sessionId, currentTransport)); } - public static void onPlayerDisconnect(ServerPlayer player) { + public static void onPlayerDisconnect(NativeProtocolPlayer player) { if (player == null) { return; } - UUID id = player.getUUID(); + UUID id = player.id(); String sessionId = SESSION_IDS.remove(id); if (sessionId == null) { sessionId = id.toString(); } ModdedPrimaryWorldRouter.forget(id); + NativeProtocolServer server = serverContext; + if (server != null) { + server.forget(id); + } SESSION_ENGINES.remove(sessionId); SESSION_LEVELS.remove(sessionId); IrisSessionRegistry current = registry; @@ -168,11 +170,11 @@ public final class ModdedProtocolHandler { * UUID.toString allocates a 36-char string every call; the dimension sync tick runs over every player four * times a second, so the id is interned per player on first use and dropped on disconnect. */ - private static String sessionId(ServerPlayer player) { - return SESSION_IDS.computeIfAbsent(player.getUUID(), UUID::toString); + private static String sessionId(NativeProtocolPlayer player) { + return SESSION_IDS.computeIfAbsent(player.id(), UUID::toString); } - public static void onInbound(ServerPlayer player, byte[] frame) { + public static void onInbound(NativeProtocolPlayer player, byte[] frame) { IrisProtocolServer current = protocolServer; if (player == null || frame == null || current == null) { return; @@ -186,7 +188,8 @@ public final class ModdedProtocolHandler { scheduler.global(() -> current.onClientFrame(sessionId, frame)); } - public static void tickDimensionSync(MinecraftServer server) { + public static void tickDimensionSync() { + NativeProtocolServer server = serverContext; IrisSessionRegistry current = registry; IrisProtocolServer protocol = protocolServer; if (server == null || current == null || protocol == null) { @@ -197,33 +200,35 @@ public final class ModdedProtocolHandler { return; } dimensionSyncTicks = 0; - for (ServerPlayer player : server.getPlayerList().getPlayers()) { - String sessionId = sessionId(player); - IrisSession session = current.get(sessionId); - if (session == null || !session.isReady()) { - continue; - } - ServerLevel level = player.level(); - String levelId = level.dimension().identifier().toString(); - Engine cached = SESSION_ENGINES.get(sessionId); - if (levelId.equals(SESSION_LEVELS.get(sessionId)) && (cached == null || !cached.isClosed())) { - continue; - } - if (syncDimension(protocol, sessionId, level, levelId)) { - SESSION_LEVELS.put(sessionId, levelId); - } + server.forEachPlayer(player -> syncPlayerDimension(current, protocol, player)); + } + + private static void syncPlayerDimension(IrisSessionRegistry current, IrisProtocolServer protocol, NativeProtocolPlayer player) { + String sessionId = sessionId(player); + IrisSession session = current.get(sessionId); + if (session == null || !session.isReady()) { + return; + } + NativeProtocolWorld level = player.world(); + String levelId = level.id(); + Engine cached = SESSION_ENGINES.get(sessionId); + if (levelId.equals(SESSION_LEVELS.get(sessionId)) && (cached == null || !cached.isClosed())) { + return; + } + if (syncDimension(protocol, sessionId, level, levelId)) { + SESSION_LEVELS.put(sessionId, levelId); } } - private static boolean syncDimension(IrisProtocolServer protocol, String sessionId, ServerLevel level, String levelId) { - ChunkGenerator generator = level.getChunkSource().getGenerator(); + private static boolean syncDimension(IrisProtocolServer protocol, String sessionId, NativeProtocolWorld level, String levelId) { + IrisModdedChunkGenerator generator = level.generator(IrisModdedChunkGenerator.class); // engineIfBound only, never commandEngine: the sync tick runs on the server thread and constructing an // engine there stalls the tick for the whole pack load. An unbound generator simply retries next tick. - Engine engine = generator instanceof IrisModdedChunkGenerator irisGenerator ? resolveEngine(level, irisGenerator) : null; - if (generator instanceof IrisModdedChunkGenerator && engine == null) { + Engine engine = generator != null ? resolveEngine(level, generator) : null; + if (generator != null && engine == null) { return false; } - long seed = level.getSeed(); + long seed = level.seed(); if (engine != null) { SESSION_ENGINES.put(sessionId, engine); protocol.sendDimensionStatus(sessionId, engine.getDimension().getLoadKey(), engine.getData().getDataFolder().getName(), @@ -231,16 +236,16 @@ public final class ModdedProtocolHandler { return true; } SESSION_ENGINES.remove(sessionId); - protocol.sendDimensionStatus(sessionId, levelId, "", seed, level.getMinY(), level.getMinY() + level.getHeight(), false); + protocol.sendDimensionStatus(sessionId, levelId, "", seed, level.minHeight(), level.maxHeight(), false); return true; } - private static Engine resolveEngine(ServerLevel level, IrisModdedChunkGenerator generator) { + private static Engine resolveEngine(NativeProtocolWorld level, IrisModdedChunkGenerator generator) { try { Engine engine = generator.engineIfBound(); return engine == null || engine.isClosed() ? null : engine; } catch (Throwable failure) { - ModdedIrisLog.error("Iris dimension status engine lookup failed for {}", level.dimension().identifier(), failure); + ModdedIrisLog.error("Iris dimension status engine lookup failed for {}", level.id(), failure); return null; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolTransport.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolTransport.java index da51279b4..929b3cea3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolTransport.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedProtocolTransport.java @@ -19,19 +19,16 @@ package art.arcane.iris.modded; import art.arcane.iris.platform.protocol.IrisServerTransport; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolDelivery; import java.util.Objects; import java.util.UUID; public final class ModdedProtocolTransport implements IrisServerTransport { - private final MinecraftServer server; - private final ModdedProtocolChannel channel; + private final NativeProtocolDelivery delivery; - public ModdedProtocolTransport(MinecraftServer server, ModdedProtocolChannel channel) { - this.server = Objects.requireNonNull(server, "server"); - this.channel = Objects.requireNonNull(channel, "protocol channel"); + public ModdedProtocolTransport(NativeProtocolDelivery delivery) { + this.delivery = Objects.requireNonNull(delivery, "delivery"); } @Override @@ -47,16 +44,7 @@ public final class ModdedProtocolTransport implements IrisServerTransport { if (scheduler == null) { return; } - ModdedIrisPayload payload = new ModdedIrisPayload(frame); - scheduler.global(() -> deliver(playerId, payload)); - } - - private void deliver(UUID playerId, ModdedIrisPayload payload) { - ServerPlayer player = server.getPlayerList().getPlayer(playerId); - if (player == null || !channel.canReceive(player)) { - return; - } - channel.send(player, payload); + scheduler.global(() -> delivery.send(playerId, frame)); } private static UUID parseUuid(String sessionId) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRegistries.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRegistries.java index 55b9107a3..d92927c16 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRegistries.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRegistries.java @@ -18,166 +18,118 @@ package art.arcane.iris.modded; -import art.arcane.iris.BuildConstants; import art.arcane.iris.modded.api.ModdedCustomContentRegistry; import art.arcane.iris.modded.api.ModdedDataType; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; import art.arcane.iris.spi.PlatformGenerationRegistry; -import art.arcane.iris.spi.PlatformItem; import art.arcane.iris.spi.PlatformRegistries; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeRegistryAccess; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.data.UnresolvedKeyLog; -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.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.enchantment.Enchantment; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.storage.loot.LootTable; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.function.Supplier; public final class ModdedRegistries implements PlatformRegistries { private static final UnresolvedKeyLog NOT_READY = new UnresolvedKeyLog("Iris modded registry reads before server ready", 60_000L); - private final Supplier server; + private final NativeRegistryAccess nativeAccess; + private final Supplier generationRegistry; - public ModdedRegistries(Supplier server) { - this.server = server; + public ModdedRegistries(NativeRegistryAccess nativeAccess, Supplier generationRegistry) { + this.nativeAccess = nativeAccess; + this.generationRegistry = generationRegistry; } @Override public PlatformGenerationRegistry generationRegistry() { - MinecraftServer instance = server.get(); - if (instance == null) { - throw new IllegalStateException("Minecraft server is not ready for generation registry capture."); - } - return new ModdedGenerationRegistry( - server, - "modded-generation-registry-v1", - "modded-generated-registry-json-v1|minecraft:" + BuildConstants.MINECRAFT_VERSION - ); + return generationRegistry.get(); } @Override - public PlatformBlockState block(String key) { - return ModdedBlockResolution.get(key); + public NativeBlockState block(String key) { + return ModdedBlockResolution.BLOCKS.get(key); } @Override - public PlatformBlockState blockOrNull(String key) { - return ModdedBlockResolution.getOrNull(key); + public NativeBlockState blockOrNull(String key) { + return ModdedBlockResolution.BLOCKS.getOrNull(key); } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { - return ModdedBlockResolution.getOrNull(key, warn); + public NativeBlockState blockOrNull(String key, boolean warn) { + return ModdedBlockResolution.BLOCKS.getOrNull(key, warn); } @Override - public PlatformBlockState air() { - return ModdedBlockResolution.getAir(); + public NativeBlockState air() { + return ModdedBlockResolution.BLOCKS.getAir(); } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { - BlockState result = ModdedBlockResolution.toDeepSlateOre((BlockState) block.nativeHandle(), (BlockState) ore.nativeHandle()); - return ModdedBlockState.of(result, null); + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { + return nativeAccess.deepSlateOre(block, ore); } @Override - public PlatformBiome biome(String key) { - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return null; - } - Registry registry = biomeRegistry(); - if (registry == null) { - return null; - } - Biome biome = registry.getValue(identifier); - return biome == null ? null : ModdedBiome.of(biome, identifier.toString()); + public NativeBiome biome(String key) { + return nativeAccess.biome(key); } @Override - public PlatformItem item(String key) { - String normalized = key.trim().toLowerCase(Locale.ROOT).replace(' ', '_'); - Identifier identifier = Identifier.tryParse(normalized.indexOf(':') >= 0 ? normalized : "minecraft:" + normalized); - if (identifier == null || !BuiltInRegistries.ITEM.containsKey(identifier)) { - return null; - } - Item item = BuiltInRegistries.ITEM.getValue(identifier); - return ModdedItem.of(item, identifier.toString()); + public NativeItem item(String key) { + return nativeAccess.item(key); } @Override - public PlatformEntityType entity(String key) { - Identifier identifier = Identifier.tryParse(key); - if (identifier == null || !BuiltInRegistries.ENTITY_TYPE.containsKey(identifier)) { - return null; - } - EntityType type = BuiltInRegistries.ENTITY_TYPE.getValue(identifier); - return ModdedEntityType.of(type, identifier.toString()); + public NativeEntityType entity(String key) { + return nativeAccess.entity(key); } @Override public List blockKeys() { - List keys = new ArrayList<>(); - for (Identifier identifier : BuiltInRegistries.BLOCK.keySet()) { - keys.add(identifier.toString()); - } - return keys; + return nativeAccess.blockKeys(); } @Override public List biomeKeys() { - List keys = new ArrayList<>(); - Registry registry = biomeRegistry(); - if (registry == null) { - warnNotReady("biome"); - return keys; - } - for (Identifier identifier : registry.keySet()) { - keys.add(identifier.toString()); - } - return keys; + return nativeAccess.biomeKeys(); } @Override public List structureKeys() { - List keys = new ArrayList<>(); - MinecraftServer instance = server.get(); - if (instance == null) { - warnNotReady("structure"); - return keys; - } - for (Identifier identifier : instance.registryAccess().lookupOrThrow(Registries.STRUCTURE).keySet()) { - keys.add(identifier.toString()); - } - return keys; + return nativeAccess.structureKeys(); + } + + @Override + public List entityKeys() { + return nativeAccess.entityKeys(); + } + + @Override + public List enchantmentKeys() { + return nativeAccess.enchantmentKeys(); + } + + @Override + public List potionEffectKeys() { + return nativeAccess.potionEffectKeys(); + } + + @Override + public List lootTableKeys() { + return nativeAccess.lootTableKeys(); } @Override public List itemKeys() { - List keys = new ArrayList<>(); - for (Identifier identifier : BuiltInRegistries.ITEM.keySet()) { - keys.add(identifier.toString()); - } + List keys = nativeAccess.itemKeys(); keys.addAll(ModdedCustomContentRegistry.providerKeys(ModdedDataType.ITEM)); return keys; } @@ -187,100 +139,36 @@ public final class ModdedRegistries implements PlatformRegistries { return ModdedCustomContentRegistry.providerKeys(ModdedDataType.ENTITY); } - @Override - public List entityKeys() { - List keys = new ArrayList<>(); - for (Identifier identifier : BuiltInRegistries.ENTITY_TYPE.keySet()) { - keys.add(identifier.toString()); - } - return keys; - } - @Override public List blockTypeKeys() { - List keys = new ArrayList<>(); - for (Identifier identifier : BuiltInRegistries.BLOCK.keySet()) { - keys.add(identifier.toString()); - } + List keys = nativeAccess.blockKeys(); keys.addAll(customBlockKeys()); return keys; } @Override - public List enchantmentKeys() { - List keys = new ArrayList<>(); - Registry registry = enchantmentRegistry(); - if (registry == null) { - warnNotReady("enchantment"); - return keys; - } - for (Identifier identifier : registry.keySet()) { - keys.add(identifier.toString()); - } - return keys; - } - - @Override - public List potionEffectKeys() { - List keys = new ArrayList<>(); - for (Identifier identifier : BuiltInRegistries.MOB_EFFECT.keySet()) { - keys.add(identifier.toString()); - } - return keys; - } - - @Override - public List lootTableKeys() { - List keys = new ArrayList<>(); - MinecraftServer instance = server.get(); - if (instance == null) { - warnNotReady("loot table"); - return keys; - } - HolderLookup.RegistryLookup registry = instance.reloadableRegistries().lookup() - .lookupOrThrow(Registries.LOOT_TABLE); - registry.listElementIds().forEach(key -> keys.add(key.identifier().toString())); - return keys; - } - - @Override - public Map> blockStateProperties() { - Map> properties = new LinkedHashMap<>(); - // One List instance per identical property group. SchemaBuilder groups consecutive entries by list - // identity, so sharing collapses the emitted schema instead of writing a block-state object per block. - Map> shared = new LinkedHashMap<>(); - for (Block block : BuiltInRegistries.BLOCK) { - BlockState defaultState = block.defaultBlockState(); - List converted = new ArrayList<>(); - for (Property property : block.getStateDefinition().getProperties()) { - converted.add(convertProperty(property, defaultState)); + public Map> blockStateProperties() { + Map> properties = nativeAccess.blockStateProperties(); + List none = List.of(); + for (List group : properties.values()) { + if (group.isEmpty()) { + none = group; + break; } - List group = shared.computeIfAbsent(groupSignature(converted), key -> List.copyOf(converted)); - properties.put(BuiltInRegistries.BLOCK.getKey(block).toString(), group); } - List none = shared.computeIfAbsent(groupSignature(List.of()), key -> List.of()); for (String key : customBlockKeys()) { properties.putIfAbsent(key, none); } return properties; } - private static String groupSignature(List group) { - StringBuilder signature = new StringBuilder(group.size() * 24); - for (PlatformBlockProperty property : group) { - signature.append(property.name()).append(':').append(property.jsonType()).append('=') - .append(property.defaultValue()).append(property.allowedValues()).append(';'); - } - return signature.toString(); - } - private static List customBlockKeys() { List keys = new ArrayList<>(ModdedCustomContentRegistry.aliasBlockKeys()); keys.addAll(ModdedCustomContentRegistry.providerKeys(ModdedDataType.BLOCK)); return keys; } - private static void warnNotReady(String registryName) { + static void warnNotReady(String registryName) { if (NOT_READY.firstOccurrence(registryName)) { IrisLogging.warn("Iris registry read for '" + registryName + "' before the server is ready; returning empty"); } @@ -289,37 +177,4 @@ public final class ModdedRegistries implements PlatformRegistries { IrisLogging.warn(summary); } } - - private Registry biomeRegistry() { - MinecraftServer instance = server.get(); - if (instance == null) { - return null; - } - return instance.registryAccess().lookupOrThrow(Registries.BIOME); - } - - private Registry enchantmentRegistry() { - MinecraftServer instance = server.get(); - if (instance == null) { - return null; - } - return instance.registryAccess().lookupOrThrow(Registries.ENCHANTMENT); - } - - private static > PlatformBlockProperty convertProperty(Property property, BlockState defaultState) { - T defaultValue = defaultState.getValue(property); - Class valueClass = property.getValueClass(); - List allowedValues = new ArrayList<>(); - if (valueClass == Boolean.class || valueClass == Integer.class) { - for (T value : property.getPossibleValues()) { - allowedValues.add(value); - } - String jsonType = valueClass == Boolean.class ? "boolean" : "integer"; - return new PlatformBlockProperty(property.getName(), jsonType, defaultValue, List.copyOf(allowedValues), null); - } - for (T value : property.getPossibleValues()) { - allowedValues.add(property.getName(value)); - } - return new PlatformBlockProperty(property.getName(), "string", property.getName(defaultValue), List.copyOf(allowedValues), null); - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRuntimeRegistry.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRuntimeRegistry.java deleted file mode 100644 index 7bb65d703..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedRuntimeRegistry.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.generation.block.DataProvider; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.dimension.DimensionType; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public final class ModdedRuntimeRegistry { - private ModdedRuntimeRegistry() { - } - - static void ensureDimensionType(Registry registry, - ResourceKey typeKey, String typeRef) { - if (registry.get(typeKey).isPresent()) { - return; - } - throw new IllegalStateException("Iris dimension type '" + typeRef - + "' is not registered. Minecraft freezes the dimension-type registry before Iris can add it," - + " so a pack installed after boot only takes effect on the next start." - + restartAdvice()); - } - - static void ensureCustomBiomes(RegistryAccess registryAccess, IrisDimension dimension, String pack) { - File packFolder = ModdedWorldEngines.packFolder(pack); - if (!packFolder.isDirectory()) { - return; - } - Registry registry = registryAccess.lookupOrThrow(Registries.BIOME); - IrisData data = IrisData.get(packFolder); - DataProvider provider = () -> data; - Set seen = new HashSet<>(); - List missing = new ArrayList<>(); - for (IrisBiome irisBiome : dimension.getAllBiomes(provider)) { - if (!irisBiome.isCustom()) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - String biomeId = customBiome.getId(); - if (!seen.add(biomeId)) { - continue; - } - String biomeRef = data.customBiomeResourceKey(dimension, customBiome); - ResourceKey biomeKey = ResourceKey.create(Registries.BIOME, Identifier.parse(biomeRef)); - if (registry.get(biomeKey).isEmpty()) { - missing.add(biomeRef); - } - } - } - if (!missing.isEmpty()) { - throw new IllegalStateException("Iris pack '" + pack + "' has " + missing.size() - + " custom biome(s) that are not registered. Pack '" + pack - + "' was installed after boot, and Minecraft freezes the biome registry before Iris can add them." - + restartAdvice() + " First missing entry: " - + missing.getFirst()); - } - } - - /** - * One complete instruction per environment. Singleplayer has no /iris world create step to follow the - * restart, so the client branch must not be suffixed with one. - */ - private static String restartAdvice() { - boolean client; - try { - client = ModdedEngineBootstrap.loader().clientEnvironment(); - } catch (RuntimeException unbound) { - client = false; - } - return client - ? " Quit to the title screen and re-create the world from the Iris world type." - : " Restart the server, then run /iris world create again."; - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSavedTerrainCapture.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSavedTerrainCapture.java index 19295506e..2aae89bf2 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSavedTerrainCapture.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSavedTerrainCapture.java @@ -2,141 +2,25 @@ package art.arcane.iris.modded; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.world.history.SavedTerrainChunk; -import art.arcane.iris.modded.mixin.ChunkMapTerrainReceiptAccess; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; +import art.arcane.iris.world.history.SavedTerrainCapturePolicy; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedTerrainSnapshots; +import art.arcane.volmlib.nativelib.terrain.TerrainSnapshotPolicy; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; public final class ModdedSavedTerrainCapture { private ModdedSavedTerrainCapture() { } public static CompletableFuture capture(Engine engine, int chunkX, int chunkZ) { - ServerLevel level = requireLevel(engine); - CompletableFuture snapshot = new CompletableFuture<>(); - runOwned(level, () -> { - try { - ChunkMap map = level.getChunkSource().chunkMap; - ChunkHolder holder = map.getUpdatingChunkIfPresent(ChunkPos.pack(chunkX, chunkZ)); - ChunkAccess chunk = holder == null ? null : holder.getLatestChunk(); - if (chunk == null) { - map.read(new ChunkPos(chunkX, chunkZ)).whenComplete((data, failure) -> { - if (failure != null) { - snapshot.completeExceptionally(failure); - } else { - snapshot.complete(data.orElse(null)); - } - }); - return; - } - String status = BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getPersistedStatus()).toString(); - if (!SavedTerrainChunk.hasTerrain(status)) { - throw new IOException("Native chunk " + chunkX + "," + chunkZ + " has no terrain at " + status); - } - CompoundTag data = SerializableChunkData.copyOf(level, chunk).write(); - if (chunk.isUnsaved()) { - ((ChunkMapTerrainReceiptAccess) map).iris$saveTerrainCheckpoint(chunk); - } - snapshot.complete(data); - } catch (Throwable failure) { - snapshot.completeExceptionally(failure); - } - }); - return snapshot.thenApplyAsync(data -> { - try { - if (data == null) { - throw new IOException("Saved native chunk is unavailable: " + chunkX + "," + chunkZ); - } - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (DataOutputStream output = new DataOutputStream(bytes)) { - NbtIo.write(data, output); - } - return SavedTerrainChunk.readNbt(bytes.toByteArray(), chunkX, chunkZ, engine.getMinHeight(), engine.getHeight()); - } catch (IOException failure) { - throw new CompletionException(failure); - } - }); + TerrainSnapshotPolicy.CaptureTarget target = new TerrainSnapshotPolicy.CaptureTarget( + engine.getWorld().worldFolder().toPath(), chunkX, chunkZ, engine.getMinHeight(), engine.getHeight()); + return ModdedTerrainSnapshots.forWorld(engine.getWorld().platformWorld()) + .capture(target, SavedTerrainCapturePolicy.INSTANCE); } public static CompletableFuture flush(Engine engine) { - ServerLevel level = requireLevel(engine); - CompletableFuture complete = new CompletableFuture<>(); - runOwned(level, () -> { - try { - ChunkMap map = level.getChunkSource().chunkMap; - List checkpoints = new ArrayList<>(); - for (ChunkHolder holder : ((ChunkMapTerrainReceiptAccess) map).iris$updatingChunks().values()) { - ChunkAccess chunk = holder.getLatestChunk(); - if (chunk == null) { - continue; - } - String status = BuiltInRegistries.CHUNK_STATUS.getKey(chunk.getPersistedStatus()).toString(); - if (!SavedTerrainChunk.hasTerrain(status) - && ModdedNativeTerrainReceipts.structureActivation(chunk) == 0) { - continue; - } - checkpoints.add(new Checkpoint(chunk.getPos().x(), chunk.getPos().z(), status, - ModdedNativeTerrainReceipts.get(chunk), ModdedNativeTerrainReceipts.structureActivation(chunk))); - if (chunk.isUnsaved()) { - ((ChunkMapTerrainReceiptAccess) map).iris$saveTerrainCheckpoint(chunk); - } - } - map.synchronize(true).thenRunAsync(() -> verifyCheckpoints(engine, checkpoints)) - .whenComplete((ignored, failure) -> { - if (failure != null) { - complete.completeExceptionally(failure); - } else { - complete.complete(null); - } - }); - } catch (Throwable failure) { - complete.completeExceptionally(failure); - } - }); - return complete; - } - - private static void verifyCheckpoints(Engine engine, List checkpoints) { - try { - for (Checkpoint checkpoint : checkpoints) { - SavedTerrainChunk.verifyCheckpoint(engine.getWorld().worldFolder().toPath(), - checkpoint.chunkX(), checkpoint.chunkZ(), checkpoint.status(), checkpoint.receipt(), - checkpoint.structureActivation()); - } - } catch (IOException failure) { - throw new CompletionException(failure); - } - } - - private record Checkpoint(int chunkX, int chunkZ, String status, byte[] receipt, long structureActivation) { - } - - private static ServerLevel requireLevel(Engine engine) { - if (!(engine.getWorld().platformWorld().nativeHandle() instanceof ServerLevel level)) { - throw new IllegalStateException("The level is unavailable for terrain capture."); - } - return level; - } - - private static void runOwned(ServerLevel level, Runnable action) { - if (level.getServer().isSameThread()) { - action.run(); - } else { - level.getServer().execute(action); - } + return ModdedTerrainSnapshots.forWorld(engine.getWorld().platformWorld()) + .flush(engine.getWorld().worldFolder().toPath(), SavedTerrainCapturePolicy.INSTANCE); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedScheduler.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedScheduler.java index 2b771ed6a..e49d6e537 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedScheduler.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedScheduler.java @@ -18,9 +18,11 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; + + import art.arcane.iris.spi.PlatformScheduler; -import art.arcane.iris.spi.PlatformWorld; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.util.ArrayList; import java.util.List; @@ -90,17 +92,13 @@ public final class ModdedScheduler implements PlatformScheduler { }; } - public static void tick(MinecraftServer server) { - if (server == null) { + public static void tick(Thread running) { + if (running == null) { return; } - Thread running = server.getRunningThread(); if (mainThread != running) { mainThread = running; } - // First thing in the Iris tick body: keep the off-thread level snapshot current for levels registered - // outside ModdedServerAccess (vanilla boot, other mods) before the rest of the tick reads it. - ModdedServerLevels.refreshIfStale(server); ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull(); if (scheduler == null) { return; @@ -126,7 +124,7 @@ public final class ModdedScheduler implements PlatformScheduler { } @Override - public void region(PlatformWorld world, int chunkX, int chunkZ, Runnable task) { + public void region(NativeWorld world, int chunkX, int chunkZ, Runnable task) { global(task); } @@ -171,7 +169,7 @@ public final class ModdedScheduler implements PlatformScheduler { } @Override - public void laterRegion(PlatformWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { + public void laterRegion(NativeWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { laterGlobal(task, ticks); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerAccess.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerAccess.java deleted file mode 100644 index e4fa27511..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerAccess.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.LevelStorageSource; - -import java.util.concurrent.Executor; - -public interface ModdedServerAccess { - Executor levelExecutor(MinecraftServer server); - - LevelStorageSource.LevelStorageAccess levelStorage(MinecraftServer server); - - void initializeLevelData(MinecraftServer server, ServerLevel level); - - ServerLevel putLevel(MinecraftServer server, ResourceKey key, ServerLevel level); - - ServerLevel putLevelIfAbsent(MinecraftServer server, ResourceKey key, ServerLevel level); - - ServerLevel removeLevel(MinecraftServer server, ResourceKey key); - - boolean hasLevel(MinecraftServer server, ResourceKey key); -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerCommands.java deleted file mode 100644 index 800f4e396..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerCommands.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.spi.IrisLogging; -import net.minecraft.server.MinecraftServer; - -public final class ModdedServerCommands { - private ModdedServerCommands() { - } - - public static boolean dispatch(MinecraftServer server, String command) { - if (server == null || command == null || command.isBlank()) { - return false; - } - String prepared = command.startsWith("/") ? command.substring(1) : command; - try { - server.getCommands().performPrefixedCommand(server.createCommandSourceStack(), prepared); - return true; - } catch (Throwable error) { - IrisLogging.reportError(error); - return false; - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerLevels.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerLevels.java deleted file mode 100644 index 66e6b7a1a..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServerLevels.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.storage.LevelStorageSource; -import net.minecraft.world.level.storage.PrimaryLevelData; -import net.minecraft.world.level.storage.WorldData; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ConcurrentModificationException; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -public final class ModdedServerLevels implements ModdedServerAccess { - private static final int CAPTURE_ATTEMPTS = 16; - private static final ClassValue HOST_LEVEL_PUBLICATIONS = new ClassValue<>() { - @Override - protected HostLevelPublication computeValue(Class type) { - return HostLevelPublication.detect(type, ServerLevel.class); - } - }; - private static final ClassValue HOST_LEVEL_DATA_ATTACHMENTS = new ClassValue<>() { - @Override - protected HostLevelDataAttachment computeValue(Class type) { - return HostLevelDataAttachment.detect(type, PrimaryLevelData.class, ResourceKey.class); - } - }; - private static volatile Snapshot snapshot; - - private final Consumer levelCacheInvalidator; - - public ModdedServerLevels(Consumer levelCacheInvalidator) { - this.levelCacheInvalidator = Objects.requireNonNull(levelCacheInvalidator); - } - - /** - * Immutable view of the loaded levels. {@code server.levels} is loader-owned and published on the - * server thread, so every off-server-thread reader must iterate this snapshot instead of - * {@code server.getAllLevels()} to avoid ConcurrentModificationException. - */ - public static List levels(MinecraftServer server) { - if (server == null) { - return List.of(); - } - Snapshot current = snapshot; - if (current != null && current.server() == server) { - return current.levels(); - } - Snapshot captured = capture(server); - return captured == null ? List.of() : captured.levels(); - } - - /** - * Keyed view of the loaded levels for off-server-thread lookups. Never used to gate injection: - * {@link #hasLevel} stays on the live map so a stale snapshot cannot mask a registered level. - */ - public static ServerLevel level(MinecraftServer server, ResourceKey key) { - if (server == null || key == null) { - return null; - } - Snapshot current = snapshot; - Snapshot resolved = current != null && current.server() == server ? current : capture(server); - return resolved == null ? null : resolved.byKey().get(key); - } - - /** - * Re-captures the snapshot when the live map no longer matches it. Server thread only; called once - * per tick so levels registered outside {@link ModdedServerAccess} (vanilla boot, other mods) are - * picked up without any reader touching the live map. - */ - public static void refreshIfStale(MinecraftServer server) { - if (server == null) { - return; - } - Snapshot current = snapshot; - if (current == null || current.server() != server) { - capture(server); - return; - } - List cached = current.levels(); - int index = 0; - for (ServerLevel level : server.getAllLevels()) { - if (index >= cached.size() || cached.get(index) != level) { - capture(server); - return; - } - index++; - } - if (index != cached.size()) { - capture(server); - } - } - - /** - * Drops the snapshot at shutdown so a stopped server (and its level graph) is not held alive until the - * next server publishes one. Integrated servers restart inside the same JVM. - */ - static void forget() { - snapshot = null; - } - - private static Snapshot capture(MinecraftServer server) { - for (int attempt = 0; attempt < CAPTURE_ATTEMPTS; attempt++) { - try { - LinkedHashMap, ServerLevel> byKey = new LinkedHashMap<>(); - for (ServerLevel level : server.getAllLevels()) { - byKey.put(level.dimension(), level); - } - Snapshot captured = new Snapshot(server, List.copyOf(byKey.values()), Map.copyOf(byKey)); - snapshot = captured; - return captured; - } catch (ConcurrentModificationException e) { - Thread.onSpinWait(); - } - } - ModdedIrisLog.error("Iris could not snapshot the level map after {} attempts; readers will see the previous snapshot", CAPTURE_ATTEMPTS); - Snapshot current = snapshot; - return current != null && current.server() == server ? current : null; - } - - @Override - public Executor levelExecutor(MinecraftServer server) { - return server.executor; - } - - @Override - public LevelStorageSource.LevelStorageAccess levelStorage(MinecraftServer server) { - return server.storageSource; - } - - @Override - public void initializeLevelData(MinecraftServer server, ServerLevel level) { - Object levelData = level.getLevelData(); - HostLevelDataAttachment attachment = HOST_LEVEL_DATA_ATTACHMENTS.get(levelData.getClass()); - if (!attachment.supported()) { - return; - } - WorldData worldData = server.getWorldData(); - if (!(worldData instanceof PrimaryLevelData rootData)) { - throw new IllegalStateException("Host level data attachment requires PrimaryLevelData, found " - + worldData.getClass().getName()); - } - attachment.attach(levelData, rootData, level.dimension()); - } - - @Override - public ServerLevel putLevel(MinecraftServer server, ResourceKey key, ServerLevel level) { - HostLevelPublication hostPublication = HOST_LEVEL_PUBLICATIONS.get(server.getClass()); - if (hostPublication.supported()) { - ServerLevel previous = server.levels.get(key); - if (previous != level) { - hostPublication.add(server, level); - capture(server); - } - return previous; - } - ServerLevel previous = server.levels.put(key, level); - if (previous != level) { - capture(server); - levelCacheInvalidator.accept(server); - } - return previous; - } - - @Override - public ServerLevel putLevelIfAbsent(MinecraftServer server, ResourceKey key, ServerLevel level) { - HostLevelPublication hostPublication = HOST_LEVEL_PUBLICATIONS.get(server.getClass()); - if (hostPublication.supported()) { - ServerLevel previous = server.levels.get(key); - if (previous == null) { - hostPublication.add(server, level); - capture(server); - } - return previous; - } - ServerLevel previous = server.levels.putIfAbsent(key, level); - if (previous == null) { - capture(server); - levelCacheInvalidator.accept(server); - } - return previous; - } - - @Override - public ServerLevel removeLevel(MinecraftServer server, ResourceKey key) { - HostLevelPublication hostPublication = HOST_LEVEL_PUBLICATIONS.get(server.getClass()); - if (hostPublication.supported()) { - ServerLevel removed = server.levels.get(key); - if (removed != null) { - hostPublication.remove(server, removed); - capture(server); - } - return removed; - } - ServerLevel removed = server.levels.remove(key); - if (removed != null) { - capture(server); - levelCacheInvalidator.accept(server); - } - return removed; - } - - @Override - public boolean hasLevel(MinecraftServer server, ResourceKey key) { - return server.levels.containsKey(key); - } - - private record Snapshot(MinecraftServer server, List levels, - Map, ServerLevel> byKey) { - } - - static final class HostLevelPublication { - private final Method addLevel; - private final Method removeLevel; - - private HostLevelPublication(Method addLevel, Method removeLevel) { - this.addLevel = addLevel; - this.removeLevel = removeLevel; - } - - static HostLevelPublication detect(Class serverType, Class levelType) { - try { - Method addLevel = serverType.getMethod("addLevel", levelType); - Method removeLevel = serverType.getMethod("removeLevel", levelType); - return new HostLevelPublication(addLevel, removeLevel); - } catch (NoSuchMethodException e) { - return new HostLevelPublication(null, null); - } - } - - boolean supported() { - return addLevel != null && removeLevel != null; - } - - void add(Object server, Object level) { - invoke(addLevel, server, level); - } - - void remove(Object server, Object level) { - invoke(removeLevel, server, level); - } - - private void invoke(Method method, Object server, Object level) { - try { - method.invoke(server, level); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Iris cannot access host level publication method " - + method.getName(), e); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException runtimeException) { - throw runtimeException; - } - if (cause instanceof Error fatalError) { - throw fatalError; - } - throw new IllegalStateException("Host level publication method " + method.getName() - + " failed", cause); - } - } - } - - static final class HostLevelDataAttachment { - private final Method attach; - - private HostLevelDataAttachment(Method attach) { - this.attach = attach; - } - - static HostLevelDataAttachment detect(Class levelDataType, Class rootDataType, - Class dimensionKeyType) { - try { - return new HostLevelDataAttachment(levelDataType.getMethod( - "attach", rootDataType, dimensionKeyType)); - } catch (NoSuchMethodException e) { - return new HostLevelDataAttachment(null); - } - } - - boolean supported() { - return attach != null; - } - - void attach(Object levelData, Object rootData, Object dimensionKey) { - try { - attach.invoke(levelData, rootData, dimensionKey); - } catch (IllegalAccessException e) { - throw new IllegalStateException("Iris cannot access host level data attachment method", e); - } catch (InvocationTargetException e) { - Throwable cause = e.getCause(); - if (cause instanceof RuntimeException runtimeException) { - throw runtimeException; - } - if (cause instanceof Error fatalError) { - throw fatalError; - } - throw new IllegalStateException("Host level data attachment failed", cause); - } - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServiceManager.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServiceManager.java index 222f1d2ca..c093054f1 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServiceManager.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedServiceManager.java @@ -20,7 +20,7 @@ package art.arcane.iris.modded; import art.arcane.iris.modded.service.ModdedService; import art.arcane.iris.modded.service.ModdedTickableService; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.util.LinkedHashMap; import java.util.Map; @@ -72,7 +72,7 @@ public final class ModdedServiceManager { } } - public synchronized void tick(MinecraftServer server) { + public synchronized void tick(NativeModdedServer server) { if (!enabled) { return; } @@ -125,7 +125,7 @@ public final class ModdedServiceManager { services.clear(); } - private void tickService(ModdedTickableService service, MinecraftServer server) { + private void tickService(ModdedTickableService service, NativeModdedServer server) { try { service.onServerTick(server); } catch (Throwable error) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnBiomePolicy.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnBiomePolicy.java new file mode 100644 index 000000000..7e873511b --- /dev/null +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnBiomePolicy.java @@ -0,0 +1,59 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.pack.loading.IrisData; +import art.arcane.iris.generation.runtime.DimensionStackContext; +import art.arcane.iris.generation.runtime.DimensionTerrainContext; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.biome.IrisBiome; +import art.arcane.iris.generation.biome.IrisBiomeCustom; +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.iris.generation.context.IrisContext; +import art.arcane.volmlib.nativelib.terrain.NativeSpawnBiomePolicy; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationLease; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public record ModdedSpawnBiomePolicy(Supplier engines, BiFunction leases) + implements NativeSpawnBiomePolicy { + @Override + public Engine current() { return engines.get(); } + + @Override + public int runtimeId(Engine engine) { return engine.getCacheID(); } + + @Override + public NativeGenerationLease lease(Engine engine) { return leases.apply(engine, "modded_spawn_biomes"); } + + @Override + public NativeGenerationScope context(Engine engine, NativeGenerationLease lease) { + return IrisContext.open(engine, lease.sessionId(), null); + } + + @Override + public void populate(Engine engine, MappingTarget target) { + IrisDimension host = engine.getDimension(); + add(target, host, engine.getData(), host.getReachableBiomes(engine)); + DimensionStackContext stack = engine.getDimensionStackContext(); + if (stack != null) { + for (DimensionTerrainContext terrain : stack.getLayersBottomToTop()) { + if (!terrain.isSelfReferencing()) { + IrisDimension dimension = terrain.getDimension(); + add(target, dimension, terrain.getData(), dimension.getReachableBiomes(terrain)); + } + } + } + } + + private static void add(MappingTarget target, IrisDimension dimension, IrisData data, Iterable biomes) { + for (IrisBiome biome : biomes) { + if (biome == null || !biome.isCustom()) { continue; } + Consumer custom = target.vanilla(biome.getVanillaDerivativeKey()); + if (custom == null) { continue; } + for (IrisBiomeCustom derivative : biome.getCustomDerivitives()) { + custom.accept(data.customBiomeResourceKey(dimension, derivative)); + } + } + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnTableMerger.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnTableMerger.java deleted file mode 100644 index 7a92bf814..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedSpawnTableMerger.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.runtime.DimensionStackContext; -import art.arcane.iris.generation.runtime.DimensionTerrainContext; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.GenerationSessionLease; -import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.generation.context.IrisContext; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.resources.Identifier; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.MobSpawnSettings; - -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -final class ModdedSpawnTableMerger { - private final IrisModdedChunkGenerator generator; - private final ConcurrentHashMap> vanillaSpawnBiomes = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> mergedSpawnTables = - new ConcurrentHashMap<>(); - private final Set initializedRuntimeIdentities = ConcurrentHashMap.newKeySet(); - - ModdedSpawnTableMerger(IrisModdedChunkGenerator generator) { - this.generator = generator; - } - - void initializeVanillaSpawnBiomes(Registry registry) { - Engine current = generator.engineOrNull(); - if (current == null) { - return; - } - int runtimeIdentity = current.getCacheID(); - if (initializedRuntimeIdentities.contains(runtimeIdentity)) { - return; - } - synchronized (generator) { - if (initializedRuntimeIdentities.contains(runtimeIdentity)) { - return; - } - - try (GenerationSessionLease lease = generator.requireGenerationLease(current, "modded_spawn_biomes"); - IrisContext.Scope ignored = IrisContext.open(current, lease.sessionId(), null)) { - IrisDimension hostDimension = current.getDimension(); - addVanillaSpawnBiomes( - registry, - hostDimension, - current.getData(), - hostDimension.getReachableBiomes(current), - runtimeIdentity - ); - DimensionStackContext stackContext = current.getDimensionStackContext(); - if (stackContext != null) { - for (DimensionTerrainContext terrainContext : stackContext.getLayersBottomToTop()) { - if (terrainContext.isSelfReferencing()) { - continue; - } - IrisDimension dimension = terrainContext.getDimension(); - addVanillaSpawnBiomes( - registry, - dimension, - terrainContext.getData(), - dimension.getReachableBiomes(terrainContext), - runtimeIdentity - ); - } - } - initializedRuntimeIdentities.add(runtimeIdentity); - } - } - } - - private void addVanillaSpawnBiomes( - Registry registry, - IrisDimension dimension, - IrisData data, - Iterable biomes, - int runtimeIdentity - ) { - for (IrisBiome irisBiome : biomes) { - if (irisBiome == null || !irisBiome.isCustom()) { - continue; - } - Holder vanillaHolder = resolveBiomeHolder( - registry, irisBiome.getVanillaDerivativeKey()); - if (vanillaHolder == null) { - continue; - } - for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - Holder customHolder = resolveBiomeHolder( - registry, - data.customBiomeResourceKey(dimension, customBiome) - ); - if (customHolder == null) { - continue; - } - vanillaSpawnBiomes.putIfAbsent( - new SpawnBiomeKey(runtimeIdentity, customHolder.value()), - vanillaHolder - ); - } - } - } - - Holder vanillaSpawnBiome(Biome biome) { - Engine current = generator.engineOrNull(); - return current == null - ? null - : vanillaSpawnBiomes.get(new SpawnBiomeKey(current.getCacheID(), biome)); - } - - WeightedList mergedSpawnTable( - Engine engine, - Biome biome, - Biome vanillaBiome, - MobCategory category, - WeightedList vanillaSpawns, - WeightedList explicitSpawns) { - SpawnTableKey key = new SpawnTableKey(engine.getCacheID(), biome, vanillaBiome, category); - return mergedSpawnTables.computeIfAbsent( - key, - ignored -> NativeSpawnTableMerger.merge(vanillaSpawns, explicitSpawns) - ); - } - - void evictRuntime(int runtimeIdentity) { - vanillaSpawnBiomes.keySet().removeIf(key -> key.runtimeIdentity() == runtimeIdentity); - mergedSpawnTables.keySet().removeIf(key -> key.runtimeIdentity() == runtimeIdentity); - initializedRuntimeIdentities.remove(runtimeIdentity); - } - - Holder resolveBiomeHolder(Registry registry, String key) { - if (key == null || key.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - return null; - } - Optional> reference = registry.get(identifier); - return reference.>map((Holder.Reference value) -> value).orElse(null); - } - - void resetVanillaSpawnBiomes() { - synchronized (generator) { - vanillaSpawnBiomes.clear(); - mergedSpawnTables.clear(); - initializedRuntimeIdentities.clear(); - } - } - - private record SpawnBiomeKey(int runtimeIdentity, Biome biome) { - } - - private record SpawnTableKey(int runtimeIdentity, Biome biome, Biome vanillaBiome, MobCategory category) { - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStartup.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStartup.java index 6fdd41506..80483b617 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStartup.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStartup.java @@ -26,10 +26,8 @@ import art.arcane.iris.pack.PackValidationResult; import art.arcane.iris.pack.PackValidator; import art.arcane.iris.modded.command.ModdedPackCommands; import art.arcane.iris.spi.IrisPlatforms; -import net.minecraft.commands.Commands; -import net.minecraft.network.chat.Component; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; import java.io.File; import java.io.IOException; @@ -107,8 +105,8 @@ public final class ModdedStartup { } } - public static void runOnce(MinecraftServer server) { - if (server == null || server.getPlayerList() == null) { + public static void runOnce(NativeModdedServer server) { + if (server == null || !server.hasPlayerList()) { return; } prepareForStartup(); @@ -210,7 +208,7 @@ public final class ModdedStartup { } } - private static void reinjectPersistentDimensions(MinecraftServer server) { + private static void reinjectPersistentDimensions(NativeModdedServer server) { List dimensions = ModdedDimensionRegistryStore.loadForStartup(server); if (dimensions.isEmpty()) { @@ -266,8 +264,8 @@ public final class ModdedStartup { * SP-6: a pack excluded by validation is otherwise only visible in the console. Tell the operators who * can actually act on it when they join. */ - public static void warnPackFailuresTo(ServerPlayer player) { - if (player == null || !Commands.LEVEL_GAMEMASTERS.check(player.permissions())) { + public static void warnPackFailuresTo(NativeProtocolPlayer player) { + if (player == null || !player.isGameMaster()) { return; } for (Map.Entry entry : PackValidationRegistry.snapshot().entrySet()) { @@ -278,8 +276,8 @@ public final class ModdedStartup { String reason = result.getBlockingErrors().isEmpty() ? "unknown validation failure" : result.getBlockingErrors().getFirst(); - player.sendSystemMessage(Component.literal("Iris pack '" + entry.getKey() - + "' failed validation and cannot be used: " + reason)); + player.sendMessage("Iris pack '" + entry.getKey() + + "' failed validation and cannot be used: " + reason); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateMerger.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateMerger.java deleted file mode 100644 index c6dcf0d6d..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateMerger.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.generation.block.BlockDataMergeSupport; -import art.arcane.iris.spi.PlatformBlockState; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; - -import java.util.Map; - -public final class ModdedStateMerger implements BlockDataMergeSupport.StateMerger { - @Override - public PlatformBlockState merge(PlatformBlockState base, PlatformBlockState update) { - if (!(base instanceof ModdedBlockState fabricBase) || !(update instanceof ModdedBlockState fabricUpdate)) { - return update; - } - ModdedBlockState metadataSource = fabricUpdate.isCustom() ? fabricUpdate : fabricBase; - - try { - return metadataSource.withHandle(mergeStates(fabricBase.handle(), fabricUpdate.handle(), fabricUpdate.parsedProperties()), null); - } catch (IllegalArgumentException e) { - ModdedBlockResolution.Parsed normalizedBase = ModdedBlockResolution.resolveGet(ModdedBlockState.serialize(fabricBase.handle())); - ModdedBlockResolution.Parsed normalizedUpdate = ModdedBlockResolution.resolveGet(ModdedBlockState.serialize(fabricUpdate.handle())); - - if (normalizedBase != null && normalizedUpdate != null) { - try { - return metadataSource.withHandle(mergeStates(normalizedBase.state(), normalizedUpdate.state(), normalizedUpdate.properties()), null); - } catch (IllegalArgumentException ignored) { - return metadataSource.withHandle(normalizedUpdate.state(), normalizedUpdate.properties()); - } - } - - if (normalizedUpdate != null) { - return metadataSource.withHandle(normalizedUpdate.state(), normalizedUpdate.properties()); - } - - return update; - } - } - - private static BlockState mergeStates(BlockState base, BlockState update, Map, Comparable> parsedProperties) { - if (parsedProperties == null) { - throw new IllegalArgumentException("Block data not created via string parsing"); - } - if (base.getBlock() != update.getBlock()) { - throw new IllegalArgumentException("States have different types (got " + update.getBlock() + ", expected " + base.getBlock() + ")"); - } - BlockState merged = base; - for (Map.Entry, Comparable> entry : parsedProperties.entrySet()) { - merged = apply(merged, entry.getKey(), entry.getValue()); - } - return merged; - } - - @SuppressWarnings("unchecked") - private static > BlockState apply(BlockState state, Property property, Comparable value) { - return state.setValue((Property) property, (T) value); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateRotator.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateRotator.java index 9e3ecfdcd..72c0ec397 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateRotator.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStateRotator.java @@ -1,367 +1,23 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.modded; -import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; -import net.minecraft.core.Direction; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.EnumProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; -import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.block.state.properties.RedstoneSide; -import net.minecraft.world.level.block.state.properties.RotationSegment; -import net.minecraft.world.level.block.state.properties.WallSide; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.structure.object.IrisObjectRotation; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStateRotator; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public final class ModdedStateRotator implements IrisObjectRotation.StateRotator { - private static final String[] FACE_NAMES = {"north", "east", "south", "west", "up", "down"}; - private static final String[] WALL_FACE_NAMES = {"north", "south", "east", "west"}; - private static final int[][] ROTATION_CYCLE_MODS = { - {0, 1}, {-1, 2}, {-1, 1}, {-2, 1}, {-1, 0}, {-2, -1}, {-1, -1}, {-1, -2}, - {0, -1}, {1, -2}, {1, -1}, {2, -1}, {1, 0}, {2, 1}, {1, 1}, {1, 2} - }; - @Override - public PlatformBlockState rotate(IrisObjectRotation rotation, PlatformBlockState state, int spinxx, int spinyy, int spinzz) { - if (!(state instanceof ModdedBlockState fabric)) { + public NativeBlockState rotate(IrisObjectRotation rotation, NativeBlockState state, int spinxx, int spinyy, int spinzz) { + if (!rotation.canRotate()) { return state; } - BlockState d = fabric.handle(); - boolean deleted = false; - - try { - int spinx = (int) (90D * (Math.ceil(Math.abs((spinxx % 360D) / 90D)))); - int spiny = (int) (90D * (Math.ceil(Math.abs((spinyy % 360D) / 90D)))); - int spinz = (int) (90D * (Math.ceil(Math.abs((spinzz % 360D) / 90D)))); - - if (!rotation.canRotate()) { - return state; - } - - Property facing = findFacing(d); - Property rotationProperty = findRotation(d); - Property axis = findAxis(d); - Map multipleFacing = findMultipleFacing(d); - Map> wall = findWall(d); - Map> wire = findWire(d); - - if (facing != null) { - Direction f = (Direction) d.getValue(facing); - IrisBlockVector bv = new IrisBlockVector(f.getStepX(), f.getStepY(), f.getStepZ()); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - Direction t = faceFor(bv); - - if (facing.getPossibleValues().contains(t)) { - d = apply(d, facing, t); - } else if (!ModdedBlockResolution.isSolid(d.getBlock().defaultBlockState())) { - deleted = true; - } - } else if (rotationProperty != null) { - int segment = (Integer) d.getValue(rotationProperty); - int[] mods = ROTATION_CYCLE_MODS[segment]; - - IrisBlockVector bv = new IrisBlockVector(mods[0], 0, mods[1]); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - int[] hex = hexMods(bv); - - if (hex[0] == 0 && hex[1] == 0) { - throw new IllegalArgumentException("Invalid face, only horizontal face are allowed for this property!"); - } - double length = Math.sqrt((double) (hex[0] * hex[0] + hex[1] * hex[1])); - float angle = (float) -Math.toDegrees(Math.atan2(hex[0] / length, hex[1] / length)); - d = apply(d, rotationProperty, RotationSegment.convertToSegment(angle)); - } else if (axis != null) { - Direction.Axis current = (Direction.Axis) d.getValue(axis); - IrisBlockVector bv = axisFaceVector(current); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - Direction.Axis a = axisOf(bv); - - if (!a.equals(current) && axis.getPossibleValues().contains(a)) { - d = apply(d, axis, a); - } - } else if (multipleFacing.size() >= 2) { - List trueFaces = new ArrayList<>(); - for (Map.Entry entry : multipleFacing.entrySet()) { - if (d.getValue(entry.getValue())) { - trueFaces.add(entry.getKey()); - } - } - - List faces = new ArrayList<>(); - for (String name : trueFaces) { - IrisBlockVector bv = faceVector(name); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - String r = faceName(faceFor(bv)); - - if (multipleFacing.containsKey(r)) { - faces.add(r); - } - } - - for (String name : trueFaces) { - d = apply(d, multipleFacing.get(name), Boolean.FALSE); - } - - for (String name : faces) { - d = apply(d, multipleFacing.get(name), Boolean.TRUE); - } - } else if (wall.size() == 4) { - Map heights = new LinkedHashMap<>(); - - for (String name : WALL_FACE_NAMES) { - WallSide h = (WallSide) d.getValue(wall.get(name)); - IrisBlockVector bv = faceVector(name); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - String r = faceName(faceFor(bv)); - if (wall.containsKey(r)) { - heights.put(r, h); - } - } - - for (String name : WALL_FACE_NAMES) { - d = apply(d, wall.get(name), heights.getOrDefault(name, WallSide.NONE)); - } - } else if (wire.size() == 4) { - Map connections = new LinkedHashMap<>(); - - for (String name : WALL_FACE_NAMES) { - RedstoneSide connection = (RedstoneSide) d.getValue(wire.get(name)); - IrisBlockVector bv = faceVector(name); - bv = rotation.rotate(bv.clone(), spinx, spiny, spinz); - String r = faceName(faceFor(bv)); - if (wire.containsKey(r)) { - connections.put(r, connection); - } - } - - for (String name : WALL_FACE_NAMES) { - d = apply(d, wire.get(name), connections.getOrDefault(name, RedstoneSide.NONE)); - } - } - } catch (Throwable e) { - IrisLogging.reportError(e); - } - - if (deleted) { - return null; - } - - return d == fabric.handle() ? state : fabric.withHandle(d, fabric.parsedProperties()); - } - - private static Property findFacing(BlockState state) { - for (Property property : state.getProperties()) { - if (property.getName().equals("facing") && property.getValueClass() == Direction.class) { - return property; - } - } - return null; - } - - private static Property findRotation(BlockState state) { - for (Property property : state.getProperties()) { - if (property.getName().equals("rotation") - && property instanceof IntegerProperty integer - && isFullRotationCycle(integer)) { - return property; - } - } - return null; - } - - private static boolean isFullRotationCycle(IntegerProperty property) { - List values = property.getPossibleValues(); - if (values.size() != ROTATION_CYCLE_MODS.length) { - return false; - } - for (int value : values) { - if (value < 0 || value >= ROTATION_CYCLE_MODS.length) { - return false; - } - } - return true; - } - - private static Property findAxis(BlockState state) { - for (Property property : state.getProperties()) { - if (property.getName().equals("axis") && property.getValueClass() == Direction.Axis.class) { - return property; - } - } - return null; - } - - private static Map findMultipleFacing(BlockState state) { - Map properties = new LinkedHashMap<>(); - for (String name : FACE_NAMES) { - for (Property property : state.getProperties()) { - if (property.getName().equals(name) && property instanceof BooleanProperty bool) { - properties.put(name, bool); - } - } - } - return properties; - } - - @SuppressWarnings("unchecked") - private static Map> findWall(BlockState state) { - Map> properties = new LinkedHashMap<>(); - for (String name : WALL_FACE_NAMES) { - for (Property property : state.getProperties()) { - if (property.getName().equals(name) && property.getValueClass() == WallSide.class) { - properties.put(name, (EnumProperty) property); - } - } - } - return properties; - } - - @SuppressWarnings("unchecked") - private static Map> findWire(BlockState state) { - Map> properties = new LinkedHashMap<>(); - for (String name : WALL_FACE_NAMES) { - for (Property property : state.getProperties()) { - if (property.getName().equals(name) && property.getValueClass() == RedstoneSide.class) { - properties.put(name, (EnumProperty) property); - } - } - } - return properties; - } - - private static Direction faceFor(IrisBlockVector v) { - int x = (int) Math.round(v.getX()); - int y = (int) Math.round(v.getY()); - int z = (int) Math.round(v.getZ()); - - if (x == 0 && z == -1) { - return Direction.NORTH; - } - - if (x == 0 && z == 1) { - return Direction.SOUTH; - } - - if (x == 1 && z == 0) { - return Direction.EAST; - } - - if (x == -1 && z == 0) { - return Direction.WEST; - } - - if (y > 0) { - return Direction.UP; - } - - if (y < 0) { - return Direction.DOWN; - } - - return Direction.SOUTH; - } - - private static int[] hexMods(IrisBlockVector v) { - int x = v.getBlockX(); - int y = v.getBlockY(); - int z = v.getBlockZ(); - - if (x == 0 && z == -1) return new int[]{0, -1}; - if (x == 1 && z == -2) return new int[]{1, -2}; - if (x == 1 && z == -1) return new int[]{1, -1}; - if (x == 2 && z == -1) return new int[]{2, -1}; - if (x == 1 && z == 0) return new int[]{1, 0}; - if (x == 2 && z == 1) return new int[]{2, 1}; - if (x == 1 && z == 1) return new int[]{1, 1}; - if (x == 1 && z == 2) return new int[]{1, 2}; - if (x == 0 && z == 1) return new int[]{0, 1}; - if (x == -1 && z == 2) return new int[]{-1, 2}; - if (x == -1 && z == 1) return new int[]{-1, 1}; - if (x == -2 && z == 1) return new int[]{-2, 1}; - if (x == -1 && z == 0) return new int[]{-1, 0}; - if (x == -2 && z == -1) return new int[]{-2, -1}; - if (x == -1 && z == -1) return new int[]{-1, -1}; - if (x == -1 && z == -2) return new int[]{-1, -2}; - - if (y > 0) { - return new int[]{0, 0}; - } - - if (y < 0) { - return new int[]{0, 0}; - } - - return new int[]{0, 1}; - } - - private static IrisBlockVector axisFaceVector(Direction.Axis axis) { - if (axis == Direction.Axis.X) { - return new IrisBlockVector(1, 0, 0); - } - if (axis == Direction.Axis.Y) { - return new IrisBlockVector(0, 1, 0); - } - return new IrisBlockVector(0, 0, 1); - } - - private static Direction.Axis axisOf(IrisBlockVector v) { - if (Math.abs(v.getBlockX()) > Math.max(Math.abs(v.getBlockY()), Math.abs(v.getBlockZ()))) { - return Direction.Axis.X; - } - - if (Math.abs(v.getBlockY()) > Math.max(Math.abs(v.getBlockX()), Math.abs(v.getBlockZ()))) { - return Direction.Axis.Y; - } - - if (Math.abs(v.getBlockZ()) > Math.max(Math.abs(v.getBlockX()), Math.abs(v.getBlockY()))) { - return Direction.Axis.Z; - } - - return Direction.Axis.Y; - } - - private static IrisBlockVector faceVector(String name) { - return switch (name) { - case "north" -> new IrisBlockVector(0, 0, -1); - case "south" -> new IrisBlockVector(0, 0, 1); - case "east" -> new IrisBlockVector(1, 0, 0); - case "west" -> new IrisBlockVector(-1, 0, 0); - case "up" -> new IrisBlockVector(0, 1, 0); - case "down" -> new IrisBlockVector(0, -1, 0); - default -> new IrisBlockVector(0, 0, 0); - }; - } - - private static String faceName(Direction direction) { - return direction.getSerializedName(); - } - - @SuppressWarnings("unchecked") - private static > BlockState apply(BlockState state, Property property, Object value) { - return state.setValue((Property) property, (T) value); + int spinx = (int) (90D * Math.ceil(Math.abs((spinxx % 360D) / 90D))); + int spiny = (int) (90D * Math.ceil(Math.abs((spinyy % 360D) / 90D))); + int spinz = (int) (90D * Math.ceil(Math.abs((spinzz % 360D) / 90D))); + return NativeStateRotator.rotate(state, (x, y, z) -> { + IrisBlockVector rotated = rotation.rotate(new IrisBlockVector(x, y, z), spinx, spiny, spinz); + return new NativeStateRotator.Vector(rotated.getX(), rotated.getY(), rotated.getZ()); + }, IrisLogging::reportError); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureHooks.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureHooks.java index a874ba674..dc1f42884 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureHooks.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureHooks.java @@ -18,47 +18,14 @@ package art.arcane.iris.modded; -import art.arcane.iris.nativegen.NativeStructureGenerationException; -import art.arcane.iris.nativegen.NativeStructureGenerationKeys; -import art.arcane.iris.nativegen.NativeStructureFactory; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; -import art.arcane.iris.spi.PlatformWorld; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.StructureManager; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.RandomState; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.XoroshiroRandomSource; -import net.minecraft.world.level.levelgen.feature.AbstractHugeMushroomFeature; -import net.minecraft.world.level.levelgen.feature.ConfiguredFeature; -import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; -import net.minecraft.world.level.levelgen.feature.Feature; -import net.minecraft.world.level.levelgen.feature.TreeFeature; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.structures.JigsawStructure; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; - -import java.util.ArrayList; -import java.util.LinkedHashSet; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureOperations; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; + import java.util.function.Supplier; public final class ModdedStructureHooks implements PlatformStructureHooks { @@ -67,391 +34,82 @@ public final class ModdedStructureHooks implements PlatformStructureHooks { * the grid synchronously on the server thread, so a structure with a runaway bounding box (or maxSpan 0, * which disables the span check entirely) would otherwise stall the server for thousands of chunk loads. */ - static final int MAX_PLACEMENT_CHUNKS = 1024; + private static final int MAX_PLACEMENT_CHUNKS = 1024; - private final Supplier server; + private final NativeStructureOperations operations; - public ModdedStructureHooks(Supplier server) { - this.server = Objects.requireNonNull(server); + public ModdedStructureHooks(Supplier server) { + operations = new NativeStructureOperations(new NativeStructureOperations.Options( + server, MAX_PLACEMENT_CHUNKS, IrisLogging::reportError, IrisLogging::warn)); } @Override public List structureKeys() { - return registryKeys(Registries.STRUCTURE); + return operations.structureKeys(); } @Override public List jigsawStructureKeys() { - List keys = new ArrayList<>(); - MinecraftServer instance = requireServer("read registered jigsaw structures"); - Registry structures = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE); - for (Map.Entry, Structure> entry : structures.entrySet()) { - if (entry.getValue() instanceof JigsawStructure) { - keys.add(entry.getKey().identifier().toString()); - } - } - return keys; + return operations.jigsawStructureKeys(); } @Override public List templatePoolKeys() { - return registryKeys(Registries.TEMPLATE_POOL); + return operations.templatePoolKeys(); } @Override public JigsawSourceMetadata jigsawSourceMetadata(String structureKey) { - MinecraftServer instance = requireServer("resolve live jigsaw metadata for registered structure '" - + structureKey + "'"); - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Registry registry = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Structure structure = registry.getValue(identifier); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - return NativeStructureFactory.sourceMetadata( - instance.registryAccess(), instance.getStructureManager(), jigsaw); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve live jigsaw metadata for registered structure '" - + structureKey + "' from the modded structure registry", error); - } + return operations.jigsawSourceMetadata(structureKey); } @Override public int templatePoolHorizontalSpan(String templatePoolKey) { - MinecraftServer instance = requireServer("resolve the live horizontal span for registered template pool '" - + templatePoolKey + "'"); - try { - return NativeStructureFactory.templatePoolHorizontalSpan( - instance.registryAccess(), instance.getStructureManager(), templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the live horizontal span for registered " - + "template pool '" + templatePoolKey + "' from the modded template-pool registry", error); - } + return operations.templatePoolHorizontalSpan(templatePoolKey); } @Override public int jigsawStartPoolHorizontalSpan(String structureKey, String templatePoolKey) { - MinecraftServer instance = requireServer("resolve the effective start-pool span for registered jigsaw '" - + structureKey + "'"); - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Invalid registered structure key: " + structureKey); - } - Structure structure = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE) - .getValue(identifier); - if (!(structure instanceof JigsawStructure jigsaw)) { - throw new IllegalArgumentException("Registered structure is not a jigsaw: " + structureKey); - } - return NativeStructureFactory.jigsawStartPoolHorizontalSpan( - instance.registryAccess(), instance.getStructureManager(), jigsaw, templatePoolKey); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve the effective start-pool span for registered " - + "jigsaw structure '" + structureKey + "' and pool '" + templatePoolKey - + "' from the modded registries", error); - } + return operations.jigsawStartPoolHorizontalSpan(structureKey, templatePoolKey); } @Override public List structureSetKeys() { - return registryKeys(Registries.STRUCTURE_SET); + return operations.structureSetKeys(); } @Override public List structureBiomeKeys(String structureKey) { - List keys = new ArrayList<>(); - MinecraftServer instance = requireServer("resolve biome keys for registered structure '" + structureKey + "'"); - try { - Identifier identifier = Identifier.tryParse(structureKey); - if (identifier == null) { - throw new IllegalArgumentException("Registered structure key is invalid: " + structureKey); - } - Registry registry = instance.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Structure structure = registry.getValue(identifier); - if (structure == null) { - throw new IllegalArgumentException("Registered structure does not exist: " + structureKey); - } - boolean hasFilterEntries = false; - for (Holder holder : structure.biomes()) { - hasFilterEntries = true; - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - // An empty biome filter is legal datapack content (opt-in structures whose tags only - // reference absent modded biomes); the structure is unreachable, not an error state. - if (keys.isEmpty() && hasFilterEntries) { - throw new IllegalStateException("Registered structure '" + structureKey - + "' has biome filter entries but none resolve to registered biome keys"); - } - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve biome keys for registered structure '" - + structureKey + "' from the modded structure registry", error); - } - return keys; + return operations.structureBiomeKeys(structureKey); } @Override public List objectFeatureKeys() { - List keys = new ArrayList<>(); - try { - MinecraftServer instance = server.get(); - if (instance == null) { - return keys; - } - Registry> registry = instance.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE); - for (Identifier identifier : registry.keySet()) { - ConfiguredFeature configuredFeature = registry.getValue(identifier); - if (configuredFeature == null) { - continue; - } - String group = classifyFeature(configuredFeature.feature()); - if (group != null) { - keys.add(group + "|" + identifier); - } - } - } catch (Throwable error) { - IrisLogging.reportError(error); - } - return keys; + return operations.objectFeatureKeys(); } @Override - public List reachableStructureKeys(PlatformWorld world) { - ServerLevel level = requireLevel(world, "resolve reachable structures"); - try { - BiomeSource source = level.getChunkSource().getGenerator().getBiomeSource(); - Set possibleBiomes = possibleBiomeKeys(source); - return new ArrayList<>(NativeStructureGenerationKeys.reachable(level, possibleBiomes)); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve reachable structures for modded level '" - + level.dimension().identifier() + "'", error); - } + public List reachableStructureKeys(NativeWorld world) { + return operations.reachableStructureKeys(world); } @Override - public List possibleBiomeKeys(PlatformWorld world) { - List keys = new ArrayList<>(); - ServerLevel level = requireLevel(world, "resolve possible structure biome keys"); - try { - keys.addAll(possibleBiomeKeys(level.getChunkSource().getGenerator().getBiomeSource())); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to resolve possible structure biome keys for modded level '" - + level.dimension().identifier() + "'", error); - } - return keys; - } - - /** - * Swallow contract: a feature that refuses to place, or that throws while placing, is a normal outcome for - * the importer that drives this - it probes many keys against arbitrary terrain and treats false as "not - * here". So every Throwable is reported through IrisLogging and answered with false; placement never - * escalates to the caller. placeStructure below deliberately does the opposite (it rethrows with context) - * because a failed structure capture means the capture pass is broken, not that the site was unsuitable. - */ - @Override - public boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed) { - try { - ServerLevel level = level(world); - Identifier identifier = Identifier.tryParse(featureKey); - if (level == null || identifier == null) { - return false; - } - ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry> registry = level.registryAccess().lookupOrThrow(Registries.CONFIGURED_FEATURE); - ConfiguredFeature configuredFeature = registry.getValue(identifier); - if (configuredFeature == null) { - return false; - } - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - return configuredFeature.place(level, generator, random, new BlockPos(x, y, z)); - } catch (Throwable error) { - IrisLogging.reportError(error); - return false; - } + public List possibleBiomeKeys(NativeWorld world) { + return operations.possibleBiomeKeys(world); } @Override - public int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { - try { - ServerLevel level = level(world); - Identifier identifier = Identifier.tryParse(structureKey); - if (level == null || identifier == null) { - return null; - } - if (!level.getServer().isSameThread()) { - throw new IllegalStateException("Structure placement loads chunks synchronously and must run on the server thread, not " - + Thread.currentThread().getName()); - } - ChunkGenerator generator = level.getChunkSource().getGenerator(); - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Structure structure = registry.getValue(identifier); - if (structure == null) { - return null; - } - Holder holder = registry.wrapAsHolder(structure); - RandomState randomState = level.getChunkSource().randomState(); - StructureTemplateManager templateManager = level.getStructureManager(); - StructureManager structureManager = level.structureManager(); - BiomeSource biomeSource = generator.getBiomeSource(); - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - StructureStart start = structure.generate( - holder, - level.dimension(), - level.registryAccess(), - generator, - biomeSource, - randomState, - templateManager, - seed, - chunkPos, - 0, - level, - biome -> true); - if (start == null || !start.isValid()) { - return null; - } - BoundingBox box = start.getBoundingBox(); - if (!isWithinSpan(box, maxSpan)) { - return null; - } - int placementChunks = chunkGridSize(box); - if (placementChunks > MAX_PLACEMENT_CHUNKS) { - IrisLogging.warn("Skipped structure capture for " + structureKey + " at " + chunkX + "," + chunkZ - + ": bounding box spans " + placementChunks + " chunks, cap is " + MAX_PLACEMENT_CHUNKS); - return null; - } - placeChunks(level, structureManager, generator, start, box, seed); - return bounds(box); - } catch (RuntimeException error) { - throw NativeStructureGenerationException.failure( - "capture placement", structureKey, chunkX, chunkZ, error); - } + public boolean placeFeature(NativeWorld world, int x, int y, int z, String featureKey, long seed) { + return operations.placeFeature(world, x, y, z, featureKey, seed); + } + + @Override + public int[] placeStructure(NativeWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { + return operations.placeStructure(world, chunkX, chunkZ, structureKey, seed, maxSpan); } @Override public boolean supportsStructurePlacement() { - return true; - } - - static String classifyFeature(Feature feature) { - if (feature instanceof TreeFeature) { - return "trees"; - } - if (feature instanceof FallenTreeFeature) { - return "fallen_trees"; - } - if (feature instanceof AbstractHugeMushroomFeature) { - return "mushrooms"; - } - return null; - } - - static boolean isWithinSpan(BoundingBox box, int maxSpan) { - return maxSpan <= 0 - || box.getXSpan() <= maxSpan && box.getYSpan() <= maxSpan && box.getZSpan() <= maxSpan; - } - - /** - * Number of chunks placeChunks would load for this bounding box. Computed in long arithmetic because a - * corrupt box can span the whole coordinate range and the product overflows int. - */ - static int chunkGridSize(BoundingBox box) { - long spanX = ((long) (box.maxX() >> 4)) - (box.minX() >> 4) + 1L; - long spanZ = ((long) (box.maxZ() >> 4)) - (box.minZ() >> 4) + 1L; - if (spanX <= 0L || spanZ <= 0L) { - return 0; - } - long total = spanX * spanZ; - return total > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) total; - } - - static int[] bounds(BoundingBox box) { - return new int[]{box.minX(), box.minY(), box.minZ(), box.maxX(), box.maxY(), box.maxZ()}; - } - - private static Set possibleBiomeKeys(BiomeSource source) { - if (source == null) { - throw new IllegalStateException("Minecraft chunk generator has no biome source"); - } - Set keys = new LinkedHashSet<>(); - for (Holder holder : source.possibleBiomes()) { - Optional> key = holder.unwrapKey(); - if (key.isPresent()) { - keys.add(key.get().identifier().toString()); - } - } - if (keys.isEmpty()) { - throw new IllegalStateException("Minecraft biome source exposes no registered possible biomes"); - } - return keys; - } - - private static ServerLevel level(PlatformWorld world) { - return world instanceof ModdedPlatformWorld moddedWorld ? moddedWorld.level() : null; - } - - private static void placeChunks(ServerLevel level, StructureManager structureManager, ChunkGenerator generator, - StructureStart start, BoundingBox box, long seed) { - WorldgenRandom random = new WorldgenRandom(new XoroshiroRandomSource(seed)); - int minChunkX = box.minX() >> 4; - int maxChunkX = box.maxX() >> 4; - int minChunkZ = box.minZ() >> 4; - int maxChunkZ = box.maxZ() >> 4; - for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { - for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { - level.getChunk(chunkX, chunkZ); - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - BoundingBox chunkBox = new BoundingBox( - chunkPos.getMinBlockX(), box.minY(), chunkPos.getMinBlockZ(), - chunkPos.getMaxBlockX(), box.maxY(), chunkPos.getMaxBlockZ()); - start.placeInChunk(level, structureManager, generator, random, chunkBox, chunkPos); - } - } - } - - private List registryKeys(ResourceKey> registryKey) { - List keys = new ArrayList<>(); - MinecraftServer instance = requireServer("read registry '" + registryKey.identifier() + "'"); - try { - Registry registry = instance.registryAccess().lookupOrThrow(registryKey); - for (Identifier identifier : registry.keySet()) { - keys.add(identifier.toString()); - } - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to read modded registry '" - + registryKey.identifier() + "'", error); - } - return keys; - } - - private MinecraftServer requireServer(String operation) { - MinecraftServer instance; - try { - instance = server.get(); - } catch (RuntimeException error) { - throw new IllegalStateException("Iris failed to access the Minecraft server to " + operation, error); - } - if (instance == null) { - throw new IllegalStateException("Iris cannot " + operation + " before the Minecraft server is available"); - } - return instance; - } - - private static ServerLevel requireLevel(PlatformWorld world, String operation) { - ServerLevel level = level(world); - if (level == null) { - throw new IllegalStateException("Iris cannot " + operation - + " without a bound modded ServerLevel"); - } - return level; + return operations.supportsStructurePlacement(); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverrides.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverrides.java deleted file mode 100644 index bbc9a28fb..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverrides.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.structure.nativegen.NativeStructureFrequencyScale; -import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderOwner; -import net.minecraft.core.Vec3i; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -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; - -final class ModdedStructureSetFrequencyOverrides { - private ModdedStructureSetFrequencyOverrides() { - } - - static ChunkGeneratorStructureState apply( - ChunkGeneratorStructureState state, - IrisImportedStructureControl importedStructures - ) { - if (!importedStructures.hasFrequencyOverrides()) { - return state; - } - List> original = state.possibleStructureSets(); - List> scaled = scaleSets(original, importedStructures); - if (scaled == original) { - return state; - } - Field possibleSetsField = declaredField(ChunkGeneratorStructureState.class, List.class); - try { - possibleSetsField.set(state, scaled); - return state; - } catch (IllegalAccessException error) { - throw new IllegalStateException( - "Could not apply native structure-set frequency overrides", error); - } - } - - static List> scaleSets( - List> structureSets, - IrisImportedStructureControl importedStructures - ) { - Map> holdersByKey = new HashMap<>(); - for (Holder holder : structureSets) { - String key = structureSetKey(holder); - if (key != null) { - holdersByKey.putIfAbsent(key, holder); - } - } - Map, Holder> scaledByIdentity = new IdentityHashMap<>(); - List> scaledSets = new ArrayList<>(structureSets.size()); - boolean changed = false; - for (Holder holder : structureSets) { - Holder scaled = scaleHolder( - holder, importedStructures, holdersByKey, scaledByIdentity); - scaledSets.add(scaled); - changed |= scaled != holder; - } - return changed ? List.copyOf(scaledSets) : structureSets; - } - - private static Holder scaleHolder( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey, - Map, Holder> scaledByIdentity - ) { - if (scaledByIdentity.containsKey(holder)) { - return scaledByIdentity.get(holder); - } - if (!dependsOnOverride(holder, importedStructures, holdersByKey)) { - scaledByIdentity.put(holder, holder); - return holder; - } - ResourceKey holderKey = holder.unwrapKey().orElseThrow(() -> - new IllegalStateException("An affected native structure-set exclusion graph has an unkeyed holder")); - ScaledStructureSetHolder scaledHolder = new ScaledStructureSetHolder(holderKey); - scaledByIdentity.put(holder, scaledHolder); - - StructureSet originalSet = holder.value(); - StructurePlacement originalPlacement = originalSet.placement(); - Optional originalZone = exclusionZone(originalPlacement); - Optional scaledZone = originalZone; - if (originalZone.isPresent()) { - Holder target = canonicalHolder( - originalZone.get().otherSet(), holdersByKey); - Holder scaledTarget = scaleHolder( - target, importedStructures, holdersByKey, scaledByIdentity); - if (scaledTarget != originalZone.get().otherSet()) { - scaledZone = Optional.of(new StructurePlacement.ExclusionZone( - scaledTarget, originalZone.get().chunkCount())); - } - } - double multiplier = importedStructures.frequencyMultiplier(structureSetKey(holder)); - StructurePlacement scaledPlacement = multiplier == 1D && scaledZone.equals(originalZone) - ? originalPlacement - : scalePlacement(originalPlacement, originalZone, scaledZone, multiplier); - scaledHolder.bind(new StructureSet(originalSet.structures(), scaledPlacement)); - return scaledHolder; - } - - private static boolean dependsOnOverride( - Holder holder, - IrisImportedStructureControl importedStructures, - Map> holdersByKey - ) { - Set> visited = Collections.newSetFromMap(new IdentityHashMap<>()); - Holder current = holder; - while (visited.add(current)) { - if (importedStructures.frequencyMultiplier(structureSetKey(current)) != 1D) { - return true; - } - Optional zone = - exclusionZone(current.value().placement()); - if (zone.isEmpty()) { - return false; - } - current = canonicalHolder(zone.get().otherSet(), holdersByKey); - } - return false; - } - - private static Holder canonicalHolder( - Holder holder, - Map> holdersByKey - ) { - String key = structureSetKey(holder); - return key == null ? holder : holdersByKey.getOrDefault(key, holder); - } - - private static StructurePlacement scalePlacement( - StructurePlacement placement, - Optional originalZone, - Optional scaledZone, - double multiplier - ) { - Vec3i locateOffset = (Vec3i) declaredFieldValue( - StructurePlacement.class, placement, Vec3i.class); - StructurePlacement.FrequencyReductionMethod reductionMethod = - (StructurePlacement.FrequencyReductionMethod) declaredFieldValue( - StructurePlacement.class, - placement, - StructurePlacement.FrequencyReductionMethod.class); - float frequency = (float) declaredFieldValue( - StructurePlacement.class, placement, float.class); - int salt = (int) declaredFieldValue( - StructurePlacement.class, placement, int.class); - - if (placement.getClass() == RandomSpreadStructurePlacement.class) { - RandomSpreadStructurePlacement randomSpread = - (RandomSpreadStructurePlacement) placement; - NativeStructureFrequencyScale scale = NativeStructureFrequencyScale.randomSpread( - frequency, randomSpread.spacing(), randomSpread.separation(), multiplier); - if (scale.frequency() == frequency - && scale.spacing() == randomSpread.spacing() - && scaledZone.equals(originalZone)) { - return placement; - } - return new RandomSpreadStructurePlacement( - locateOffset, - reductionMethod, - scale.frequency(), - salt, - scaledZone, - scale.spacing(), - randomSpread.separation(), - randomSpread.spreadType()); - } - if (placement.getClass() == ConcentricRingsStructurePlacement.class) { - ConcentricRingsStructurePlacement rings = - (ConcentricRingsStructurePlacement) placement; - float scaledFrequency = NativeStructureFrequencyScale.probability(frequency, multiplier); - if (scaledFrequency == frequency && scaledZone.equals(originalZone)) { - return placement; - } - return new ConcentricRingsStructurePlacement( - locateOffset, - reductionMethod, - scaledFrequency, - salt, - scaledZone, - rings.distance(), - rings.spread(), - rings.count(), - rings.preferredBiomes()); - } - throw new IllegalStateException("Unsupported native structure placement: " - + placement.getClass().getName()); - } - - private static Optional exclusionZone( - StructurePlacement placement - ) { - Object value = declaredFieldValue(StructurePlacement.class, placement, Optional.class); - if (value instanceof Optional optional && (optional.isEmpty() - || optional.get() instanceof StructurePlacement.ExclusionZone)) { - @SuppressWarnings("unchecked") - Optional resolved = - (Optional) optional; - return resolved; - } - throw new IllegalStateException("Could not read native structure exclusion zone from " - + placement.getClass().getName()); - } - - private static String structureSetKey(Holder holder) { - Optional> key = holder.unwrapKey(); - return key.map(resourceKey -> resourceKey.identifier().toString()).orElse(null); - } - - private static Object declaredFieldValue( - Class declaringType, - Object target, - Class fieldType - ) { - Field field = declaredField(declaringType, fieldType); - try { - return field.get(target); - } catch (IllegalAccessException error) { - throw new IllegalStateException("Could not read " + fieldType.getName() - + " field on " + declaringType.getName(), error); - } - } - - private static Field declaredField(Class declaringType, Class fieldType) { - Field match = null; - for (Field field : declaringType.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers()) || field.getType() != fieldType) { - continue; - } - if (match != null) { - throw new IllegalStateException("Ambiguous " + fieldType.getName() + " field on " - + declaringType.getName()); - } - match = field; - } - if (match == null || !match.trySetAccessible()) { - throw new IllegalStateException("Missing accessible " + fieldType.getName() - + " field on " + declaringType.getName()); - } - return match; - } - - private static final class ScaledStructureSetHolder extends Holder.Reference { - private ScaledStructureSetHolder(ResourceKey key) { - super(Type.STAND_ALONE, new HolderOwner<>() { - }, key, null); - } - - private void bind(StructureSet structureSet) { - bindValue(structureSet); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTeleportBounds.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTeleportBounds.java deleted file mode 100644 index df51cc065..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTeleportBounds.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -final class ModdedTeleportBounds { - private ModdedTeleportBounds() { - } - - static int blockCoordinate(double coordinate) { - return (int) Math.floor(coordinate); - } - - static int clampY(int minY, int maxY, int requestedY) { - int minimumY = minimumY(minY, maxY); - int maximumY = maximumY(minY, maxY); - return Math.max(minimumY, Math.min(maximumY, requestedY)); - } - - static int minimumY(int minY, int maxY) { - requireStandingSpace(minY, maxY); - return minY + 1; - } - - static int maximumY(int minY, int maxY) { - requireStandingSpace(minY, maxY); - return maxY - 2; - } - - private static void requireStandingSpace(int minY, int maxY) { - if (maxY - minY < 3) { - throw new IllegalArgumentException("Level height must provide support, feet, and head space."); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileData.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileData.java index 570474313..ecf19a614 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileData.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileData.java @@ -1,418 +1,69 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.modded; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileData; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KMap; -import art.arcane.volmlib.util.data.UnresolvedKeyLog; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.Strictness; -import net.minecraft.nbt.ByteTag; -import net.minecraft.nbt.CollectionTag; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.DoubleTag; -import net.minecraft.nbt.FloatTag; -import net.minecraft.nbt.IntTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.nbt.NumericTag; -import net.minecraft.nbt.ShortTag; -import net.minecraft.nbt.StringTag; -import net.minecraft.nbt.Tag; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.util.ProblemReporter; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BannerBlockEntity; -import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; -import net.minecraft.world.level.block.entity.SignBlockEntity; -import net.minecraft.world.level.block.entity.SpawnerBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -import net.minecraft.world.level.storage.TagValueInput; -import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; public final class ModdedTileData extends TileData { - public static final String NBT_PROPERTY = "nbt"; - static final String LEGACY_BANNER_COLOR_PROPERTY = "iris:legacy_banner_color"; - private static final int MAX_TAG_DEPTH = 64; - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().setStrictness(Strictness.LENIENT).create(); - private static final UnresolvedKeyLog SNBT_FALLBACK = new UnresolvedKeyLog("Iris tile capture SNBT fallback", 30_000L); + private final NativeTileData data; - private final byte[] raw; - private final KMap tileProperties; - private final String expectedBlockKey; - private final int legacyType; - private int hash; + private ModdedTileData(NativeTileData data) { + this.data = data; + } - ModdedTileData(byte[] raw, KMap tileProperties, String expectedBlockKey, int legacyType) { - super(); - this.raw = raw; - this.tileProperties = tileProperties == null ? new KMap<>() : tileProperties; - this.expectedBlockKey = expectedBlockKey; - this.legacyType = legacyType; + public static ModdedTileData wrap(NativeTileData data) { + return new ModdedTileData(data); } public static ModdedTileData capture(String blockKey, String snbt) throws IOException { - KMap properties = captureProperties(blockKey, snbt); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (DataOutputStream out = new DataOutputStream(bytes)) { - out.writeUTF(blockKey); - out.writeUTF(GSON.toJson(properties)); - } - return new ModdedTileData(bytes.toByteArray(), properties, normalizeBlockKey(blockKey), -1); + return wrap(NativeTileData.capture(blockKey, snbt, IrisLogging::warn)); } - /** - * Converts a captured tile to the generic map form the Bukkit side reads and writes, so an object captured on a - * mod loader still pastes on Bukkit, and keeps the original SNBT under {@value #NBT_PROPERTY} alongside it. - *

- * Both forms are stored because the map form is lossy: {@link #fromTag(Tag, int, int)} collapses ByteArray, - * IntArray and LongArray tags to a plain List, and pasting that back produces a ListTag, which Minecraft rejects - * where it expects an array - a player head's {@code profile.id} (IntArray of 4) is the common case. Modded paste - * reads the SNBT first ({@link #payload()}), so a modded capture pastes byte-identical on a mod loader; Bukkit - * still reads the map form and accepts the array-shaped members degrading to lists, as it did before. - *

- * The SNBT is dropped when the captured tag itself has a root member named {@code nbt}, since that member owns the - * map key; such a tile keeps the pre-existing lossy behaviour on both platforms. No vanilla block entity has one. - */ - @SuppressWarnings("unchecked") - private static KMap captureProperties(String blockKey, String snbt) { - KMap properties = new KMap<>(); - if (snbt != null && !snbt.isBlank()) { - try { - Object converted = fromTag(NbtUtils.snbtToStructure(snbt), 0, MAX_TAG_DEPTH); - if (converted instanceof KMap map) { - properties.putAll((KMap) map); - } - } catch (Throwable e) { - if (SNBT_FALLBACK.firstOccurrence(blockKey == null ? "" : blockKey)) { - IrisLogging.warn("Tile capture for '" + blockKey + "' kept SNBT form: " + e.getMessage()); - } - String summary = SNBT_FALLBACK.pollSummary(); - if (summary != null) { - IrisLogging.warn(summary); - } - } - } - if (snbt != null && !properties.containsKey(NBT_PROPERTY)) { - properties.put(NBT_PROPERTY, snbt); - } - return properties; + public static ModdedTileData fromProperties(NativeBlockState state, KMap properties) { + return wrap(NativeTileData.fromProperties(state, properties)); } - public static ModdedTileData fromProperties(PlatformBlockState state, KMap properties) { - String blockKey = state.placementBaseState().key(); - int bracket = blockKey.indexOf('['); - if (bracket >= 0) { - blockKey = blockKey.substring(0, bracket); - } - KMap copied = properties == null ? new KMap<>() : properties.copy(); - try { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(); - try (DataOutputStream out = new DataOutputStream(bytes)) { - out.writeUTF(blockKey); - out.writeUTF(GSON.toJson(copied)); - } - return new ModdedTileData(bytes.toByteArray(), copied, normalizeBlockKey(blockKey), -1); - } catch (IOException e) { - throw new IllegalStateException("Failed to encode modded tile data for " + blockKey, e); - } + public NativeTileData nativeData() { + return data; } - public String snbt() { - Object value = tileProperties.get(NBT_PROPERTY); - return value == null ? null : value.toString(); - } - - public boolean apply(BlockEntity blockEntity, ServerLevel level) throws Exception { - CompoundTag payload = payload(); - if (payload == null) { - return false; - } - CompoundTag merged = blockEntity.saveWithoutMetadata(level.registryAccess()); - merged.merge(payload); - blockEntity.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, level.registryAccess(), merged)); - blockEntity.setChanged(); - level.sendBlockUpdated(blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity.getBlockState(), Block.UPDATE_CLIENTS); - return true; - } - - /** - * The platform-neutral block key. The superclass reads its own {@code material} field, which a modded record - * never populates (it carries the key as {@link #expectedBlockKey} instead), so it must be answered here. - * Null for a legacy record, which identifies its target by block-entity type rather than by key - see - * {@link #isApplicable(BlockState, BlockEntity)}. - */ @Override public String getMaterialKey() { - return expectedBlockKey; - } - - public boolean isApplicable(BlockState state, BlockEntity blockEntity) { - if (expectedBlockKey != null) { - return expectedBlockKey.equals(normalizeBlockKey(BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString())); - } - return switch (legacyType) { - case 0 -> blockEntity instanceof SignBlockEntity; - case 1 -> blockEntity instanceof SpawnerBlockEntity; - case 2 -> blockEntity instanceof BannerBlockEntity; - case 3 -> blockEntity instanceof RandomizableContainerBlockEntity; - default -> false; - }; - } - - CompoundTag payload() throws Exception { - String snbt = snbt(); - if (snbt != null && !snbt.isBlank()) { - return NbtUtils.snbtToStructure(snbt); - } - Tag converted = toTag(tileProperties); - return converted instanceof CompoundTag compound ? compound : null; - } - - public BlockState adjustBlockState(BlockState state) { - Object colorValue = tileProperties.get(LEGACY_BANNER_COLOR_PROPERTY); - if (colorValue == null) { - return state; - } - Identifier currentId = BuiltInRegistries.BLOCK.getKey(state.getBlock()); - String currentPath = currentId.getPath(); - if (!currentPath.endsWith("_banner")) { - return state; - } - String suffix = currentPath.endsWith("_wall_banner") ? "_wall_banner" : "_banner"; - Identifier targetId = Identifier.tryParse("minecraft:" + colorValue + suffix); - if (targetId == null || !BuiltInRegistries.BLOCK.containsKey(targetId)) { - return state; - } - BlockState adjusted = BuiltInRegistries.BLOCK.getValue(targetId).defaultBlockState(); - for (Property property : state.getProperties()) { - if (adjusted.hasProperty(property)) { - adjusted = copyProperty(adjusted, state, property); - } - } - return adjusted; - } - - private static Tag toTag(Object value) { - if (value == null) { - return null; - } - if (value instanceof Map map) { - CompoundTag compound = new CompoundTag(); - for (Map.Entry entry : map.entrySet()) { - if (LEGACY_BANNER_COLOR_PROPERTY.equals(String.valueOf(entry.getKey()))) { - continue; - } - Tag child = toTag(entry.getValue()); - if (child != null) { - compound.put(String.valueOf(entry.getKey()), child); - } - } - return compound; - } - if (value instanceof List values) { - ListTag list = new ListTag(); - for (Object entry : values) { - Tag child = toTag(entry); - if (child != null) { - list.add(child); - } - } - return list; - } - if (value instanceof Boolean bool) { - return ByteTag.valueOf(bool); - } - if (value instanceof Byte number) { - return ByteTag.valueOf(number); - } - if (value instanceof Short number) { - return ShortTag.valueOf(number); - } - if (value instanceof Integer number) { - return IntTag.valueOf(number); - } - if (value instanceof Long number) { - return LongTag.valueOf(number); - } - if (value instanceof Float number) { - return FloatTag.valueOf(number); - } - if (value instanceof Number number) { - return DoubleTag.valueOf(number.doubleValue()); - } - return StringTag.valueOf(String.valueOf(value)); - } - - /** - * Inverse of {@link #toTag(Object)}, matching the Bukkit NMS tile converter value-for-value so both platforms - * produce the same generic map for the same block entity. - */ - private static Object fromTag(Tag tag, int depth, int maxDepth) { - if (tag == null || depth > maxDepth) { - return null; - } - if (tag instanceof CompoundTag compound) { - KMap map = new KMap<>(); - for (String key : compound.keySet()) { - Tag child = compound.get(key); - if (child == null) { - continue; - } - Object value = fromTag(child, depth + 1, maxDepth); - if (value != null) { - map.put(key, value); - } - } - return map; - } - if (tag instanceof CollectionTag collection) { - List values = new ArrayList<>(); - for (Object entry : collection) { - if (entry instanceof Tag child) { - Object value = fromTag(child, depth + 1, maxDepth); - if (value != null) { - values.add(value); - } - } else if (entry != null) { - values.add(entry); - } - } - return values; - } - if (tag instanceof NumericTag numeric) { - return numeric.box(); - } - return tag.asString().orElse(null); - } - - private static > BlockState copyProperty(BlockState target, BlockState source, Property property) { - return target.setValue(property, source.getValue(property)); - } - - private static Object deepCopy(Object value) { - if (value instanceof Map map) { - KMap copy = new KMap<>(); - for (Map.Entry entry : map.entrySet()) { - copy.put(String.valueOf(entry.getKey()), deepCopy(entry.getValue())); - } - return copy; - } - if (value instanceof List values) { - List copy = new ArrayList<>(values.size()); - for (Object entry : values) { - copy.add(deepCopy(entry)); - } - return copy; - } - return value; - } - - static String normalizeBlockKey(String blockKey) { - if (blockKey == null || blockKey.isBlank()) { - return null; - } - String normalized = blockKey.trim().toLowerCase(java.util.Locale.ROOT); - int bracket = normalized.indexOf('['); - if (bracket >= 0) { - normalized = normalized.substring(0, bracket); - } - if (!normalized.contains(":")) { - normalized = "minecraft:" + normalized.replaceAll("\\s+", "_"); - } - Identifier identifier = Identifier.tryParse(normalized); - return identifier == null ? null : identifier.toString(); + return data.getMaterialKey(); } @Override public KMap getProperties() { - return tileProperties; + return data.getProperties(); } @Override public void toBinary(DataOutputStream out) throws IOException { - out.write(raw); + data.toBinary(out); } - /** - * Identity over this record's own state. The superclass generates equals/hashCode from its - * {@code material} and {@code properties} fields, both of which stay null on a modded record, which would make - * every modded tile equal with a constant hash. Mantle tile sections are palette backed - * (16x16x16 = 4096 entries, so PaletteOrHunk picks a value-keyed DataContainer) and resolve palette ids through - * equals, so a collapsed identity writes the first tile's NBT into every other tile in the section. - *

- * The serialized {@link #raw} form is the complete identity: it carries the block key and the property JSON for a - * modern record and the consumed bytes for a legacy one. Two logically identical tiles still share one palette - * entry, which is the intended dedup. - */ @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof ModdedTileData other)) { - return false; - } - return legacyType == other.legacyType - && Objects.equals(expectedBlockKey, other.expectedBlockKey) - && Arrays.equals(raw, other.raw); + public boolean equals(Object other) { + return other instanceof ModdedTileData tile && data.equals(tile.data); } @Override public int hashCode() { - int cached = hash; - if (cached != 0) { - return cached; - } - int computed = 31 * (31 * Arrays.hashCode(raw) + Objects.hashCode(expectedBlockKey)) + legacyType; - if (computed == 0) { - computed = 1; - } - hash = computed; - return computed; + return data.hashCode(); } @Override public String toString() { - return (expectedBlockKey == null ? "legacy:" + legacyType : expectedBlockKey) + GSON.toJson(tileProperties); + return data.toString(); } - @SuppressWarnings("unchecked") @Override public TileData clone() { - return new ModdedTileData(raw == null ? null : raw.clone(), - (KMap) deepCopy(tileProperties), expectedBlockKey, legacyType); + return wrap(data.clone()); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileReader.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileReader.java deleted file mode 100644 index d526da956..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedTileReader.java +++ /dev/null @@ -1,361 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.modded; - -import art.arcane.iris.generation.block.TileData; -import art.arcane.volmlib.util.collection.KMap; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.Strictness; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.world.item.DyeColor; -import net.minecraft.world.level.block.entity.BannerPattern; - -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; -import java.util.function.Supplier; - -public final class ModdedTileReader implements TileData.TileReader { - private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().setStrictness(Strictness.LENIENT).setObjectToNumberStrategy(com.google.gson.ToNumberPolicy.LONG_OR_DOUBLE).create(); - private static final int DYE_COLOR_COUNT = 16; - private static final Identifier DEFAULT_SPAWNER_ENTITY = Identifier.parse("minecraft:pig"); - private static final Identifier DEFAULT_BANNER_PATTERN = Identifier.parse("minecraft:base"); - private static final List LEGACY_BUKKIT_ENTITY_TYPES = createLegacyBukkitEntityTypes(); - private static final List PAPER_26_2_BANNER_PATTERNS = List.of( - Identifier.parse("minecraft:small_stripes"), - Identifier.parse("minecraft:stripe_right"), - Identifier.parse("minecraft:diagonal_left"), - Identifier.parse("minecraft:stripe_middle"), - Identifier.parse("minecraft:square_bottom_right"), - Identifier.parse("minecraft:half_horizontal"), - Identifier.parse("minecraft:skull"), - Identifier.parse("minecraft:flow"), - Identifier.parse("minecraft:rhombus"), - Identifier.parse("minecraft:border"), - Identifier.parse("minecraft:gradient_up"), - Identifier.parse("minecraft:guster"), - Identifier.parse("minecraft:square_top_left"), - Identifier.parse("minecraft:triangle_bottom"), - Identifier.parse("minecraft:triangles_bottom"), - Identifier.parse("minecraft:half_horizontal_bottom"), - Identifier.parse("minecraft:gradient"), - Identifier.parse("minecraft:triangle_top"), - Identifier.parse("minecraft:piglin"), - Identifier.parse("minecraft:stripe_center"), - Identifier.parse("minecraft:circle"), - Identifier.parse("minecraft:stripe_left"), - Identifier.parse("minecraft:stripe_bottom"), - Identifier.parse("minecraft:square_top_right"), - Identifier.parse("minecraft:curly_border"), - Identifier.parse("minecraft:creeper"), - Identifier.parse("minecraft:square_bottom_left"), - Identifier.parse("minecraft:triangles_top"), - Identifier.parse("minecraft:half_vertical"), - Identifier.parse("minecraft:mojang"), - Identifier.parse("minecraft:diagonal_right"), - Identifier.parse("minecraft:cross"), - Identifier.parse("minecraft:straight_cross"), - Identifier.parse("minecraft:bricks"), - Identifier.parse("minecraft:diagonal_up_left"), - Identifier.parse("minecraft:base"), - Identifier.parse("minecraft:flower"), - Identifier.parse("minecraft:stripe_downleft"), - Identifier.parse("minecraft:diagonal_up_right"), - Identifier.parse("minecraft:stripe_downright"), - Identifier.parse("minecraft:stripe_top"), - Identifier.parse("minecraft:globe"), - Identifier.parse("minecraft:half_vertical_right")); - - private final Supplier server; - - public ModdedTileReader(Supplier server) { - this.server = server; - } - - private static final class ReplayInputStream extends InputStream { - private final InputStream source; - private byte[] buffer = new byte[256]; - private int size = 0; - private int position = 0; - private int marked = 0; - - private ReplayInputStream(InputStream source) { - this.source = source; - } - - @Override - public int read() throws IOException { - if (position < size) { - int value = buffer[position] & 0xFF; - position++; - return value; - } - int value = source.read(); - if (value < 0) { - return value; - } - if (size == buffer.length) { - buffer = Arrays.copyOf(buffer, buffer.length * 2); - } - buffer[size] = (byte) value; - size++; - position++; - return value; - } - - @Override - public boolean markSupported() { - return true; - } - - @Override - public synchronized void mark(int readLimit) { - marked = position; - } - - @Override - public synchronized void reset() { - position = marked; - } - - private void rewind() { - position = 0; - } - - private int position() { - return position; - } - - private byte[] consumed() { - return Arrays.copyOf(buffer, position); - } - } - - @Override - public TileData read(DataInputStream in) throws IOException { - if (!in.markSupported()) { - throw new IOException("Mark not supported"); - } - in.mark(Integer.MAX_VALUE); - ReplayInputStream replay = new ReplayInputStream(in); - DataInputStream din = new DataInputStream(replay); - try { - return parse(din, replay); - } finally { - in.reset(); - in.skipNBytes(replay.position()); - in.mark(0); - } - } - - private TileData parse(DataInputStream din, ReplayInputStream replay) throws IOException { - try { - String materialKey = din.readUTF(); - boolean materialMatched = matchMaterial(materialKey); - String json = din.readUTF(); - KMap properties = kmapFromJson(json); - if (!materialMatched) { - throw new NullPointerException("material is marked non-null but is null"); - } - if (properties == null) { - throw new NullPointerException("properties is marked non-null but is null"); - } - return new ModdedTileData(replay.consumed(), properties, - ModdedTileData.normalizeBlockKey(materialKey), -1); - } catch (Throwable e) { - replay.rewind(); - return parseLegacy(din, replay); - } - } - - @SuppressWarnings("unchecked") - private static KMap kmapFromJson(String json) { - return GSON.fromJson(json, KMap.class); - } - - private TileData parseLegacy(DataInputStream din, ReplayInputStream replay) throws IOException { - int id = din.readShort(); - String expectedBlockKey = null; - KMap properties = switch (id) { - case 0 -> readSign(din); - case 1 -> readSpawner(din, replay); - case 2 -> readBanner(din, replay); - case 3 -> { - expectedBlockKey = ModdedTileData.normalizeBlockKey(din.readUTF()); - yield readLootable(din); - } - default -> throw new IOException("Unknown tile type: " + id); - }; - return new ModdedTileData(replay.consumed(), properties, expectedBlockKey, id); - } - - private static KMap readSign(DataInputStream din) throws IOException { - List messages = List.of(din.readUTF(), din.readUTF(), din.readUTF(), din.readUTF()); - byte dye = din.readByte(); - if (dye < 0 || dye >= DYE_COLOR_COUNT) { - throw new ArrayIndexOutOfBoundsException("Index " + dye + " out of bounds for length " + DYE_COLOR_COUNT); - } - KMap text = new KMap<>(); - text.put("messages", messages); - text.put("color", DyeColor.byId(dye).getName()); - text.put("has_glowing_text", false); - KMap properties = new KMap<>(); - properties.put("front_text", text); - properties.put("back_text", text.copy()); - return properties; - } - - private static KMap readSpawner(DataInputStream din, ReplayInputStream replay) throws IOException { - Identifier entityId = null; - replay.mark(Integer.MAX_VALUE); - - try { - String keyString = din.readUTF(); - Identifier key = Identifier.tryParse(keyString); - if (key != null && BuiltInRegistries.ENTITY_TYPE.containsKey(key)) { - entityId = key; - } else { - replay.reset(); - } - } catch (Throwable ignored) { - replay.reset(); - } - - if (entityId == null) { - entityId = legacySpawnerEntityId(din.readShort()); - } - KMap entity = new KMap<>(); - entity.put("id", entityId.toString()); - KMap spawnData = new KMap<>(); - spawnData.put("entity", entity); - KMap properties = new KMap<>(); - properties.put("SpawnData", spawnData); - return properties; - } - - static Identifier legacySpawnerEntityId(int legacyOrdinal) { - if (legacyOrdinal < 0 || legacyOrdinal >= LEGACY_BUKKIT_ENTITY_TYPES.size()) { - return DEFAULT_SPAWNER_ENTITY; - } - return LEGACY_BUKKIT_ENTITY_TYPES.get(legacyOrdinal); - } - - private static List createLegacyBukkitEntityTypes() { - List entityTypes = new ArrayList<>(); - for (Identifier key : BuiltInRegistries.ENTITY_TYPE.keySet()) { - if (Identifier.DEFAULT_NAMESPACE.equals(key.getNamespace())) { - entityTypes.add(key); - } - } - entityTypes.sort(Identifier::compareTo); - return List.copyOf(entityTypes); - } - - private KMap readBanner(DataInputStream din, ReplayInputStream replay) throws IOException { - int baseColor = din.readUnsignedByte(); - int listSize = din.readUnsignedByte(); - replay.mark(Integer.MAX_VALUE); - - List layers = new ArrayList<>(listSize); - try { - for (int i = 0; i < listSize; i++) { - int color = din.readUnsignedByte(); - Identifier patternKey = Identifier.tryParse(din.readUTF()); - if (patternKey == null || !bannerPatternExists(patternKey)) { - throw new IOException("Unknown banner pattern key"); - } - layers.add(bannerLayer(patternKey, color)); - } - } catch (Throwable ignored) { - replay.reset(); - layers.clear(); - } - - if (layers.isEmpty() && listSize > 0) { - for (int i = 0; i < listSize; i++) { - int color = din.readUnsignedByte(); - int pattern = din.readUnsignedByte(); - layers.add(bannerLayer(legacyBannerPatternKey(pattern), color)); - } - } - KMap properties = new KMap<>(); - properties.put("patterns", layers); - properties.put(ModdedTileData.LEGACY_BANNER_COLOR_PROPERTY, DyeColor.byId(baseColor).getName()); - return properties; - } - - private static KMap bannerLayer(Identifier pattern, int color) { - KMap layer = new KMap<>(); - layer.put("pattern", pattern.toString()); - layer.put("color", DyeColor.byId(color).getName()); - return layer; - } - - static Identifier legacyBannerPatternKey(int legacyOrdinal) { - if (legacyOrdinal < 0 || legacyOrdinal >= PAPER_26_2_BANNER_PATTERNS.size()) { - return DEFAULT_BANNER_PATTERN; - } - return PAPER_26_2_BANNER_PATTERNS.get(legacyOrdinal); - } - - private boolean bannerPatternExists(Identifier key) { - MinecraftServer instance = server.get(); - if (instance == null) { - return true; - } - Registry registry = instance.registryAccess().lookupOrThrow(Registries.BANNER_PATTERN); - return registry.containsKey(key); - } - - private static KMap readLootable(DataInputStream din) throws IOException { - String lootTable = din.readUTF(); - long seed = din.readLong(); - KMap properties = new KMap<>(); - if (!lootTable.isBlank()) { - properties.put("LootTable", lootTable); - properties.put("LootTableSeed", seed); - } - return properties; - } - - private static boolean matchMaterial(String name) { - String filtered = name.trim().toLowerCase(Locale.ROOT); - int bracket = filtered.indexOf('['); - if (bracket >= 0) { - filtered = filtered.substring(0, bracket); - } - if (!filtered.contains(":")) { - filtered = "minecraft:" + filtered.replaceAll("\\s+", "_"); - } - Identifier identifier = Identifier.tryParse(filtered); - if (identifier == null) { - return false; - } - return BuiltInRegistries.BLOCK.containsKey(identifier); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldCheck.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldCheck.java index 3e17f4790..06a56fd68 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldCheck.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldCheck.java @@ -18,20 +18,15 @@ package art.arcane.iris.modded; + import art.arcane.iris.modded.WorldCheckStructureAudit.NativeStructureGate; import art.arcane.iris.modded.WorldCheckStructureAudit.PendingVillagePoi; -import art.arcane.iris.modded.WorldCheckStructureAudit.PoiAudit; -import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.Heightmap; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.PoiAudit; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldInspection; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; @@ -51,10 +46,10 @@ public final class ModdedWorldCheck { private static final long SERVER_WAIT_TIMEOUT_MILLIS = 600000L; private static final long SERVER_WAIT_INTERVAL_MILLIS = 250L; private static final long SERVER_TASK_TIMEOUT_MILLIS = 900000L; - // halt, not exit: awaitStopAndExit already waited for MinecraftServer.halt(true), and exit() would run the + // halt, not exit: awaitStopAndExit already waited for NativeModdedServer.halt(true), and exit() would run the // shutdown hooks and block behind the server thread it just stopped, so a finished check could hang forever. private static final ProcessExit PROCESS_EXIT = Runtime.getRuntime()::halt; - private static volatile MinecraftServer startedServer; + private static volatile NativeModdedServer startedServer; private ModdedWorldCheck() { } @@ -63,12 +58,12 @@ public final class ModdedWorldCheck { coordinatorThread(() -> waitAndRun(PROCESS_EXIT)).start(); } - public static void serverStarted(MinecraftServer server) { + public static void serverStarted(NativeModdedServer server) { startedServer = server; } - public static void serverStopped(MinecraftServer server) { - if (startedServer == server) { + public static void serverStopped(NativeModdedServer server) { + if (startedServer != null && startedServer.sameServer(server)) { startedServer = null; } } @@ -83,12 +78,12 @@ public final class ModdedWorldCheck { private static void waitAndRun(ProcessExit processExit) { long start = System.currentTimeMillis(); - MinecraftServer server = null; + NativeModdedServer server = null; int exitCode = EXIT_FAILURE; AtomicBoolean stopRequested = new AtomicBoolean(false); try { while (System.currentTimeMillis() - start < SERVER_WAIT_TIMEOUT_MILLIS) { - MinecraftServer candidate = startedServer; + NativeModdedServer candidate = startedServer; if (candidate != null) { server = candidate; break; @@ -101,7 +96,7 @@ public final class ModdedWorldCheck { return; } - MinecraftServer serverRef = server; + NativeModdedServer serverRef = server; WorldCheckPreparation preparation = serverRef.submit(() -> run(serverRef)) .get(SERVER_TASK_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); exitCode = serverRef.submit(() -> runAndRequestStop( @@ -121,7 +116,7 @@ public final class ModdedWorldCheck { } finally { int resultCode = exitCode; ModdedIrisLog.info("[worldcheck] shutting down dev server (result={})", resultCode == EXIT_PASS ? "PASS" : "FAIL"); - MinecraftServer serverRef = server; + NativeModdedServer serverRef = server; if (serverRef != null && stopRequested.get()) { awaitStopAndExit(() -> serverRef.halt(true), resultCode, processExit); } else { @@ -165,19 +160,19 @@ public final class ModdedWorldCheck { processExit.exit(exitCode); } - private static WorldCheckPreparation run(MinecraftServer server) { - ServerLevel level = targetLevel(server); + private static WorldCheckPreparation run(NativeModdedServer server) { + NativeWorld level = targetLevel(server); if (level == null) { ModdedIrisLog.error("[worldcheck] no Iris dimension is loaded"); return new WorldCheckPreparation(false, false, false, false, new NativeStructureGate(false, 0, false, null)); } - String levelId = level.dimension().identifier().toString(); - String generatorClass = level.getChunkSource().getGenerator().getClass().getName(); + String levelId = level.name(); + NativeWorldInspection inspection = new NativeWorldInspection(level); + String generatorClass = inspection.generatorClass(); ModdedIrisLog.info("[worldcheck] {} generator: {}", levelId, generatorClass); - IrisModdedChunkGenerator generator = level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator iris - ? iris : null; + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); boolean irisGenerator = generator != null; if (!irisGenerator) { ModdedIrisLog.error("[worldcheck] {} is NOT using IrisModdedChunkGenerator", levelId); @@ -185,21 +180,19 @@ public final class ModdedWorldCheck { boolean dimensionTypeOk = generator != null && WorldCheckDimensionContract.checkDimensionType(level, generator); - BlockPos spawn = level.getRespawnData().pos(); - ModdedIrisLog.info("[worldcheck] spawn: {} {} {} (minY={} height={})", spawn.getX(), spawn.getY(), spawn.getZ(), level.getMinY(), level.getHeight()); + NativeBlockPoint spawn = inspection.spawn(); + ModdedIrisLog.info("[worldcheck] spawn: {} {} {} (minY={} height={})", spawn.x(), spawn.y(), spawn.z(), level.minHeight(), (level.maxHeight() - level.minHeight())); MessageDigest digest = WorldCheckPredicates.sha256(); List samples = new ArrayList<>(); Set surfaceKeys = new LinkedHashSet<>(); for (int dx = 0; dx < 4; dx++) { for (int dz = 0; dz < 4; dz++) { - int x = spawn.getX() + (dx - 2) * 16 + 8; - int z = spawn.getZ() + (dz - 2) * 16 + 8; - level.getChunk(x >> 4, z >> 4); - int y = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z); - BlockState surface = level.getBlockState(new BlockPos(x, y - 1, z)); - String key = BuiltInRegistries.BLOCK.getKey(surface.getBlock()).toString(); - String line = x + " " + (y - 1) + " " + z + " " + key; + int x = spawn.x() + (dx - 2) * 16 + 8; + int z = spawn.z() + (dz - 2) * 16 + 8; + NativeWorldInspection.Surface surface = inspection.surface(x, z); + String key = surface.block(); + String line = x + " " + surface.y() + " " + z + " " + key; samples.add(line); surfaceKeys.add(key); digest.update((line + "\n").getBytes(StandardCharsets.UTF_8)); @@ -212,23 +205,11 @@ public final class ModdedWorldCheck { ModdedIrisLog.info("[worldcheck] surface digest: {} ({} columns, {} distinct surface blocks: {})", HexFormat.of().formatHex(digest.digest()).substring(0, 12), samples.size(), surfaceKeys.size(), surfaceKeys); - ChunkAccess zeroChunk = level.getChunk(0, 0); - int nonEmptySections = 0; - for (LevelChunkSection section : zeroChunk.getSections()) { - if (!section.hasOnlyAir()) { - nonEmptySections++; - } - } - Set columnKeys = new LinkedHashSet<>(); - int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, 8, 8); - for (int y = level.getMinY(); y < surfaceY; y += 16) { - BlockState state = zeroChunk.getBlockState(new BlockPos(8, y, 8)); - if (!state.isAir()) { - columnKeys.add(BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString()); - } - } + NativeWorldInspection.Column column = inspection.column(8, 8, 16); + int nonEmptySections = column.nonEmptySections(); + Set columnKeys = column.blocks(); ModdedIrisLog.info("[worldcheck] chunk 0,0: {} non-empty sections of {}; column blocks at (8,*,8): {}", - nonEmptySections, zeroChunk.getSections().length, columnKeys); + nonEmptySections, column.totalSections(), columnKeys); boolean sectionsOk = nonEmptySections >= 4; boolean varietyOk = columnKeys.size() >= 2 || surfaceKeys.size() >= 2; @@ -282,13 +263,15 @@ public final class ModdedWorldCheck { return pass; } - private static ServerLevel targetLevel(MinecraftServer server) { + private static NativeWorld targetLevel(NativeModdedServer server) { String target = System.getProperty("iris.worldcheck.dimension"); if (target != null && !target.isBlank()) { - Identifier identifier = Identifier.tryParse(target.trim()); - ServerLevel requested = identifier == null - ? null - : ModdedServerLevels.level(server, ResourceKey.create(Registries.DIMENSION, identifier)); + NativeWorld requested; + try { + requested = server.world(target.trim()); + } catch (IllegalArgumentException invalidKey) { + requested = null; + } if (requested != null) { return requested; } @@ -296,8 +279,8 @@ public final class ModdedWorldCheck { return null; } - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator) { + for (NativeWorld level : server.worlds()) { + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) != null) { return level; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldEngines.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldEngines.java index 2dd11192d..fa7fcf731 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldEngines.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldEngines.java @@ -18,6 +18,12 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldDimensions; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; + import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.PackValidationRegistry; @@ -37,9 +43,6 @@ import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; import art.arcane.iris.world.IrisWorld; import art.arcane.iris.modded.command.ModdedGuiHost; import art.arcane.iris.spi.IrisPlatforms; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelResource; import java.io.File; import java.io.IOException; @@ -50,13 +53,13 @@ import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; public final class ModdedWorldEngines { - private static final ConcurrentHashMap ENGINES = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap ENGINES = new ConcurrentHashMap<>(); private ModdedWorldEngines() { } public static Engine get( - ServerLevel level, + NativeWorld level, String pack, String dimensionKey, long seedOverride, @@ -67,7 +70,7 @@ public final class ModdedWorldEngines { return existing; } return ENGINES.computeIfAbsent(level, - (ServerLevel l) -> createAndApplyWorldBoundary( + (NativeWorld l) -> createAndApplyWorldBoundary( l, pack, dimensionKey, @@ -80,17 +83,17 @@ public final class ModdedWorldEngines { return new ArrayList<>(ENGINES.values()); } - public static void evict(ServerLevel level) { + public static void evict(NativeWorld level) { try { evictOrThrow(level); } catch (Throwable e) { - ModdedIrisLog.error("Iris engine evict close failed for {}", level.dimension().identifier(), e); + ModdedIrisLog.error("Iris engine evict close failed for {}", level.name(), e); } } - static void evictOrThrow(ServerLevel level) { + static void evictOrThrow(NativeWorld level) { Engine[] removed = new Engine[1]; - ENGINES.computeIfPresent(level, (ServerLevel ignored, Engine current) -> { + ENGINES.computeIfPresent(level, (NativeWorld ignored, Engine current) -> { close(current); removed[0] = current; return null; @@ -98,13 +101,13 @@ public final class ModdedWorldEngines { if (removed[0] == null) { return; } - // The GUI host holds strong Engine/ServerLevel references with no other remove path. + // The GUI host holds strong Engine/NativeWorld references with no other remove path. ModdedGuiHost.unbind(removed[0]); - ModdedIrisLog.info("Iris engine evicted for {}", level.dimension().identifier()); + ModdedIrisLog.info("Iris engine evicted for {}", level.name()); } static Engine prepareReplacement( - ServerLevel level, + NativeWorld level, String pack, String dimensionKey, long seedOverride, @@ -113,10 +116,10 @@ public final class ModdedWorldEngines { return create(level, pack, dimensionKey, seedOverride, generationMode); } - static void installReplacement(ServerLevel level, Engine replacement) { - ServerLevel activeLevel = Objects.requireNonNull(level); + static void installReplacement(NativeWorld level, Engine replacement) { + NativeWorld activeLevel = Objects.requireNonNull(level); Engine activeReplacement = Objects.requireNonNull(replacement); - ENGINES.compute(activeLevel, (ServerLevel ignored, Engine current) -> { + ENGINES.compute(activeLevel, (NativeWorld ignored, Engine current) -> { if (current != null && current != activeReplacement) { close(current); ModdedGuiHost.unbind(current); @@ -127,12 +130,12 @@ public final class ModdedWorldEngines { static void closeUnregistered(Engine engine) { close(engine); - ENGINES.entrySet().removeIf((Map.Entry entry) -> entry.getValue() == engine); + ENGINES.entrySet().removeIf((Map.Entry entry) -> entry.getValue() == engine); ModdedGuiHost.unbind(engine); } private static Engine createAndApplyWorldBoundary( - ServerLevel level, + NativeWorld level, String pack, String dimensionKey, long seedOverride, @@ -155,19 +158,19 @@ public final class ModdedWorldEngines { throw runtimeException; } throw new IllegalStateException("Failed to apply Iris world boundary for '" - + level.dimension().identifier() + "'.", failure); + + level.name() + "'.", failure); } } private static Engine create( - ServerLevel level, + NativeWorld level, String pack, String dimensionKey, long seedOverride, ModdedGenerationMode generationMode ) { ModdedEngineBootstrap.bind(); - long seed = seedOverride == Long.MIN_VALUE ? level.getSeed() : seedOverride; + long seed = seedOverride == Long.MIN_VALUE ? level.seed() : seedOverride; ModdedGenerationMode requiredMode = Objects.requireNonNull(generationMode, "generationMode"); if (requiredMode == ModdedGenerationMode.TRANSIENT_STUDIO) { return createTransientStudioEngine(level, pack, dimensionKey, seed); @@ -182,12 +185,12 @@ public final class ModdedWorldEngines { return createHistoryEngine(level, seed, active); } catch (IOException failure) { throw new IllegalStateException("Iris generation history could not initialize runtime routing for '" - + level.dimension().identifier() + "'.", failure); + + level.name() + "'.", failure); } } private static Engine createHistoryEngine( - ServerLevel level, + NativeWorld level, long seed, ModdedGenerationHistoryStorage.ActivePack opened ) throws IOException { @@ -220,7 +223,7 @@ public final class ModdedWorldEngines { requireReady(candidate, level, ModdedGenerationMode.PERSISTENT_RESTORE); ready = true; ModdedIrisLog.info("Iris engine up for {}: pack={} dim={} seed={} height={}..{} activation={}", - level.dimension().identifier(), + level.name(), candidate.getData().getDataFolder().getAbsolutePath(), candidate.getDimension().getLoadKey(), seed, @@ -237,7 +240,7 @@ public final class ModdedWorldEngines { } private static IrisEngine buildEngine( - ServerLevel level, + NativeWorld level, long seed, ModdedGenerationHistoryStorage.ActivePack active ) throws IOException { @@ -255,10 +258,7 @@ public final class ModdedWorldEngines { } validateDimensionContract(active.dimensionContract(), dimension, level); - File worldFolder = DimensionType.getStorageFolder( - level.dimension(), - level.getServer().getWorldPath(LevelResource.ROOT) - ).toFile(); + File worldFolder = NativeModdedServer.forWorld(level).dimensionFolder(level.name()).toFile(); IrisWorld world = buildWorld(level, seed, dimension, worldFolder); GenerationHistory history = active.history(); GenerationActivation activation = history.activeActivation(); @@ -282,7 +282,7 @@ public final class ModdedWorldEngines { } private static IrisEngine createTransientStudioEngine( - ServerLevel level, + NativeWorld level, String pack, String dimensionKey, long seed @@ -299,10 +299,7 @@ public final class ModdedWorldEngines { throw new IllegalStateException("Transient Iris Studio pack does not contain dimension '" + dimensionKey + "'."); } - File worldFolder = DimensionType.getStorageFolder( - level.dimension(), - level.getServer().getWorldPath(LevelResource.ROOT) - ).toFile(); + File worldFolder = NativeModdedServer.forWorld(level).dimensionFolder(level.name()).toFile(); IrisWorld world = buildWorld(level, seed, dimension, worldFolder); engine = new IrisEngine( new EngineTarget(world, dimension, data), @@ -312,7 +309,7 @@ public final class ModdedWorldEngines { requireReady(engine, level, ModdedGenerationMode.TRANSIENT_STUDIO); ready = true; ModdedIrisLog.info("Iris transient Studio engine up for {}: pack={} dim={} seed={} height={}..{}", - level.dimension().identifier(), + level.name(), packDir.getAbsolutePath(), dimension.getLoadKey(), seed, @@ -329,47 +326,47 @@ public final class ModdedWorldEngines { } private static IrisWorld buildWorld( - ServerLevel level, + NativeWorld level, long seed, IrisDimension dimension, File worldFolder ) { return IrisWorld.builder() - .platformIdentity(level.dimension().identifier().toString()) - .name(level.dimension().identifier().toString().replace(':', '_')) + .platformIdentity(level.name()) + .name(level.name().replace(':', '_')) .seed(seed) .worldFolder(worldFolder) .minHeight(dimension.getMinHeight()) .maxHeight(dimension.getMaxHeight()) - .platformWorld(new ModdedPlatformWorld(level)) + .platformWorld(level) .build(); } private static void requireReady( IrisEngine engine, - ServerLevel level, + NativeWorld level, ModdedGenerationMode generationMode ) { if (engine.isClosed() || engine.getComplex() == null) { - throw new IllegalStateException("Iris engine for " + level.dimension().identifier() + throw new IllegalStateException("Iris engine for " + level.name() + " did not initialize a ready runtime"); } if (generationMode.historyRequired() && engine.getGenerationHistoryRuntimeRouter().isEmpty()) { - throw new IllegalStateException("Persistent Iris engine for " + level.dimension().identifier() + throw new IllegalStateException("Persistent Iris engine for " + level.name() + " did not initialize a generation-history runtime router"); } if (!generationMode.historyRequired() && (!engine.isStudio() || engine.getGenerationHistoryRuntimeRouter().isPresent())) { throw new IllegalStateException("Transient Iris Studio engine for " - + level.dimension().identifier() + " has an invalid history mode"); + + level.name() + " has an invalid history mode"); } } private static void validateDimensionContract( GenerationEpoch.DimensionContract recorded, IrisDimension dimension, - ServerLevel level + NativeWorld level ) { GenerationEpoch.DimensionContract loaded = GenerationEpochContractFactory.create( dimension, @@ -378,12 +375,10 @@ public final class ModdedWorldEngines { ); if (!recorded.equals(loaded)) { throw new IllegalStateException("Immutable Iris dimension contract changed for '" - + level.dimension().identifier() + "'."); + + level.name() + "'."); } - DimensionType actualType = level.dimensionType(); - String actualTypeKey = level.dimensionTypeRegistration().unwrapKey() - .map(key -> key.identifier().toString()) - .orElse(""); + NativeWorldDimensions.Settings actualType = NativeWorldDimensions.settings(level); + String actualTypeKey = "inline".equals(actualType.key()) ? "" : actualType.key(); IrisDimensionRuntimeContract expected = new IrisDimensionRuntimeContract( recorded.dimensionTypeKey(), recorded.minHeight(), @@ -391,12 +386,12 @@ public final class ModdedWorldEngines { recorded.logicalHeight()); IrisDimensionRuntimeContract actual = new IrisDimensionRuntimeContract( actualTypeKey, - actualType.minY(), - actualType.height(), + actualType.minimumHeight(), + (actualType.maximumHeight() - actualType.minimumHeight()), actualType.logicalHeight()); - String runtimeName = "Modded level '" + level.dimension().identifier() + "'"; + String runtimeName = "Modded level '" + level.name() + "'"; expected.requireExact(runtimeName, actual); - expected.requireHeight(runtimeName, level.getMinY(), level.getHeight()); + expected.requireHeight(runtimeName, level.minHeight(), (level.maxHeight() - level.minHeight())); } public static File packFolder(String pack) { @@ -425,26 +420,27 @@ public final class ModdedWorldEngines { public static void shutdown() { Throwable failure = null; - for (Map.Entry entry : new ArrayList<>(ENGINES.entrySet())) { - ServerLevel level = entry.getKey(); + for (Map.Entry entry : new ArrayList<>(ENGINES.entrySet())) { + NativeWorld level = entry.getKey(); Engine engine = entry.getValue(); try { // Latch the generator's unloading flag BEFORE closing (unbindEngine sets it, // then evicts): chunk-system drain work running after this stage would // otherwise see a closed engine and silently rebuild a fresh engine + Mantle // that no teardown stage ever closes, writing plates after the final save. - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (generator != null) { generator.unbindEngine(level); } else { close(engine); if (!ENGINES.remove(level, engine) && ENGINES.containsKey(level)) { throw new IllegalStateException("Iris engine mapping changed during shutdown for " - + level.dimension().identifier()); + + level.name()); } } - ModdedIrisLog.info("Iris engine closed for {}", level.dimension().identifier()); + ModdedIrisLog.info("Iris engine closed for {}", level.name()); } catch (Throwable e) { - ModdedIrisLog.error("Iris engine close failed for {}", level.dimension().identifier(), e); + ModdedIrisLog.error("Iris engine close failed for {}", level.name(), e); if (failure == null) { failure = e; } else if (e != failure) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldManager.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldManager.java index 9fffc8245..e55391bf1 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldManager.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedWorldManager.java @@ -18,6 +18,14 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnQueries; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldMaintenance; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockProperties; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.studio.view.PregeneratorJob; import art.arcane.iris.generation.runtime.IrisComplex; @@ -46,19 +54,6 @@ import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.matter.Matter; import art.arcane.volmlib.util.matter.MatterMarker; import art.arcane.volmlib.util.matter.slices.MarkerMatter; -import it.unimi.dsi.fastutil.objects.Object2IntMap; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.NaturalSpawner; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.phys.AABB; import java.util.HashSet; import java.util.List; @@ -81,6 +76,7 @@ public final class ModdedWorldManager implements EngineWorldManager { private static final int AMBIENT_CHUNK_SAMPLE = 64; private static final long INITIAL_RECOVERY_INTERVAL_MS = 1_000L; + private volatile NativeEntityRuntime entityRuntime; private final Engine engine; private final InitialSpawnQueue initialSpawnQueue; private final Set mantleWarmups; @@ -137,11 +133,11 @@ public final class ModdedWorldManager implements EngineWorldManager { moddedWorldManager.initialSpawnQueue.offer(pack(chunkX, chunkZ)); } - public void serverTick(ServerLevel level) { + public void serverTick(NativeWorld level) { EngineLifecycleTasks.run(engine, "modded_world_manager_tick", () -> runServerTick(level)); } - private void runServerTick(ServerLevel level) { + private void runServerTick(NativeWorld level) { if (closed || engine.isClosed() || engine.getMantle().getMantle().isClosed()) { return; } @@ -156,7 +152,7 @@ public final class ModdedWorldManager implements EngineWorldManager { ambientTick(level); } - private void recoverLoadedInitialSpawns(ServerLevel level) { + private void recoverLoadedInitialSpawns(NativeWorld level) { if (!markerSystemEnabled() && !ambientSystemEnabled()) { return; } @@ -167,23 +163,23 @@ public final class ModdedWorldManager implements EngineWorldManager { lastInitialRecoveryAt = now; Set candidates = new HashSet<>(); - for (ServerPlayer player : level.players()) { - int centerX = player.blockPosition().getX() >> 4; - int centerZ = player.blockPosition().getZ() >> 4; + new NativeWorldMaintenance(level).forEachPlayerPosition(player -> { + int centerX = player.x() >> 4; + int centerZ = player.z() >> 4; for (int dx = -1; dx <= 1; dx++) { for (int dz = -1; dz <= 1; dz++) { candidates.add(pack(centerX + dx, centerZ + dz)); } } - } - candidates.addAll(level.getForceLoadedChunks()); + }); + new NativeWorldMaintenance(level).addForcedChunks(candidates); Mantle mantle = engine.getMantle().getMantle(); int recovered = 0; for (long key : candidates) { int chunkX = unpackX(key); int chunkZ = unpackZ(key); - if (level.getChunkSource().getChunkNow(chunkX, chunkZ) == null) { + if (!level.isChunkLoaded(chunkX, chunkZ)) { continue; } if (mantle.isChunkLoaded(chunkX, chunkZ) && mantle.hasFlag(chunkX, chunkZ, INITIAL_SPAWN_COMPLETION_FLAG)) { @@ -195,7 +191,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void drainInitialSpawns(ServerLevel level) { + private void drainInitialSpawns(NativeWorld level) { if (initialSpawnQueue.isEmpty()) { return; } @@ -214,7 +210,7 @@ public final class ModdedWorldManager implements EngineWorldManager { int chunkZ = unpackZ(key); boolean retry = false; try { - if (level.getChunkSource().getChunkNow(chunkX, chunkZ) == null) { + if (!level.isChunkLoaded(chunkX, chunkZ)) { retry = true; continue; } @@ -237,8 +233,8 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - boolean initialSpawnChunk(ServerLevel level, int chunkX, int chunkZ) { - if (!ModdedEntitySpawner.chunksSafe(level, chunkX, chunkZ)) { + boolean initialSpawnChunk(NativeWorld level, int chunkX, int chunkZ) { + if (!ModdedEntitySpawner.chunksSafe(entityRuntime(level), chunkX, chunkZ)) { return false; } Mantle mantle = engine.getMantle().getMantle(); @@ -267,7 +263,7 @@ public final class ModdedWorldManager implements EngineWorldManager { return true; } - private void scheduleInitialFollowUp(ServerLevel level, int chunkX, int chunkZ, BiomeEnvironment environment) { + private void scheduleInitialFollowUp(NativeWorld level, int chunkX, int chunkZ, BiomeEnvironment environment) { ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull(); if (scheduler == null) { IrisLogging.error("Iris could not schedule the initial entity-spawn follow-up because the modded scheduler is unavailable."); @@ -281,12 +277,12 @@ public final class ModdedWorldManager implements EngineWorldManager { RNG.r.i(5, 200)); } - private void runInitialFollowUp(ServerLevel level, int chunkX, int chunkZ, BiomeEnvironment environment) { + private void runInitialFollowUp(NativeWorld level, int chunkX, int chunkZ, BiomeEnvironment environment) { Mantle mantle = engine.getMantle().getMantle(); if (closed || engine.isClosed() || mantle.isClosed() || !isEntitySpawningEnabledForCurrentWorld()) { return; } - if (level.getChunkSource().getChunkNow(chunkX, chunkZ) == null || !mantle.isChunkLoaded(chunkX, chunkZ)) { + if (!level.isChunkLoaded(chunkX, chunkZ) || !mantle.isChunkLoaded(chunkX, chunkZ)) { return; } if (engine.getComplex() == null) { @@ -333,7 +329,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void ambientTick(ServerLevel level) { + private void ambientTick(NativeWorld level) { long now = System.currentTimeMillis(); long interval = IrisSettings.get().getWorld().getAsyncTickIntervalMS(); if (now - lastAmbientAt < interval) { @@ -344,7 +340,7 @@ public final class ModdedWorldManager implements EngineWorldManager { if (!markerSystemEnabled() && !ambientSystemEnabled()) { return; } - if (level.players().isEmpty()) { + if (!new NativeWorldMaintenance(level).hasPlayers()) { return; } @@ -375,8 +371,8 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void ambientSpawnChunk(ServerLevel level, int chunkX, int chunkZ) { - if (!ModdedEntitySpawner.chunksSafe(level, chunkX, chunkZ)) { + private void ambientSpawnChunk(NativeWorld level, int chunkX, int chunkZ) { + if (!ModdedEntitySpawner.chunksSafe(entityRuntime(level), chunkX, chunkZ)) { return; } Mantle mantle = engine.getMantle().getMantle(); @@ -397,7 +393,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private List prepareMarkerSpawns(ServerLevel level, int chunkX, int chunkZ, MantleChunk chunk) { + private List prepareMarkerSpawns(NativeWorld level, int chunkX, int chunkZ, MantleChunk chunk) { int minHeight = engine.getWorld().minHeight(); KList obstructed = new KList<>(); List prepared = new ArrayList<>(); @@ -459,7 +455,7 @@ public final class ModdedWorldManager implements EngineWorldManager { return spawners; } - private void spawnPreparedMarkers(ServerLevel level, List markers, boolean initial) { + private void spawnPreparedMarkers(NativeWorld level, List markers, boolean initial) { for (PreparedMarkerSpawn marker : markers) { try (BiomeEnvironment.Scope ignored = engine.openBiomeEnvironmentScope(marker.environment())) { spawnFromSpawner(level, marker.position(), marker.spawner(), initial); @@ -467,7 +463,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void spawnFromSpawner(ServerLevel level, IrisPosition position, IrisSpawner spawner, boolean initial) { + private void spawnFromSpawner(NativeWorld level, IrisPosition position, IrisSpawner spawner, boolean initial) { KList spawns = initial ? spawner.getInitialSpawns() : spawner.getSpawns(); if (spawns.isEmpty()) { return; @@ -503,7 +499,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void spawnAmbient(ServerLevel level, int chunkX, int chunkZ, boolean initial) { + private void spawnAmbient(NativeWorld level, int chunkX, int chunkZ, boolean initial) { Optional resolved = resolveSurfaceEnvironment(chunkX, chunkZ); if (resolved.isEmpty()) { return; @@ -514,7 +510,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private void spawnAmbient(ServerLevel level, int chunkX, int chunkZ, boolean initial, BiomeEnvironment environment) { + private void spawnAmbient(NativeWorld level, int chunkX, int chunkZ, boolean initial, BiomeEnvironment environment) { IrisComplex complex = engine.getComplex(); if (complex == null) { return; @@ -568,7 +564,7 @@ public final class ModdedWorldManager implements EngineWorldManager { } } - private int spawnEntry(ServerLevel level, IrisEntitySpawn entry, IrisSpawner spawner, int chunkX, int chunkZ) { + private int spawnEntry(NativeWorld level, IrisEntitySpawn entry, IrisSpawner spawner, int chunkX, int chunkZ) { IrisEntity irisEntity = entry.getRealEntity(engine); if (irisEntity == null) { return 0; @@ -602,15 +598,15 @@ public final class ModdedWorldManager implements EngineWorldManager { } else { worldX = (chunkX << 4) + RNG.r.i(16); worldZ = (chunkZ << 4) + RNG.r.i(16); - int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, worldX, worldZ) - 1; - int solidY = level.getHeight(Heightmap.Types.OCEAN_FLOOR, worldX, worldZ) - 1; + int surfaceY = NativeSpawnQueries.surfaceHeight(level, worldX, worldZ, false); + int solidY = NativeSpawnQueries.surfaceHeight(level, worldX, worldZ, true); Integer selectedY = IrisEntitySpawn.selectSurfaceSpawnY(group, irisEntity.getSurface(), solidY, surfaceY, RNG.r); if (selectedY == null) { continue; } worldY = selectedY; } - if (worldY <= level.getMinY() || worldY >= level.getMaxY()) { + if (worldY <= level.minHeight() || worldY >= level.maxHeight() - 1) { continue; } // Rarity is applied exactly once, as pool weighting in rarityPick - never re-rolled per position (Bukkit parity). @@ -620,17 +616,17 @@ public final class ModdedWorldManager implements EngineWorldManager { if (!surfaceMatches(irisEntity.getSurface(), level, worldX, worldY, worldZ)) { continue; } - if (!ModdedEntitySpawner.isAreaClearForSpawn(level, irisEntity, worldX, worldY, worldZ)) { + if (!ModdedEntitySpawner.isAreaClearForSpawn(entityRuntime(level), irisEntity, worldX, worldY, worldZ)) { continue; } - if (ModdedEntitySpawner.spawn(engine, irisEntity, level, worldX, worldY, worldZ, entityRng) != null) { + if (ModdedEntitySpawner.spawn(engine, irisEntity, entityRuntime(level), worldX, worldY, worldZ, entityRng) != null) { spawned++; } } return spawned; } - private int spawnEntryAt(ServerLevel level, IrisEntitySpawn entry, IrisSpawner spawner, IrisPosition position) { + private int spawnEntryAt(NativeWorld level, IrisEntitySpawn entry, IrisSpawner spawner, IrisPosition position) { IrisEntity irisEntity = entry.getRealEntity(engine); if (irisEntity == null) { return 0; @@ -657,10 +653,10 @@ public final class ModdedWorldManager implements EngineWorldManager { } if (irisEntity.getSurface().isFluid() && (!surfaceMatches(irisEntity.getSurface(), level, worldX, worldY, worldZ) - || !ModdedEntitySpawner.isAreaClearForSpawn(level, irisEntity, worldX, worldY, worldZ))) { + || !ModdedEntitySpawner.isAreaClearForSpawn(entityRuntime(level), irisEntity, worldX, worldY, worldZ))) { continue; } - if (ModdedEntitySpawner.spawn(engine, irisEntity, level, worldX, worldY, worldZ, entityRng) != null) { + if (ModdedEntitySpawner.spawn(engine, irisEntity, entityRuntime(level), worldX, worldY, worldZ, entityRng) != null) { spawned++; } } @@ -679,64 +675,41 @@ public final class ModdedWorldManager implements EngineWorldManager { return spawner.canSpawn(engine, chunkX, chunkZ); } - private boolean lightAllowed(IrisSpawner spawner, ServerLevel level, int worldX, int worldY, int worldZ) { + private boolean lightAllowed(IrisSpawner spawner, NativeWorld level, int worldX, int worldY, int worldZ) { IrisRange range = spawner.getAllowedLightLevels(); if (range.getMin() > 0 || range.getMax() < 15) { - return range.contains(level.getMaxLocalRawBrightness(new BlockPos(worldX, worldY, worldZ))); + return range.contains(NativeSpawnQueries.light(level, worldX, worldY, worldZ)); } return true; } - private boolean surfaceMatches(IrisSurface surface, ServerLevel level, int worldX, int worldY, int worldZ) { - BlockState below = level.getBlockState(new BlockPos(worldX, worldY - (surface.isFluid() ? 0 : 1), worldZ)); + private boolean surfaceMatches(IrisSurface surface, NativeWorld level, int worldX, int worldY, int worldZ) { + NativeBlockState below = level.getBlock(worldX, worldY - (surface.isFluid() ? 0 : 1), worldZ); return matchesSurface(surface, below); } - static boolean matchesSurface(IrisSurface surface, BlockState below) { - if (ModdedBlockResolution.isSolid(below)) { + static boolean matchesSurface(IrisSurface surface, NativeBlockState below) { + if (NativeSpawnQueries.solid(below)) { return surface == IrisSurface.LAND || surface == IrisSurface.OVERWORLD - || (surface == IrisSurface.ANIMAL && isAnimalGround(below)); + || (surface == IrisSurface.ANIMAL && NativeSpawnQueries.animalGround(below)); } - if (below.is(Blocks.LAVA)) { + if (NativeSpawnQueries.lava(below)) { return surface == IrisSurface.LAVA; } - if (ModdedBlockResolution.isWater(below) || ModdedBlockResolution.isWaterLogged(below) || isAquaticFoliage(below)) { + if (NativeSpawnQueries.water(below)) { return surface == IrisSurface.WATER || surface == IrisSurface.OVERWORLD; } return false; } - private static boolean isAnimalGround(BlockState state) { - return state.is(Blocks.GRASS_BLOCK) || state.is(Blocks.DIRT) || state.is(Blocks.DIRT_PATH) - || state.is(Blocks.COARSE_DIRT) || state.is(Blocks.ROOTED_DIRT) || state.is(Blocks.PODZOL) - || state.is(Blocks.MYCELIUM) || state.is(Blocks.SNOW_BLOCK); + private boolean aboveObstructed(NativeWorld level, int worldX, int worldY, int worldZ) { + return worldY + 2 >= level.maxHeight() - 1 + || NativeSpawnQueries.solid(level.getBlock(worldX, worldY + 1, worldZ)) + || NativeSpawnQueries.solid(level.getBlock(worldX, worldY + 2, worldZ)); } - private static boolean isAquaticFoliage(BlockState state) { - return state.is(Blocks.SEAGRASS) || state.is(Blocks.TALL_SEAGRASS) - || state.is(Blocks.KELP) || state.is(Blocks.KELP_PLANT); - } - - private boolean aboveObstructed(ServerLevel level, int worldX, int worldY, int worldZ) { - return worldY + 2 >= level.getMaxY() - || ModdedBlockResolution.isSolid(level.getBlockState(new BlockPos(worldX, worldY + 1, worldZ))) - || ModdedBlockResolution.isSolid(level.getBlockState(new BlockPos(worldX, worldY + 2, worldZ))); - } - - private int countChunkLivingEntities(ServerLevel level, int chunkX, int chunkZ) { - int baseX = chunkX << 4; - int baseZ = chunkZ << 4; - AABB box = new AABB(baseX, level.getMinY(), baseZ, baseX + 16, level.getMaxY(), baseZ + 16); - return level.getEntities((Entity) null, box, - (Entity entity) -> isLivingEntityInChunk(entity, chunkX, chunkZ)).size(); - } - - private static boolean isLivingEntityInChunk(Entity entity, int chunkX, int chunkZ) { - if (!(entity instanceof LivingEntity)) { - return false; - } - BlockPos position = entity.blockPosition(); - return (position.getX() >> 4) == chunkX && (position.getZ() >> 4) == chunkZ; + private int countChunkLivingEntities(NativeWorld level, int chunkX, int chunkZ) { + return NativeSpawnQueries.livingEntities(level, chunkX, chunkZ); } /** @@ -746,9 +719,9 @@ public final class ModdedWorldManager implements EngineWorldManager { * population the ambient spawn gate throttles against. No state means the level has not ticked chunks * yet, so hold spawning rather than guess. */ - private void refreshEntityCount(ServerLevel level, int loadedChunks) { + private void refreshEntityCount(NativeWorld level, int loadedChunks) { cachedConsideredChunks = loadedChunks; - NaturalSpawner.SpawnState spawnState = level.getChunkSource().getLastSpawnState(); + NativeSpawnQueries.Population spawnState = NativeSpawnQueries.population(level); if (spawnState == null) { entityCountAvailable = false; if (!spawnStateMissingLogged) { @@ -758,17 +731,13 @@ public final class ModdedWorldManager implements EngineWorldManager { return; } - Object2IntMap counts = spawnState.getMobCategoryCounts(); - int mobs = 0; - for (MobCategory category : counts.keySet()) { - mobs += counts.getInt(category); - } + int mobs = spawnState.mobs(); entityCountAvailable = true; cachedEntityCount = mobs; // Metric = natural-spawn-cap population over natural-spawn chunk count: numerator and denominator both // come from MC's own spawn state, so the ratio is not diluted by chunks the spawner never counts. - cachedSaturation = mobs / (spawnState.getSpawnableChunkCount() + 1.0) * 1.28; + cachedSaturation = mobs / (spawnState.spawnableChunks() + 1.0) * 1.28; } /** @@ -777,14 +746,14 @@ public final class ModdedWorldManager implements EngineWorldManager { * pure garbage. Returns how many chunks the walk saw, which is the same considered-chunk count the old * snapshot length reported. Server thread only, so the reservoir and its counter are plain fields. */ - private int sampleLoadedChunks(ServerLevel level) { + private int sampleLoadedChunks(NativeWorld level) { ambientChunkSampleSeen = 0; - level.getChunkSource().chunkMap.forEachReadyToSendChunk((LevelChunk chunk) -> { + NativeSpawnQueries.forEachReadyChunk(level, chunk -> { int index = ambientChunkSampleSeen++; int capacity = ambientChunkSample.length; int slot = reservoirSlot(index, capacity, index < capacity ? 0 : RNG.r.nextInt(index + 1)); if (slot >= 0) { - ambientChunkSample[slot] = chunk.getPos().pack(); + ambientChunkSample[slot] = chunk; } }); return ambientChunkSampleSeen; @@ -842,6 +811,7 @@ public final class ModdedWorldManager implements EngineWorldManager { @Override public synchronized void close() { closed = true; + entityRuntime = null; Throwable failure = null; if (!initialSpawnQueueClosed) { try { @@ -921,4 +891,13 @@ public final class ModdedWorldManager implements EngineWorldManager { private record PreparedMarkerSpawn(IrisPosition position, IrisSpawner spawner, BiomeEnvironment environment) { } + private NativeEntityRuntime entityRuntime(NativeWorld level) { + NativeEntityRuntime current = entityRuntime; + if (current == null || !current.world().equals(level)) { + current = NativeEntityRuntime.forWorld(level); + entityRuntime = current; + } + return current; + } + } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeSpawnTableMerger.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeSpawnTableMerger.java deleted file mode 100644 index 2faa17e2b..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeSpawnTableMerger.java +++ /dev/null @@ -1,34 +0,0 @@ -package art.arcane.iris.modded; - -import net.minecraft.util.random.Weighted; -import net.minecraft.util.random.WeightedList; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.MobSpawnSettings; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -final class NativeSpawnTableMerger { - private NativeSpawnTableMerger() { - } - - static WeightedList merge( - WeightedList vanillaSpawns, - WeightedList explicitSpawns) { - List> entries = new ArrayList<>( - vanillaSpawns.unwrap().size() + explicitSpawns.unwrap().size()); - Set> explicitTypes = new HashSet<>(); - for (Weighted entry : explicitSpawns.unwrap()) { - explicitTypes.add(entry.value().type()); - } - for (Weighted entry : vanillaSpawns.unwrap()) { - if (!explicitTypes.contains(entry.value().type())) { - entries.add(entry); - } - } - entries.addAll(explicitSpawns.unwrap()); - return WeightedList.of(entries); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeTerrainReceiptHolder.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeTerrainReceiptHolder.java deleted file mode 100644 index 5c0038228..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/NativeTerrainReceiptHolder.java +++ /dev/null @@ -1,11 +0,0 @@ -package art.arcane.iris.modded; - -public interface NativeTerrainReceiptHolder { - long iris$getStructureActivation(); - - void iris$setStructureActivation(long activation); - - byte[] iris$getNaturalTerrain(); - - void iris$setNaturalTerrain(byte[] receipt); -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java index 66ee00edf..3fcf5e4c0 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java @@ -18,27 +18,27 @@ package art.arcane.iris.modded; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; + +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.volmlib.util.json.JSONObject; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.dimension.DimensionType; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldInspection; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldInspection.Dimension; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; final class WorldCheckDimensionContract { private WorldCheckDimensionContract() { } - static boolean checkDimensionType(ServerLevel level, IrisModdedChunkGenerator generator) { + static boolean checkDimensionType(NativeWorld level, IrisModdedChunkGenerator generator) { try { IrisDimension dimension = generator.commandEngine().getDimension(); - DimensionContract expected = expectedDimensionContract(dimension); - DimensionContract actual = runtimeDimensionContract(level.dimensionType()); - boolean pass = matchesDimensionContract(level.getMinY(), level.getHeight(), expected, actual); + Dimension expected = expectedDimensionContract(dimension); + Dimension actual = new NativeWorldInspection(level).dimension(); + boolean pass = matchesDimensionContract(level.minHeight(), (level.maxHeight() - level.minHeight()), expected, actual); String detail = "expected=" + expected + ",actual=" + actual - + ",levelMinY=" + level.getMinY() + ",levelHeight=" + level.getHeight(); + + ",levelMinY=" + level.minHeight() + ",levelHeight=" + (level.maxHeight() - level.minHeight()); WorldCheckPredicates.qaEvent("dimension_type", dimension.getLoadKey(), pass, detail); if (!pass) { ModdedIrisLog.error("[worldcheck] dimension type mismatch for {}: {}", dimension.getLoadKey(), detail); @@ -54,9 +54,9 @@ final class WorldCheckDimensionContract { } } - static DimensionContract expectedDimensionContract(IrisDimension dimension) { + static Dimension expectedDimensionContract(IrisDimension dimension) { JSONObject json = new JSONObject(dimension.getDimensionType().toJson(DataVersion.V26_2.get())); - return new DimensionContract( + return new Dimension( json.getInt("min_y"), json.getInt("height"), json.getInt("logical_height"), @@ -68,33 +68,18 @@ final class WorldCheckDimensionContract { json.getInt("monster_spawn_block_light_limit")); } - static DimensionContract runtimeDimensionContract(DimensionType dimensionType) { - return new DimensionContract( - dimensionType.minY(), - dimensionType.height(), - dimensionType.logicalHeight(), - dimensionType.coordinateScale(), - dimensionType.ambientLight(), - dimensionType.hasSkyLight(), - dimensionType.hasCeiling(), - dimensionType.hasEnderDragonFight(), - dimensionType.monsterSpawnBlockLightLimit()); - } - static boolean matchesDimensionContract(int levelMinY, int levelHeight, - DimensionContract expected, DimensionContract actual) { + Dimension expected, Dimension actual) { return levelMinY == expected.minY() && levelHeight == expected.height() && actual.equals(expected); } - static boolean checkEntityMixins(ServerLevel level) { - ItemEntity item = new ItemEntity(level, 0D, level.getMinY(), 0D, Items.COBBLESTONE.getDefaultInstance()); - boolean vanillaSave = item.shouldBeSaved(); - ModdedEntityPersistence.configure(item, false); - boolean suppressed = !item.shouldBeSaved(); - ModdedEntityPersistence.configure(item, true); - boolean restored = item.shouldBeSaved(); + static boolean checkEntityMixins(NativeWorld level) { + NativeWorldInspection.Persistence result = new NativeWorldInspection(level).persistence(); + boolean vanillaSave = result.vanilla(); + boolean suppressed = result.suppressed(); + boolean restored = result.restored(); boolean pass = vanillaSave && suppressed && restored; WorldCheckPredicates.qaEvent("entity_mixin", "persistence", pass, "vanilla=" + vanillaSave + ",suppressed=" + suppressed + ",restored=" + restored); @@ -104,8 +89,4 @@ final class WorldCheckDimensionContract { return pass; } - record DimensionContract(int minY, int height, int logicalHeight, double coordinateScale, - float ambientLight, boolean hasSkyLight, boolean hasCeiling, - boolean hasEnderDragonFight, int monsterSpawnBlockLightLimit) { - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckFeaturePlacement.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckFeaturePlacement.java index b19395227..6affd8f91 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckFeaturePlacement.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckFeaturePlacement.java @@ -18,7 +18,6 @@ package art.arcane.iris.modded; -import net.minecraft.world.level.ChunkPos; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; @@ -101,7 +100,7 @@ final class WorldCheckFeaturePlacement { } } - static void recordPlacementFailure(ChunkPos chunkPos, Throwable error) { + static void recordPlacementFailure(int chunkX, int chunkZ, Throwable error) { if (!ENABLED) { return; } @@ -110,7 +109,7 @@ final class WorldCheckFeaturePlacement { String detail = message.contains(UNAVAILABLE_CHUNK_MARKER) ? "unavailableChunk" : "placementFailure"; - report(detail + ",chunk=" + chunkPos.x() + "," + chunkPos.z() + ",error=" + report(detail + ",chunk=" + chunkX + "," + chunkZ + ",error=" + error.getClass().getSimpleName()); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckMaterials.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckMaterials.java index 32fe1eccd..af8b88bd4 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckMaterials.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckMaterials.java @@ -18,24 +18,23 @@ package art.arcane.iris.modded; -import net.minecraft.resources.Identifier; final class WorldCheckMaterials { private WorldCheckMaterials() { } - static boolean isCharacteristicMaterial(String structureLabel, Identifier structureKey, Identifier blockKey) { - if (structureKey == null || blockKey == null || !blockKey.getNamespace().equals("minecraft")) { + static boolean isCharacteristicMaterial(String structureLabel, String structureKey, String blockKey) { + if (structureKey == null || blockKey == null || !blockKey.startsWith("minecraft:")) { return false; } - String block = blockKey.getPath(); + String block = blockKey.substring(blockKey.indexOf(':') + 1); return switch (structureLabel) { case "stronghold" -> isStrongholdMaterial(block); case "trial_chambers" -> isTrialChamberMaterial(block); case "mansion" -> isWoodConstructionMaterial(block, "dark_oak") || isWoodConstructionMaterial(block, "birch") || isCobblestoneConstructionMaterial(block); - case "village" -> isVillageMaterial(structureKey.getPath(), block); + case "village" -> isVillageMaterial(structureKey.substring(structureKey.indexOf(':') + 1), block); case "monument" -> isMonumentMaterial(block); default -> false; }; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckPredicates.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckPredicates.java index e19e006a8..26dac564b 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckPredicates.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckPredicates.java @@ -18,7 +18,7 @@ package art.arcane.iris.modded; -import art.arcane.iris.structure.placement.StructureVerticalBounds; +import art.arcane.volmlib.util.structure.StructureVerticalBounds; import art.arcane.iris.modded.WorldCheckStructureAudit.StructureCheck; import java.security.MessageDigest; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckStructureAudit.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckStructureAudit.java index 35ecb71f0..a7163544f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckStructureAudit.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckStructureAudit.java @@ -19,35 +19,16 @@ package art.arcane.iris.modded; import art.arcane.iris.generation.runtime.Engine; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.Reference; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.Start; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.Found; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.FootprintAudit; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection.PoiAudit; import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.nativegen.NativeStructureFoundationBuilder; -import art.arcane.iris.nativegen.NativeStructureVegetationClearer; -import com.mojang.datafixers.util.Pair; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.tags.BlockTags; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.entity.ai.village.poi.PoiRecord; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; import java.util.ArrayList; import java.util.Arrays; @@ -72,16 +53,13 @@ final class WorldCheckStructureAudit { "minecraft:village_taiga"), 128), new StructureCheck("monument", List.of("minecraft:monument"), 128) ); - private static final int MAX_FOOTPRINT_CHUNKS = 96; - private static final int MAX_START_REFERENCE_CHUNKS = 16; - private static final int MAX_STRUCTURE_CANDIDATES = 1024; private WorldCheckStructureAudit() { } - static NativeStructureGate checkNativeStructures(ServerLevel level, + static NativeStructureGate checkNativeStructures(NativeWorld level, IrisModdedChunkGenerator generator, - BlockPos origin) { + NativeBlockPoint origin) { boolean pass = true; int nonVillagePassed = 0; boolean villagePass = false; @@ -101,23 +79,15 @@ final class WorldCheckStructureAudit { return new NativeStructureGate(pass, nonVillagePassed, villagePass, pendingPoi); } - private static StructureCheckResult checkNativeStructure(ServerLevel level, + private static StructureCheckResult checkNativeStructure(NativeWorld level, IrisModdedChunkGenerator generator, - BlockPos origin, + NativeBlockPoint origin, StructureCheck check) { - Registry registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE); - List> registered = new ArrayList<>(check.registryKeys().size()); + NativeStructureInspection inspection = new NativeStructureInspection(level); + List registered = inspection.resolve(check.registryKeys()); LinkedHashSet registeredKeys = new LinkedHashSet<>(); - for (String key : check.registryKeys()) { - Identifier identifier = Identifier.tryParse(key); - if (identifier == null) { - continue; - } - Optional> resolved = registry.get(identifier); - if (resolved.isPresent()) { - registered.add(resolved.get()); - registeredKeys.add(identifier.toString()); - } + for (Reference reference : registered) { + registeredKeys.add(reference.key()); } boolean registryOk = registered.size() == check.registryKeys().size(); ModdedIrisLog.info("[worldcheck] {} registry: {}/{} resolved {}", check.label(), registered.size(), @@ -133,16 +103,15 @@ final class WorldCheckStructureAudit { return new StructureCheckResult(false, null); } - List> reachable = new ArrayList<>(registered.size()); + List reachable = new ArrayList<>(registered.size()); LinkedHashSet reachableKeys = new LinkedHashSet<>(); - for (Holder holder : registered) { - if (!generator.isNativeStructureReachable(holder)) { + for (Reference holder : registered) { + if (!inspection.reachable(holder)) { continue; } reachable.add(holder); - Identifier key = registry.getKey(holder.value()); - if (key != null) { - reachableKeys.add(key.toString()); + if (holder.key() != null) { + reachableKeys.add(holder.key()); } } boolean reachableOk = !reachable.isEmpty(); @@ -158,11 +127,10 @@ final class WorldCheckStructureAudit { } long locateStart = System.nanoTime(); - Pair> found = findGeneratedStructureCandidate( - level, reachable, origin, check.locateRadius()); + Found found = inspection.find(reachable, origin, check.locateRadius()); long locateMillis = (System.nanoTime() - locateStart) / 1_000_000L; - Identifier foundKey = found == null ? null : registry.getKey(found.getSecond().value()); - boolean locateOk = found != null && foundKey != null && reachableKeys.contains(foundKey.toString()); + String foundKey = found == null ? null : found.structure().key(); + boolean locateOk = found != null && foundKey != null && reachableKeys.contains(foundKey); WorldCheckPredicates.qaEvent("structure_locate", check.label(), locateOk, "method=placement_candidates,millis=" + locateMillis + ",radius=" + check.locateRadius() + ",result=" + (foundKey == null ? "none" : foundKey)); @@ -174,9 +142,9 @@ final class WorldCheckStructureAudit { return new StructureCheckResult(false, null); } - BlockPos position = found.getFirst(); + NativeBlockPoint position = found.position(); ModdedIrisLog.info("[worldcheck] {} generated candidate: {} {} {} in {}ms (radius={}, result={})", - check.label(), position.getX(), position.getY(), position.getZ(), locateMillis, + check.label(), position.x(), position.y(), position.z(), locateMillis, check.locateRadius(), foundKey); if (!locateOk) { ModdedIrisLog.error("[worldcheck] {} candidate scan returned unexpected structure {}", check.label(), foundKey); @@ -185,13 +153,12 @@ final class WorldCheckStructureAudit { return new StructureCheckResult(false, null); } - int chunkX = position.getX() >> 4; - int chunkZ = position.getZ() >> 4; - ChunkAccess targetChunk = level.getChunk(chunkX, chunkZ); - Structure structure = found.getSecond().value(); - StructureStart start = resolveStructureStart(level, targetChunk, structure); - boolean validStart = start != null && start.isValid(); - int references = targetChunk.getReferencesForStructure(structure).size(); + int chunkX = position.x() >> 4; + int chunkZ = position.z() >> 4; + NativeStructureInspection.Evidence evidence = inspection.evidence(found); + Start start = evidence.start(); + boolean validStart = evidence.valid(); + int references = evidence.references(); boolean startReferenceOk = WorldCheckPredicates.hasNativeStructureEvidence(validStart, references); ModdedIrisLog.info("[worldcheck] {} target chunk {},{}: valid start={}, references={}", check.label(), chunkX, chunkZ, validStart, references); @@ -207,13 +174,12 @@ final class WorldCheckStructureAudit { } IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - generator.commandEngine(), foundKey.toString(), - NativeStructureVegetationClearer.isUndergroundStep(structure.step())); - Integer appliedShift = generator.worldCheckStructureShift(foundKey.toString(), start.getChunkPos()); - BoundingBox shiftedBounds = start.getBoundingBox(); + generator.commandEngine(), foundKey, + start.underground()); + Integer appliedShift = generator.worldCheckStructureShift(foundKey, start.chunkX(), start.chunkZ()); boolean verticalShiftOk = WorldCheckPredicates.verticalShiftMatches( - decision.yShift(), appliedShift, shiftedBounds.minY(), shiftedBounds.maxY(), - level.getMinY(), level.getMaxY()); + decision.yShift(), appliedShift, start.minY(), start.maxY(), + level.minHeight(), level.maxHeight()); WorldCheckPredicates.qaEvent("structure_vertical_shift", check.label(), verticalShiftOk, "configured=" + decision.yShift() + ",applied=" + (appliedShift == null ? "unrecorded" : appliedShift)); @@ -222,7 +188,13 @@ final class WorldCheckStructureAudit { check.label(), decision.yShift(), appliedShift); } - FootprintAudit footprint = auditFootprint(level, structure, start, check, foundKey); + Engine engine = generator.commandEngine(); + NativeStructureInspection.FootprintOptions options = new NativeStructureInspection.FootprintOptions( + key -> WorldCheckMaterials.isCharacteristicMaterial(check.label(), foundKey, key), + check.label().equals("mansion"), check.label().equals("village"), + ModdedBlockResolution.BLOCKS.get("minecraft:cobblestone"), + (x, z) -> engine.getHeight(x, z, true) + engine.getMinHeight()); + FootprintAudit footprint = inspection.footprint(start, options); boolean footprintOk = footprint.inspectedChunks() > 0 && footprint.evidenceChunks() == footprint.inspectedChunks() && footprint.coveredPieces() == footprint.totalPieces(); @@ -295,452 +267,8 @@ final class WorldCheckStructureAudit { return new StructureCheckResult(pass, pass ? pendingPoi : null); } - private static StructureStart resolveStructureStart(ServerLevel level, ChunkAccess targetChunk, - Structure structure) { - StructureStart direct = targetChunk.getStartForStructure(structure); - if (direct != null && direct.isValid()) { - return direct; - } - int checked = 0; - for (long packed : targetChunk.getReferencesForStructure(structure)) { - if (checked++ >= MAX_START_REFERENCE_CHUNKS) { - break; - } - ChunkAccess referencedChunk = level.getChunk(ChunkPos.getX(packed), ChunkPos.getZ(packed)); - StructureStart referenced = referencedChunk.getStartForStructure(structure); - if (referenced != null && referenced.isValid()) { - return referenced; - } - } - return direct; - } - - private static Pair> findGeneratedStructureCandidate( - ServerLevel level, List> structures, BlockPos origin, int maxRadius) { - ChunkGeneratorStructureState state = level.getChunkSource().getGeneratorState(); - Set attempted = new LinkedHashSet<>(); - for (Holder structure : structures) { - for (StructurePlacement placement : state.getPlacementsForStructure(structure)) { - if (!(placement instanceof ConcentricRingsStructurePlacement rings)) { - continue; - } - List positions = state.getRingPositionsFor(rings); - if (positions == null) { - continue; - } - List sorted = new ArrayList<>(positions); - sorted.sort(Comparator.comparingLong(position -> distanceSquared(origin, position))); - for (ChunkPos position : sorted) { - Pair> found = inspectStructureCandidate( - level, structures, placement, position, attempted); - if (found != null) { - return found; - } - if (attempted.size() >= MAX_STRUCTURE_CANDIDATES) { - return null; - } - } - } - } - - int originChunkX = origin.getX() >> 4; - int originChunkZ = origin.getZ() >> 4; - for (int radius = 0; radius <= maxRadius; radius++) { - for (Holder structure : structures) { - for (StructurePlacement placement : state.getPlacementsForStructure(structure)) { - if (!(placement instanceof RandomSpreadStructurePlacement randomSpread)) { - continue; - } - for (int x = -radius; x <= radius; x++) { - boolean xEdge = x == -radius || x == radius; - for (int z = -radius; z <= radius; z++) { - if (!xEdge && z != -radius && z != radius) { - continue; - } - int sectorX = originChunkX + randomSpread.spacing() * x; - int sectorZ = originChunkZ + randomSpread.spacing() * z; - ChunkPos candidate = randomSpread.getPotentialStructureChunk( - state.getLevelSeed(), sectorX, sectorZ); - if (!placement.isStructureChunk(state, candidate.x(), candidate.z())) { - continue; - } - Pair> found = inspectStructureCandidate( - level, structures, placement, candidate, attempted); - if (found != null) { - return found; - } - if (attempted.size() >= MAX_STRUCTURE_CANDIDATES) { - return null; - } - } - } - } - } - } - return null; - } - - private static Pair> inspectStructureCandidate( - ServerLevel level, List> structures, StructurePlacement placement, - ChunkPos candidate, Set attempted) { - if (!attempted.add(candidate.pack())) { - return null; - } - ChunkAccess chunk = level.getChunk(candidate.x(), candidate.z()); - for (Holder structure : structures) { - StructureStart start = resolveStructureStart(level, chunk, structure.value()); - if (start == null || !start.isValid()) { - continue; - } - BlockPos locate = placement.getLocatePos(start.getChunkPos()); - BlockPos resolved = new BlockPos(locate.getX(), start.getBoundingBox().minY(), locate.getZ()); - return Pair.of(resolved, structure); - } - return null; - } - - private static long distanceSquared(BlockPos origin, ChunkPos position) { - long x = (long) position.getMinBlockX() - origin.getX(); - long z = (long) position.getMinBlockZ() - origin.getZ(); - return x * x + z * z; - } - - private static FootprintAudit auditFootprint(ServerLevel level, Structure structure, StructureStart start, - StructureCheck check, Identifier structureKey) { - List pieces = start.getPieces(); - BoundingBox bounds = start.getBoundingBox(); - int availableChunks = footprintChunkCount(bounds); - List selected = selectFootprintChunks(start, MAX_FOOTPRINT_CHUNKS); - int evidenceChunks = 0; - int blockEntityStates = 0; - int blockEntitiesPresent = 0; - int materialScannedChunks = 0; - int characteristicBlocks = 0; - int characteristicChunks = 0; - int vegetationBlocks = 0; - int vegetationColumns = 0; - int foundationBaseColumns = 0; - int foundationBlocks = 0; - int foundationColumns = 0; - int foundationGapColumns = 0; - BitSet visited = new BitSet(level.getHeight() << 8); - int[] maximumPieceY = new int[256]; - for (ChunkPos chunkPos : selected) { - ChunkAccess chunk = level.getChunk(chunkPos.x(), chunkPos.z()); - StructureStart localStart = chunk.getStartForStructure(structure); - boolean validStart = localStart != null && localStart.isValid(); - int references = chunk.getReferencesForStructure(structure).size(); - if (WorldCheckPredicates.hasNativeStructureEvidence(validStart, references)) { - evidenceChunks++; - } - BlockEntityAudit blockEntities = auditBlockEntities(level, chunk); - blockEntityStates += blockEntities.states(); - blockEntitiesPresent += blockEntities.present(); - StructureMaterialAudit material = auditStructureMaterial(level, chunk, start, check, - structureKey, visited, maximumPieceY); - if (material.scanned()) { - materialScannedChunks++; - } - characteristicBlocks += material.characteristicBlocks(); - if (material.characteristicBlocks() > 0) { - characteristicChunks++; - } - vegetationBlocks += material.vegetationBlocks(); - vegetationColumns += material.vegetationColumns(); - foundationBaseColumns += material.foundationBaseColumns(); - foundationBlocks += material.foundationBlocks(); - foundationColumns += material.foundationColumns(); - foundationGapColumns += material.foundationGapColumns(); - } - int coveredPieces = 0; - for (StructurePiece piece : pieces) { - boolean covered = false; - for (ChunkPos chunkPos : selected) { - if (intersectsChunk(piece.getBoundingBox(), chunkPos)) { - covered = true; - break; - } - } - if (covered) { - coveredPieces++; - } - } - return new FootprintAudit(selected.size(), availableChunks, evidenceChunks, - coveredPieces, pieces.size(), blockEntityStates, blockEntitiesPresent, - materialScannedChunks, characteristicBlocks, characteristicChunks, - vegetationBlocks, vegetationColumns, foundationBaseColumns, foundationBlocks, - foundationColumns, foundationGapColumns); - } - - private static StructureMaterialAudit auditStructureMaterial(ServerLevel level, ChunkAccess chunk, - StructureStart start, - StructureCheck check, - Identifier structureKey, - BitSet visited, - int[] maximumPieceY) { - List pieces = start.getPieces(); - visited.clear(); - Arrays.fill(maximumPieceY, Integer.MIN_VALUE); - int characteristicBlocks = 0; - int minimumWorldY = level.getMinY(); - int maximumWorldY = level.getMaxY() - 1; - int minimumChunkX = chunk.getPos().getMinBlockX(); - int maximumChunkX = chunk.getPos().getMaxBlockX(); - int minimumChunkZ = chunk.getPos().getMinBlockZ(); - int maximumChunkZ = chunk.getPos().getMaxBlockZ(); - boolean scanned = false; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - for (StructurePiece piece : pieces) { - BoundingBox bounds = piece.getBoundingBox(); - int minimumX = Math.max(minimumChunkX, bounds.minX()); - int maximumX = Math.min(maximumChunkX, bounds.maxX()); - int minimumY = Math.max(minimumWorldY, bounds.minY()); - int maximumY = Math.min(maximumWorldY, bounds.maxY()); - int minimumZ = Math.max(minimumChunkZ, bounds.minZ()); - int maximumZ = Math.min(maximumChunkZ, bounds.maxZ()); - if (minimumX > maximumX || minimumY > maximumY || minimumZ > maximumZ) { - continue; - } - scanned = true; - for (int z = minimumZ; z <= maximumZ; z++) { - int localZ = z - minimumChunkZ; - for (int x = minimumX; x <= maximumX; x++) { - int column = (localZ << 4) | (x - minimumChunkX); - maximumPieceY[column] = Math.max(maximumPieceY[column], maximumY); - } - } - for (int y = minimumY; y <= maximumY; y++) { - int verticalIndex = (y - minimumWorldY) << 8; - for (int z = minimumZ; z <= maximumZ; z++) { - int localZ = z - minimumChunkZ; - for (int x = minimumX; x <= maximumX; x++) { - int column = (localZ << 4) | (x - minimumChunkX); - int index = verticalIndex | column; - if (visited.get(index)) { - continue; - } - visited.set(index); - BlockState state = chunk.getBlockState(position.set(x, y, z)); - Identifier blockKey = BuiltInRegistries.BLOCK.getKey(state.getBlock()); - if (WorldCheckMaterials.isCharacteristicMaterial(check.label(), structureKey, blockKey)) { - characteristicBlocks++; - } - } - } - } - } - - int vegetationBlocks = 0; - int vegetationColumns = 0; - if (check.label().equals("mansion")) { - for (int column = 0; column < maximumPieceY.length; column++) { - int highestPieceY = maximumPieceY[column]; - if (highestPieceY == Integer.MIN_VALUE || highestPieceY >= maximumWorldY) { - continue; - } - boolean vegetationColumn = false; - int x = minimumChunkX + (column & 15); - int z = minimumChunkZ + (column >> 4); - for (int y = highestPieceY + 1; y <= maximumWorldY; y++) { - BlockState state = chunk.getBlockState(position.set(x, y, z)); - boolean vegetation = state.is(BlockTags.LOGS) || state.is(BlockTags.LEAVES); - if (!WorldCheckPredicates.mansionVegetationAbovePiece(vegetation, y, highestPieceY)) { - continue; - } - vegetationBlocks++; - vegetationColumn = true; - } - if (vegetationColumn) { - vegetationColumns++; - } - } - } - - int foundationBaseColumns = 0; - int foundationBlocks = 0; - int foundationColumns = 0; - int foundationGapColumns = 0; - if (check.label().equals("village")) { - BoundingBox area = new BoundingBox( - minimumChunkX, minimumWorldY, minimumChunkZ, - maximumChunkX, maximumWorldY, maximumChunkZ); - ChunkGenerator chunkGenerator = level.getChunkSource().getGenerator(); - if (!(chunkGenerator instanceof IrisModdedChunkGenerator irisGenerator)) { - throw new IllegalStateException("Iris structure audit requires the Iris chunk generator"); - } - Engine engine = irisGenerator.commandEngine(); - IntBinaryOperator surfaceHeight = (x, z) -> - engine.getHeight(x, z, true) + engine.getMinHeight(); - NativeStructureFoundationBuilder.StiltSupportAudit foundation = - NativeStructureFoundationBuilder.auditStiltSupport( - level, area, start, Blocks.COBBLESTONE.defaultBlockState(), surfaceHeight); - foundationBaseColumns = foundation.baseColumns(); - foundationBlocks = foundation.stiltBlocks(); - foundationColumns = foundation.stiltColumns(); - foundationGapColumns = foundation.unsupportedColumns(); - } - - return new StructureMaterialAudit(scanned, characteristicBlocks, vegetationBlocks, - vegetationColumns, foundationBaseColumns, foundationBlocks, foundationColumns, - foundationGapColumns); - } - - private static List selectFootprintChunks(StructureStart start, int limit) { - LinkedHashSet selected = new LinkedHashSet<>(); - addBounded(selected, start.getChunkPos(), limit); - List pieceAnchors = new ArrayList<>(); - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - pieceAnchors.add(new ChunkPos((bounds.minX() + bounds.maxX()) >> 5, - (bounds.minZ() + bounds.maxZ()) >> 5)); - pieceAnchors.add(new ChunkPos(bounds.minX() >> 4, bounds.minZ() >> 4)); - pieceAnchors.add(new ChunkPos(bounds.maxX() >> 4, bounds.maxZ() >> 4)); - } - pieceAnchors.sort(Comparator.comparingInt((ChunkPos chunkPos) -> - chunkPos.distanceSquared(start.getChunkPos()))); - for (ChunkPos chunkPos : pieceAnchors) { - addBounded(selected, chunkPos, limit); - } - for (ChunkPos chunkPos : boundedFootprintChunks(start.getBoundingBox(), start.getChunkPos(), limit)) { - if (intersectsAnyPiece(start.getPieces(), chunkPos)) { - addBounded(selected, chunkPos, limit); - } - } - return List.copyOf(selected); - } - - static List boundedFootprintChunks(BoundingBox bounds, ChunkPos origin, int limit) { - if (limit <= 0) { - return List.of(); - } - int minChunkX = bounds.minX() >> 4; - int maxChunkX = bounds.maxX() >> 4; - int minChunkZ = bounds.minZ() >> 4; - int maxChunkZ = bounds.maxZ() >> 4; - long width = (long) maxChunkX - minChunkX + 1L; - long depth = (long) maxChunkZ - minChunkZ + 1L; - long total = width * depth; - LinkedHashSet chunks = new LinkedHashSet<>(); - addBounded(chunks, origin, limit); - if (total <= limit) { - for (int chunkZ = minChunkZ; chunkZ <= maxChunkZ; chunkZ++) { - for (int chunkX = minChunkX; chunkX <= maxChunkX; chunkX++) { - addBounded(chunks, new ChunkPos(chunkX, chunkZ), limit); - } - } - return List.copyOf(chunks); - } - addBounded(chunks, new ChunkPos(minChunkX, minChunkZ), limit); - addBounded(chunks, new ChunkPos(maxChunkX, minChunkZ), limit); - addBounded(chunks, new ChunkPos(minChunkX, maxChunkZ), limit); - addBounded(chunks, new ChunkPos(maxChunkX, maxChunkZ), limit); - int samplesPerAxis = Math.max(2, (int) Math.floor(Math.sqrt(limit))); - for (int sampleZ = 0; sampleZ < samplesPerAxis; sampleZ++) { - int chunkZ = sampleCoordinate(minChunkZ, maxChunkZ, sampleZ, samplesPerAxis); - for (int sampleX = 0; sampleX < samplesPerAxis; sampleX++) { - int chunkX = sampleCoordinate(minChunkX, maxChunkX, sampleX, samplesPerAxis); - addBounded(chunks, new ChunkPos(chunkX, chunkZ), limit); - } - } - return List.copyOf(chunks); - } - - private static int footprintChunkCount(BoundingBox bounds) { - long width = (long) (bounds.maxX() >> 4) - (bounds.minX() >> 4) + 1L; - long depth = (long) (bounds.maxZ() >> 4) - (bounds.minZ() >> 4) + 1L; - long total = width * depth; - return total > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) total; - } - - private static int sampleCoordinate(int minimum, int maximum, int index, int samples) { - if (samples <= 1 || minimum == maximum) { - return minimum; - } - double progress = (double) index / (double) (samples - 1); - return minimum + (int) Math.round((maximum - minimum) * progress); - } - - private static void addBounded(Set chunks, ChunkPos chunkPos, int limit) { - if (chunks.size() < limit) { - chunks.add(chunkPos); - } - } - - private static boolean intersectsAnyPiece(List pieces, ChunkPos chunkPos) { - for (StructurePiece piece : pieces) { - if (intersectsChunk(piece.getBoundingBox(), chunkPos)) { - return true; - } - } - return false; - } - - private static boolean intersectsChunk(BoundingBox bounds, ChunkPos chunkPos) { - return bounds.maxX() >= chunkPos.getMinBlockX() - && bounds.minX() <= chunkPos.getMaxBlockX() - && bounds.maxZ() >= chunkPos.getMinBlockZ() - && bounds.minZ() <= chunkPos.getMaxBlockZ(); - } - - private static BlockEntityAudit auditBlockEntities(ServerLevel level, ChunkAccess chunk) { - int states = 0; - int present = 0; - BlockPos.MutableBlockPos position = new BlockPos.MutableBlockPos(); - LevelChunkSection[] sections = chunk.getSections(); - for (int sectionIndex = 0; sectionIndex < sections.length; sectionIndex++) { - LevelChunkSection section = sections[sectionIndex]; - if (!section.maybeHas(BlockState::hasBlockEntity)) { - continue; - } - int sectionMinY = chunk.getSectionYFromSectionIndex(sectionIndex) << 4; - for (int localY = 0; localY < 16; localY++) { - for (int localZ = 0; localZ < 16; localZ++) { - for (int localX = 0; localX < 16; localX++) { - BlockState state = section.getBlockState(localX, localY, localZ); - if (!state.hasBlockEntity()) { - continue; - } - states++; - position.set(chunk.getPos().getBlockX(localX), sectionMinY + localY, - chunk.getPos().getBlockZ(localZ)); - if (level.getBlockEntity(position) != null) { - present++; - } - } - } - } - } - return new BlockEntityAudit(states, present); - } - - static PoiAudit auditStructurePois(ServerLevel level, StructureStart start) { - int inBounds = 0; - int outOfBounds = 0; - PoiManager poiManager = level.getPoiManager(); - for (ChunkPos chunkPos : selectFootprintChunks(start, MAX_FOOTPRINT_CHUNKS)) { - List records = poiManager.getInChunk( - holder -> true, chunkPos, PoiManager.Occupancy.ANY).toList(); - for (PoiRecord record : records) { - BlockPos position = record.getPos(); - if (position.getY() < level.getMinY() || position.getY() >= level.getMaxY()) { - outOfBounds++; - continue; - } - if (insideAnyPiece(start.getPieces(), position)) { - inBounds++; - } - } - } - return new PoiAudit(inBounds, outOfBounds); - } - - private static boolean insideAnyPiece(List pieces, BlockPos position) { - for (StructurePiece piece : pieces) { - if (piece.getBoundingBox().isInside(position)) { - return true; - } - } - return false; + static PoiAudit auditStructurePois(NativeWorld level, Start start) { + return new NativeStructureInspection(level).pois(start); } record StructureCheck(String label, List registryKeys, int locateRadius) { @@ -753,28 +281,7 @@ final class WorldCheckStructureAudit { private record StructureCheckResult(boolean pass, PendingVillagePoi pendingPoi) { } - record PendingVillagePoi(ServerLevel level, StructureStart start) { + record PendingVillagePoi(NativeWorld level, Start start) { } - private record FootprintAudit(int inspectedChunks, int availableChunks, int evidenceChunks, - int coveredPieces, int totalPieces, int blockEntityStates, - int blockEntitiesPresent, int materialScannedChunks, - int characteristicBlocks, int characteristicChunks, - int vegetationBlocks, int vegetationColumns, - int foundationBaseColumns, int foundationBlocks, int foundationColumns, - int foundationGapColumns) { - } - - private record BlockEntityAudit(int states, int present) { - } - - private record StructureMaterialAudit(boolean scanned, int characteristicBlocks, - int vegetationBlocks, int vegetationColumns, - int foundationBaseColumns, int foundationBlocks, - int foundationColumns, - int foundationGapColumns) { - } - - record PoiAudit(int inBounds, int outOfBounds) { - } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/IrisModdedAPI.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/IrisModdedAPI.java index 9c864aca4..fad80086b 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/IrisModdedAPI.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/IrisModdedAPI.java @@ -23,19 +23,19 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineLifecycleTasks; import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.modded.command.ModdedPregenJob; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.chunk.ChunkGenerator; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; /** * Entry point for mods integrating with Iris on Fabric, Forge and NeoForge. *

- * Everything here is static and null-tolerant: a null or non-Iris {@link ServerLevel} produces false, null, or a + * Everything here is static and null-tolerant: a null or non-Iris {@link NativeWorld} produces false, null, or a * no-op rather than an exception, so a caller never has to pre-check whether a level is generated by Iris. *

- * Threading. {@link #isIrisLevel(ServerLevel)}, {@link #isStudioLevel(ServerLevel)} and - * {@link #getEngine(ServerLevel)} read the level's chunk generator reference and are safe from any thread once + * Threading. {@link #isIrisLevel(NativeWorld)}, {@link #isStudioLevel(NativeWorld)} and + * {@link #getEngine(NativeWorld)} read the level's chunk generator reference and are safe from any thread once * the level is loaded. The mantle accessors are safe off the server thread but touch engine storage - see their - * own notes. {@link #pregenerate(ServerLevel, int)} and {@link #registerProvider(ModdedDataProvider)} mutate + * own notes. {@link #pregenerate(NativeWorld, int)} and {@link #registerProvider(ModdedDataProvider)} mutate * global state and belong on the server thread, during mod setup or from a command. *

* Stability. This class and the {@code Modded*} types beside it are the intended integration surface. The @@ -53,18 +53,18 @@ public final class IrisModdedAPI { * Whether {@code level}'s chunk generator is an Iris generator. False for null and for every vanilla or * third-party generated level. The cheapest available Iris check. */ - public static boolean isIrisLevel(ServerLevel level) { + public static boolean isIrisLevel(NativeWorld level) { if (level == null) { return false; } - return level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator; + return NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) != null; } /** * Whether {@code level} is an Iris studio level - a throwaway world opened for pack authoring, which is * deleted on shutdown. Persist nothing against one. False for null and for non-Iris levels. */ - public static boolean isStudioLevel(ServerLevel level) { + public static boolean isStudioLevel(NativeWorld level) { Engine engine = getEngine(level); return engine != null && engine.isStudio(); } @@ -76,12 +76,12 @@ public final class IrisModdedAPI { * Never cached: resolve per use. Reloading a pack or unloading the level replaces the engine, and a stale * reference goes inert. {@link Engine} is internal to Iris; see the stability note on this class. */ - public static Engine getEngine(ServerLevel level) { + public static Engine getEngine(NativeWorld level) { if (level == null) { return null; } - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (!(generator instanceof IrisModdedChunkGenerator irisGenerator)) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { return null; } try { @@ -95,7 +95,7 @@ public final class IrisModdedAPI { * Starts a cached, asynchronous pregeneration of {@code radiusBlocks} around the world origin. * Equivalent to {@code pregenerate(level, radiusBlocks, 0, 0, false, true)}. */ - public static boolean pregenerate(ServerLevel level, int radiusBlocks) { + public static boolean pregenerate(NativeWorld level, int radiusBlocks) { return pregenerate(level, radiusBlocks, 0, 0, false, true); } @@ -112,12 +112,13 @@ public final class IrisModdedAPI { * regenerating * @return false when {@code level} is not Iris-generated or another pregeneration job is already running */ - public static boolean pregenerate(ServerLevel level, int radiusBlocks, int centerBlockX, int centerBlockZ, boolean sync, boolean cached) { + public static boolean pregenerate(NativeWorld level, int radiusBlocks, int centerBlockX, int centerBlockZ, boolean sync, boolean cached) { Engine engine = getEngine(level); if (engine == null) { return false; } - return ModdedPregenJob.start(level.getServer(), level, engine, radiusBlocks, centerBlockX, centerBlockZ, false, sync, cached); + return ModdedPregenJob.start(level, engine, new ModdedPregenJob.PregenOptions( + radiusBlocks, centerBlockX, centerBlockZ, false, sync, cached)); } /** @@ -133,7 +134,7 @@ public final class IrisModdedAPI { * * A closing or closed engine refuses the operation quietly (null / no-op) instead of racing its shutdown. */ - public static T getMantleData(ServerLevel level, int x, int y, int z, Class type) { + public static T getMantleData(NativeWorld level, int x, int y, int z, Class type) { Engine engine = getEngine(level); if (engine == null) { return null; @@ -147,17 +148,17 @@ public final class IrisModdedAPI { /** * Writes a mantle value at world coordinates, replacing any previous value of the same type there. *

- * Unlike {@link #getMantleData(ServerLevel, int, int, int, Class)}, a write creates the mantle region if it + * Unlike {@link #getMantleData(NativeWorld, int, int, int, Class)}, a write creates the mantle region if it * does not exist, which can touch disk - do not call it per block in a tick loop from the server thread. A * null {@code data}, a non-Iris level, or a {@code y} outside the engine's height range is a silent no-op; - * remove values with {@link #deleteMantleData(ServerLevel, int, int, int, Class)}. + * remove values with {@link #deleteMantleData(NativeWorld, int, int, int, Class)}. *

* Values written under a custom type are discarded when Iris trims a mantle region unless the type is * declared with {@link #retainMantleDataForSlice(Class)}. * * A closing or closed engine refuses the operation quietly (null / no-op) instead of racing its shutdown. */ - public static void setMantleData(ServerLevel level, int x, int y, int z, T data) { + public static void setMantleData(NativeWorld level, int x, int y, int z, T data) { Engine engine = getEngine(level); if (engine == null || data == null) { return; @@ -172,7 +173,7 @@ public final class IrisModdedAPI { * * A closing or closed engine refuses the operation quietly (null / no-op) instead of racing its shutdown. */ - public static void deleteMantleData(ServerLevel level, int x, int y, int z, Class type) { + public static void deleteMantleData(NativeWorld level, int x, int y, int z, Class type) { Engine engine = getEngine(level); if (engine == null) { return; @@ -186,7 +187,7 @@ public final class IrisModdedAPI { *

* Iris drops slices it does not need once a region's generation data has served its purpose - both the * normal per-chunk trim and pregeneration's forced cleanup honor this registry. Any type a mod writes - * with {@link #setMantleData(ServerLevel, int, int, int, Object)} and expects to read back later must be + * with {@link #setMantleData(NativeWorld, int, int, int, Object)} and expects to read back later must be * declared here first. Registration is by canonical class name, process-wide across every Iris world, and * cannot be undone - declare it once during mod setup. A null {@code sliceType} is ignored, and the * block-state slice can never be retained. diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockData.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockData.java index f0cc954ae..d83fa5a15 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockData.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockData.java @@ -18,7 +18,7 @@ package art.arcane.iris.modded.api; -import net.minecraft.world.level.block.state.BlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import java.util.Objects; @@ -26,14 +26,14 @@ import java.util.Objects; * A provider's answer to a block lookup: the state to write, and whether the provider wants a second pass once the * chunk is loaded. *

- * Immutable. Returned from {@link ModdedDataProvider#getBlockData(net.minecraft.resources.Identifier, java.util.Map)}; - * construct with {@link #direct(BlockState)} or {@link #deferred(BlockState)} rather than the canonical constructor. + * Immutable. Returned from {@link ModdedDataProvider#getBlockData(String, java.util.Map)}; + * construct with {@link #direct(ModdedBlockState)} or {@link #deferred(ModdedBlockState)} rather than the canonical constructor. * * @param state the block state Iris writes. Never null * @param deferredPlacement whether {@link ModdedDataProvider#processBlockPlacement(ModdedBlockPlacementContext)} * should run for this position after the chunk is loaded */ -public record ModdedBlockData(BlockState state, boolean deferredPlacement) { +public record ModdedBlockData(ModdedBlockState state, boolean deferredPlacement) { /** * @throws NullPointerException if {@code state} is null */ @@ -44,7 +44,7 @@ public record ModdedBlockData(BlockState state, boolean deferredPlacement) { /** * The state is final - Iris writes it during generation and does nothing further. */ - public static ModdedBlockData direct(BlockState state) { + public static ModdedBlockData direct(ModdedBlockState state) { return new ModdedBlockData(state, false); } @@ -55,7 +55,7 @@ public record ModdedBlockData(BlockState state, boolean deferredPlacement) { * generation thread. Pick a placeholder with the same shape and occlusion as the final block so terrain around * it generates correctly. */ - public static ModdedBlockData deferred(BlockState state) { + public static ModdedBlockData deferred(ModdedBlockState state) { return new ModdedBlockData(state, true); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockPlacementContext.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockPlacementContext.java index 559d9af6c..03d8718bd 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockPlacementContext.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedBlockPlacementContext.java @@ -19,10 +19,7 @@ package art.arcane.iris.modded.api; import art.arcane.iris.generation.runtime.Engine; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.state.BlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockPlacement; import java.util.Map; import java.util.Objects; @@ -31,34 +28,27 @@ import java.util.Objects; * Everything a provider needs to finish a deferred block placement, handed to * {@link ModdedDataProvider#processBlockPlacement(ModdedBlockPlacementContext)} on the server thread. *

- * Immutable, and constructed by Iris rather than by mods. {@code state} is defensively copied; {@code position} is - * already immutable. Because delivery is on the server thread with the chunk loaded, it is safe to write blocks, + * Immutable, and constructed by Iris rather than by mods. {@code state} is defensively copied; the position + * is available through the placement handle. Because delivery is on the server thread with the chunk loaded, it is safe to write blocks, * attach block entities and read neighbours from here. * * @param engine the Iris engine for this level. Internal Iris type - treat it as an opaque token - * @param level the level to write into. Never null - * @param position the block the placeholder was written at. Never null + * @param placement the native world, immutable position and captured block state. Never null * @param blockId the identifier the pack named, without state properties. Never null - * @param blockState the state currently at {@code position} - normally the placeholder returned as deferred, though - * another provider or a later generation stage may have replaced it. Never null * @param state the {@code [prop=value]} pairs from the pack's key, possibly empty. Never null; unmodifiable */ public record ModdedBlockPlacementContext( Engine engine, - ServerLevel level, - BlockPos position, - Identifier blockId, - Map state, - BlockState blockState) { + NativeBlockPlacement placement, + String blockId, + Map state) { /** * @throws NullPointerException if any component is null */ public ModdedBlockPlacementContext { Objects.requireNonNull(engine); - Objects.requireNonNull(level); - Objects.requireNonNull(position); + Objects.requireNonNull(placement); Objects.requireNonNull(blockId); state = Map.copyOf(state); - Objects.requireNonNull(blockState); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedCustomContentRegistry.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedCustomContentRegistry.java index 19fa93339..bc36a04d8 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedCustomContentRegistry.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedCustomContentRegistry.java @@ -18,15 +18,14 @@ package art.arcane.iris.modded.api; -import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.modded.ModdedBlockResolution; -import net.minecraft.core.BlockPos; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.block.state.BlockState; - +import art.arcane.iris.modded.ModdedIrisLog; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockPlacement; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockResolver; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnedEntity; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeResourceKeys; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -38,6 +37,7 @@ import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Supplier; /** * Registry of {@link ModdedDataProvider} instances and static block-data aliases, and the resolution path Iris @@ -57,7 +57,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public final class ModdedCustomContentRegistry { private static final List PROVIDERS = new CopyOnWriteArrayList<>(); - private static final Map CUSTOM_BLOCKS = new ConcurrentHashMap<>(); + private static final Map CUSTOM_BLOCKS = new ConcurrentHashMap<>(); private static volatile boolean scanned = false; private static DiscoveryBatch discoveryBatch; @@ -73,23 +73,23 @@ public final class ModdedCustomContentRegistry { if (namespace == null || key == null || state == null) { return; } - Identifier identifier = Identifier.tryParse(namespace + ":" + key); + String identifier = NativeResourceKeys.normalize(namespace + ":" + key); if (identifier == null) { ModdedIrisLog.warn("Iris custom block data registration rejected invalid id {}:{}", namespace, key); return; } - BlockState parsed; + ModdedBlockState parsed; try { - parsed = ModdedBlockResolution.strictParse(state).handle(); + parsed = NativeBlockResolver.strictParse(state); } catch (Throwable error) { ModdedIrisLog.error("Iris custom block data '{}:{}' has unparseable state '{}'", namespace, key, state, error); return; } DiscoveryBatch activeBatch = discoveryBatch; if (activeBatch == null) { - CUSTOM_BLOCKS.put(identifier.toString(), parsed); + CUSTOM_BLOCKS.put(identifier, parsed); } else { - activeBatch.customBlocks.put(identifier.toString(), parsed); + activeBatch.customBlocks.put(identifier, parsed); } ModdedIrisLog.info("Iris registered custom block data {}:{} -> {}", namespace, key, state); } @@ -142,7 +142,7 @@ public final class ModdedCustomContentRegistry { static synchronized Discovery discover(Iterable discoveredProviders) { List previousProviders = List.copyOf(PROVIDERS); - Map previousCustomBlocks = new LinkedHashMap<>(CUSTOM_BLOCKS); + Map previousCustomBlocks = new LinkedHashMap<>(CUSTOM_BLOCKS); boolean previousDiscoveryComplete = scanned; DiscoveryBatch batch = new DiscoveryBatch(previousProviders, previousCustomBlocks); discoveryBatch = batch; @@ -236,7 +236,7 @@ public final class ModdedCustomContentRegistry { List keys = new ArrayList<>(); Set seen = new HashSet<>(); for (ModdedDataProvider provider : PROVIDERS) { - Collection types; + Collection types; try { types = provider.getTypes(type); } catch (Throwable error) { @@ -246,9 +246,9 @@ public final class ModdedCustomContentRegistry { if (types == null) { continue; } - for (Identifier identifier : types) { - if (identifier != null && seen.add(identifier.toString())) { - keys.add(identifier.toString()); + for (String identifier : types) { + if (identifier != null && seen.add(identifier)) { + keys.add(identifier); } } } @@ -264,11 +264,11 @@ public final class ModdedCustomContentRegistry { if (key == null || (PROVIDERS.isEmpty() && CUSTOM_BLOCKS.isEmpty())) { return null; } - Identifier base = parseIdentifier(key); + String base = parseIdentifier(key); if (base == null) { return null; } - BlockState custom = CUSTOM_BLOCKS.get(base.toString()); + ModdedBlockState custom = CUSTOM_BLOCKS.get(base); if (custom != null) { return ModdedBlockData.direct(custom); } @@ -297,8 +297,8 @@ public final class ModdedCustomContentRegistry { * consulted for that position. An unparseable key or no matching provider is logged and skipped. Called on the * server thread with the chunk loaded. */ - public static void processBlockPlacement(Engine engine, ServerLevel level, BlockPos position, String key) { - Identifier base = parseIdentifier(key); + public static void processBlockPlacement(Engine engine, String key, Supplier placement) { + String base = parseIdentifier(key); if (base == null) { ModdedIrisLog.warn("Iris deferred custom block placement rejected invalid id {}", key); return; @@ -310,9 +310,9 @@ public final class ModdedCustomContentRegistry { } try { provider.processBlockPlacement(new ModdedBlockPlacementContext( - engine, level, position.immutable(), base, state, level.getBlockState(position))); + engine, placement.get(), base, state)); } catch (Throwable error) { - ModdedIrisLog.error("Iris custom content provider '{}' failed post-placement for {} at {}", provider.modId(), key, position, error); + ModdedIrisLog.error("Iris custom content provider '{}' failed post-placement for {}", provider.modId(), key, error); } return; } @@ -323,20 +323,23 @@ public final class ModdedCustomContentRegistry { * Asks each ready provider claiming {@code key} to spawn a custom entity, returning the first non-null result. * Null when no provider claims it or every attempt declined. Called on the server thread. */ - public static Entity spawnMob(ServerLevel level, double x, double y, double z, String key) { - if (PROVIDERS.isEmpty() || level == null || key == null) { + public static NativeSpawnedEntity spawnMob(NativeEntityRuntime.CustomSpawn request) { + String key = request.key(); + if (PROVIDERS.isEmpty() || request.runtime() == null || key == null) { return null; } - Identifier base = parseIdentifier(key); + String base = parseIdentifier(key); if (base == null) { return null; } + NativeEntityRuntime.CustomSpawn normalized = new NativeEntityRuntime.CustomSpawn( + request.runtime(), request.position(), base); for (ModdedDataProvider provider : PROVIDERS) { if (!provider.isReady() || !provider.isValidProvider(base, ModdedDataType.ENTITY)) { continue; } try { - Entity entity = provider.spawnMob(level, x, y, z, base); + NativeSpawnedEntity entity = provider.spawnMob(normalized); if (entity != null) { return entity; } @@ -347,11 +350,11 @@ public final class ModdedCustomContentRegistry { return null; } - private static Identifier parseIdentifier(String key) { + private static String parseIdentifier(String key) { String trimmed = key.trim(); int bracket = trimmed.indexOf('['); String head = bracket < 0 ? trimmed : trimmed.substring(0, bracket); - return Identifier.tryParse(head); + return NativeResourceKeys.normalize(head); } private static Map parseState(String key) { @@ -376,7 +379,7 @@ public final class ModdedCustomContentRegistry { } private static synchronized void restore(List providers, - Map customBlocks, + Map customBlocks, boolean discoveryComplete) { PROVIDERS.clear(); PROVIDERS.addAll(providers); @@ -391,11 +394,11 @@ public final class ModdedCustomContentRegistry { */ public static final class Discovery { private final List providers; - private final Map customBlocks; + private final Map customBlocks; private final boolean discoveryComplete; private boolean active; - private Discovery(List providers, Map customBlocks, + private Discovery(List providers, Map customBlocks, boolean discoveryComplete, boolean active) { this.providers = providers; this.customBlocks = customBlocks; @@ -423,9 +426,9 @@ public final class ModdedCustomContentRegistry { private static final class DiscoveryBatch { private final List providers; private final List additions = new ArrayList<>(); - private final Map customBlocks; + private final Map customBlocks; - private DiscoveryBatch(List providers, Map customBlocks) { + private DiscoveryBatch(List providers, Map customBlocks) { this.providers = new ArrayList<>(providers); this.customBlocks = new LinkedHashMap<>(customBlocks); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataProvider.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataProvider.java index da6a147df..38b996c77 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataProvider.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataProvider.java @@ -18,9 +18,9 @@ package art.arcane.iris.modded.api; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.Entity; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnedEntity; import java.util.Collection; import java.util.Map; @@ -35,10 +35,10 @@ import java.util.Map; * name; the class needs a public no-argument constructor. *

* Threading. Implementations must be thread-safe. - * {@link #getBlockData(Identifier, Map)} is called from generation threads, potentially many at once, for every + * {@link #getBlockData(String, Map)} is called from generation threads, potentially many at once, for every * unresolved key a pack names - it must be fast and must not touch world state. * {@link #processBlockPlacement(ModdedBlockPlacementContext)} and - * {@link #spawnMob(ServerLevel, double, double, double, Identifier)} are called on the server thread, where + * {@link #spawnMob(NativeEntityRuntime.CustomSpawn)} are called on the server thread, where * touching the level is safe. *

* Failure handling. Iris catches throwables from every callback except {@link #init()} during @@ -64,27 +64,27 @@ public interface ModdedDataProvider { /** * Every identifier this provider can supply for {@code type}. Used for command suggestion and pack tooling, not - * on the resolution path - {@link #isValidProvider(Identifier, ModdedDataType)} decides that. Return an empty + * on the resolution path - {@link #isValidProvider(String, ModdedDataType)} decides that. Return an empty * collection rather than null. */ - Collection getTypes(ModdedDataType type); + Collection getTypes(ModdedDataType type); /** * Whether this provider claims {@code id} for {@code type}. Called before every resolution callback, on * generation threads, so keep it to a set lookup. A cheap namespace check is usually enough. */ - boolean isValidProvider(Identifier id, ModdedDataType type); + boolean isValidProvider(String id, ModdedDataType type); /** * Resolves a claimed block identifier into a concrete block state. *

* {@code state} holds the {@code [prop=value]} pairs from the pack's key, already parsed and possibly empty; * never null. Return null to decline, in which case Iris tries the next provider and finally falls back to air. - * Return {@link ModdedBlockData#deferred(net.minecraft.world.level.block.state.BlockState)} when the real block + * Return {@link ModdedBlockData#deferred(ModdedBlockState)} when the real block * needs a loaded level - Iris then writes the placeholder state and calls * {@link #processBlockPlacement(ModdedBlockPlacementContext)} later. Called from generation threads. */ - default ModdedBlockData getBlockData(Identifier blockId, Map state) { + default ModdedBlockData getBlockData(String blockId, Map state) { return null; } @@ -102,7 +102,7 @@ public interface ModdedDataProvider { * Spawns a claimed custom entity at the given position. Return null to decline and let the next provider try. * Called on the server thread from Iris's entity spawning. */ - default Entity spawnMob(ServerLevel level, double x, double y, double z, Identifier entityId) { + default NativeSpawnedEntity spawnMob(NativeEntityRuntime.CustomSpawn request) { return null; } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataType.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataType.java index 7a0677fb3..124e997f9 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataType.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/api/ModdedDataType.java @@ -24,10 +24,10 @@ package art.arcane.iris.modded.api; * Constants may be added. Switch expressions over this enum need a {@code default} arm. */ public enum ModdedDataType { - /** Block states, resolved through {@link ModdedDataProvider#getBlockData(net.minecraft.resources.Identifier, java.util.Map)}. */ + /** Block states, resolved through {@link ModdedDataProvider#getBlockData(String, java.util.Map)}. */ BLOCK, /** Item types, claimed for loot and pack tooling. */ ITEM, - /** Entity types, spawned through {@link ModdedDataProvider#spawnMob(net.minecraft.server.level.ServerLevel, double, double, double, net.minecraft.resources.Identifier)}. */ + /** Entity types, spawned through {@link ModdedDataProvider#spawnMob(art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime.CustomSpawn)}. */ ENTITY } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java index e74291cb2..110654bb5 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/IrisModdedCommands.java @@ -31,27 +31,26 @@ import art.arcane.iris.pack.PackDownloader; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.modded.ModdedDimensionManager; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldTeleport; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedForcedDatapack; -import art.arcane.iris.modded.ModdedLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; import art.arcane.iris.modded.ModdedScheduler; -import art.arcane.iris.modded.ModdedServerLevels; import art.arcane.iris.modded.ModdedWorldgenIds; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KMap; -import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.tree.LiteralCommandNode; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.network.chat.Component; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.chunk.ChunkGenerator; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; import java.io.File; import java.io.IOException; @@ -69,17 +68,17 @@ public final class IrisModdedCommands { private static final long DOWNLOAD_SHUTDOWN_POLL_SECONDS = 15L; private static final Object DOWNLOAD_MONITOR = new Object(); - static final SuggestionProvider PACK_NAMES = ModdedCommandSuggestions.PACK_NAMES; + static final SuggestionProvider PACK_NAMES = ModdedCommandSuggestions.PACK_NAMES; private static PackDownloadExecution activeDownload; private static boolean downloadAdmissionOpen; private IrisModdedCommands() { } - public static void register(CommandDispatcher dispatcher) { - LiteralCommandNode root = dispatcher.register(ModdedCommandTree.rootTree()); - dispatcher.register(Commands.literal("ir").redirect(root)); - dispatcher.register(Commands.literal("irs").redirect(root)); + public static void register(NativeCommandRegistration dispatcher) { + LiteralCommandNode root = dispatcher.register(ModdedCommandTree.rootTree()); + dispatcher.register(NativeCommandRegistration.literal("ir").redirect(root)); + dispatcher.register(NativeCommandRegistration.literal("irs").redirect(root)); IrisLogging.debug("Iris /iris command tree registered"); } @@ -120,59 +119,55 @@ public final class IrisModdedCommands { } } - static int tp(CommandSourceStack source, ServerLevel level, ServerPlayer target) { - ServerPlayer player = target != null ? target : source.getPlayer(); + static int tp(NativeCommandSource source, NativeWorld level, NativeProtocolPlayer target) { + NativeProtocolPlayer player = target != null ? target : source.player(); if (player == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_PLAYER_IRIS_TP_DIMENSION_PLAYER)); return 0; } - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator)) { - fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS, MessageArgument.untrusted("value", level.dimension().identifier()))); + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) == null) { + fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS, MessageArgument.untrusted("value", level.name()))); return 0; } - String dimensionId = level.dimension().identifier().toString(); - MinecraftServer server = source.getServer(); - CompletableFuture teleport = ModdedDimensionManager.teleportAsync( - player, - server, - dimensionId, - 8.5D, - Double.MIN_VALUE, - 8.5D); + String dimensionId = level.name(); + NativeModdedServer server = source.server(); + CompletableFuture teleport = NativeWorldTeleport.teleport(player, new NativeWorldTeleport.Destination( + server, ModdedDimensionManager.level(server, dimensionId), 8.5D, Double.MIN_VALUE, 8.5D, + System.nanoTime() + TimeUnit.SECONDS.toNanos(10L))); teleport.whenComplete((success, failure) -> { if (Boolean.TRUE.equals(success) && failure == null) { return; } if (failure != null) { ModdedIrisLog.error("Iris teleport into '{}' failed for {}", - dimensionId, player.getUUID(), failure); + dimensionId, player.id(), failure); } server.execute(() -> fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORT_FAILED_DIMENSION_IS_NOT_LOADED, MessageArgument.untrusted("dimensionId", dimensionId)))); }); - ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTING, MessageArgument.untrusted("value", player.getScoreboardName()), MessageArgument.untrusted("dimensionId", dimensionId))); + ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTING, MessageArgument.untrusted("value", player.name()), MessageArgument.untrusted("dimensionId", dimensionId))); return 1; } - static int evacuate(CommandSourceStack source, ServerLevel target) { - MinecraftServer server = source.getServer(); - ServerLevel level = target != null ? target : source.getLevel(); - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator)) { - fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_2, MessageArgument.untrusted("value", level.dimension().identifier()))); + static int evacuate(NativeCommandSource source, NativeWorld target) { + NativeModdedServer server = source.server(); + NativeWorld level = target != null ? target : source.world(); + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) == null) { + fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_2, MessageArgument.untrusted("value", level.name()))); return 0; } - ServerLevel fallback = server.overworld(); - if (fallback == level) { + NativeWorld fallback = server.overworld(); + if (fallback.name().equals(level.name())) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CANNOT_EVACUATE_PRIMARY_WORLD_THERE_IS_NOWHERE_SEND_PLAYERS)); return 0; } - int count = ModdedDimensionManager.evacuate(server, level); - ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_EVACUATED_PLAYER_S_FROM, MessageArgument.untrusted("count", count), MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", fallback.dimension().identifier()))); + int count = NativeWorldTeleport.evacuate(server, level); + ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_EVACUATED_PLAYER_S_FROM, MessageArgument.untrusted("count", count), MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("value2", fallback.name()))); return 1; } - static int debug(CommandSourceStack source) { + static int debug(NativeCommandSource source) { boolean to = !IrisSettings.get().getGeneral().isDebug(); IrisSettings.get().getGeneral().setDebug(to); IrisSettings.get().forceSave(); @@ -180,7 +175,7 @@ public final class IrisModdedCommands { return 1; } - static int reload(CommandSourceStack source) { + static int reload(NativeCommandSource source) { if (IrisSettings.settings != null) { IrisSettings.invalidate(); } @@ -204,26 +199,27 @@ public final class IrisModdedCommands { return 0; } - static int height(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + static int height(NativeCommandSource source) { + NativeWorld level = source.world(); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WORLD_HEIGHT_RANGE, - MessageArgument.trusted("minY", level.getMinY()), - MessageArgument.trusted("maxY", level.getMaxY()))); + MessageArgument.trusted("minY", level.minHeight()), + MessageArgument.trusted("maxY", (level.maxHeight() - 1)))); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WORLD_HEIGHT_TOTAL, - MessageArgument.trusted("height", level.getHeight()))); + MessageArgument.trusted("height", (level.maxHeight() - level.minHeight())))); return 1; } - static int regen(CommandSourceStack source, int radius) { - ServerPlayer player = source.getPlayer(); + static int regen(NativeCommandSource source, int radius) { + NativeProtocolPlayer player = source.player(); if (player == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS)); return 0; } - ServerLevel level = source.getLevel(); - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator irisGenerator)) { + NativeWorld level = source.world(); + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_4)); return 0; } @@ -236,29 +232,29 @@ public final class IrisModdedCommands { return 1; } - static int version(CommandSourceStack source) { - ModdedLoader loader = ModdedEngineBootstrap.loader(); - int engines = engineCount(source.getServer()); + static int version(NativeCommandSource source) { + NativeModdedLoader loader = ModdedEngineBootstrap.loader(); + int engines = engineCount(source.server()); ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IRIS_BY_VOLMIT_SOFTWARE_ON_MINECRAFT_IRIS_DIMENSION_S, MessageArgument.untrusted("value", loader.modVersion()), MessageArgument.untrusted("value2", loader.platformName()), MessageArgument.untrusted("value3", loader.minecraftVersion()), MessageArgument.untrusted("engines", engines))); return 1; } - static int info(CommandSourceStack source, String filter) { - MinecraftServer server = source.getServer(); + static int info(NativeCommandSource source, String filter) { + NativeModdedServer server = source.server(); // The seed is the one field in this listing that is not free to hand a plain player, and /iris worlds // routes here too: emit it only for sources that pass the same gate /iris seed requires. boolean showSeed = ModdedCommandTree.isGamemaster(source); List lines = new ArrayList<>(); int total = 0; int iris = 0; - for (ServerLevel level : ModdedServerLevels.levels(server)) { + for (NativeWorld level : server.worlds()) { total++; - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (!(generator instanceof IrisModdedChunkGenerator irisGenerator)) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { continue; } iris++; - String dimensionId = level.dimension().identifier().toString(); + String dimensionId = level.name(); String irisIdentity = ModdedWorldgenIds.generatorIdentity(irisGenerator.dimensionKey()); if (filter != null && !dimensionId.contains(filter) && !irisIdentity.contains(filter) @@ -274,7 +270,7 @@ public final class IrisModdedCommands { String featureStatus = irisGenerator.importedFeaturesStatus(); lines.add(irisIdentity + ": pack=" + engine.getDimension().getLoadKey() + " world=" + dimensionId - + (showSeed ? " seed=" + level.getSeed() : "") + + (showSeed ? " seed=" + level.seed() : "") + " height=" + engine.getMinHeight() + ".." + engine.getMaxHeight() + " generated=" + engine.getGenerated() + (featureStatus == null ? "" : " importedFeatures=" + featureStatus) @@ -295,30 +291,30 @@ public final class IrisModdedCommands { return 1; } - static int seed(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + static int seed(NativeCommandSource source) { + NativeWorld level = source.world(); Engine engine = engineFor(level); if (engine == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_14)); return 0; } - ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, MessageArgument.untrusted("value", level.getSeed()))); + ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_WORLD_SEED, MessageArgument.untrusted("value", level.seed()))); ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_SEED_MIXED, MessageArgument.untrusted("value", engine.getSeedManager().getSeed()), MessageArgument.untrusted("value2", engine.getSeedManager().getFullMixedSeed()))); return 1; } - static int goldenhash(CommandSourceStack source, int radius, int threads, ModdedGoldenHash.Mode mode) { - ServerLevel level = source.getLevel(); + static int goldenhash(NativeCommandSource source, int radius, int threads, ModdedGoldenHash.Mode mode) { + NativeWorld level = source.world(); Engine engine = engineFor(level); if (engine == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_15)); return 0; } - ModdedGoldenHash.start(source, level, engine, radius, threads, mode); + ModdedGoldenHash.start(source, engine, new ModdedGoldenHash.ScanOptions(radius, threads, mode)); return 1; } - static int download(CommandSourceStack source, String rawRequest) { + static int download(NativeCommandSource source, String rawRequest) { DownloadRequest request = parseDownloadRequest(rawRequest); if (request == null) { fail(source, "Use /iris download pack=overworld, /iris download pack=underworld, or /iris download link=."); @@ -394,7 +390,7 @@ public final class IrisModdedCommands { } private static void executeDownload( - CommandSourceStack source, + NativeCommandSource source, DownloadRequest request, String target, String downloadSource, @@ -438,8 +434,8 @@ public final class IrisModdedCommands { MessageArgument.untrusted("downloadSource", downloadSource)))); } - private static void dispatchDownloadFeedback(CommandSourceStack source, Runnable feedback) { - source.getServer().execute(feedback); + private static void dispatchDownloadFeedback(NativeCommandSource source, Runnable feedback) { + source.server().execute(feedback); } static String downloadBusyMessage(LifecycleOperationCoordinator.ActiveOperation operation) { @@ -507,8 +503,8 @@ public final class IrisModdedCommands { : new DownloadRequest(pack, url, Boolean.TRUE.equals(overwrite)); } - static int metrics(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + static int metrics(NativeCommandSource source) { + NativeWorld level = source.world(); Engine engine = engineFor(level); if (engine == null) { fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_16)); @@ -526,50 +522,50 @@ public final class IrisModdedCommands { return 1; } - static int verifyStructures(CommandSourceStack source, String keyRaw) { + static int verifyStructures(NativeCommandSource source, String keyRaw) { return ModdedLocateCommands.verifyStructures(source, keyRaw); } - static CompletableFuture suggestStructureKeys(CommandContext context, SuggestionsBuilder builder) { + static CompletableFuture suggestStructureKeys(CommandContext context, SuggestionsBuilder builder) { return ModdedCommandSuggestions.suggestStructureKeys(context, builder); } - static void warnTabFailure(String suggestion, CommandSourceStack source, Throwable error) { + static void warnTabFailure(String suggestion, NativeCommandSource source, Throwable error) { ModdedCommandSuggestions.warnTabFailure(suggestion, source, error); } - static Engine engineFor(ServerLevel level) { - ChunkGenerator generator = level.getChunkSource().getGenerator(); - if (generator instanceof IrisModdedChunkGenerator irisGenerator) { + static Engine engineFor(NativeWorld level) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator != null) { try { return irisGenerator.commandEngine(); } catch (Throwable e) { - ModdedIrisLog.error("Iris engine lookup failed for {}", level.dimension().identifier(), e); + ModdedIrisLog.error("Iris engine lookup failed for {}", level.name(), e); return null; } } return null; } - private static int engineCount(MinecraftServer server) { + private static int engineCount(NativeModdedServer server) { int count = 0; - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator) { + for (NativeWorld level : server.worlds()) { + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) != null) { count++; } } return count; } - static void ok(CommandSourceStack source, String message) { + static void ok(NativeCommandSource source, String message) { ModdedCommandFeedback.ok(source, message); } - static void ok(CommandSourceStack source, Component component) { + static void ok(NativeCommandSource source, NativeCommandText component) { ModdedCommandFeedback.ok(source, component); } - static void fail(CommandSourceStack source, String message) { + static void fail(NativeCommandSource source, String message) { ModdedCommandFeedback.fail(source, message); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandFeedback.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandFeedback.java index 334835dfe..969c1d439 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandFeedback.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandFeedback.java @@ -18,18 +18,9 @@ package art.arcane.iris.modded.command; -import net.minecraft.ChatFormatting; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.network.chat.Style; -import net.minecraft.network.chat.TextColor; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText.Format; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.util.Map; import java.util.UUID; @@ -63,52 +54,52 @@ final class ModdedCommandFeedback { private ModdedCommandFeedback() { } - static void ok(CommandSourceStack source, String message) { - source.sendSuccess(() -> Component.literal(message).withStyle(ChatFormatting.GREEN), false); + static void ok(NativeCommandSource source, String message) { + source.sendSuccess(() -> NativeCommandText.literal(message).withStyle(Format.GREEN), false); playSuccess(source); } - static void ok(CommandSourceStack source, Component component) { + static void ok(NativeCommandSource source, NativeCommandText component) { source.sendSuccess(() -> component, false); playSuccess(source); } - static void fail(CommandSourceStack source, String message) { - source.sendFailure(Component.literal(message).withStyle(ChatFormatting.RED)); + static void fail(NativeCommandSource source, String message) { + source.sendFailure(NativeCommandText.literal(message).withStyle(Format.RED)); playFailure(source); } - static void send(CommandSourceStack source, Component component) { + static void send(NativeCommandSource source, NativeCommandText component) { source.sendSuccess(() -> component, false); } - static void clear(CommandSourceStack source) { - if (source.getPlayer() != null) { - send(source, Component.literal("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")); + static void clear(NativeCommandSource source) { + if (source.playerId() != null) { + send(source, NativeCommandText.literal("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")); } } - static MutableComponent header(String title) { - MutableComponent header = Component.empty(); + static NativeCommandText header(String title) { + NativeCommandText header = NativeCommandText.empty(); header.append(text(" ".repeat(18), HEADER_A, false, true)); header.append(text(" " + title + " ", IRIS, true, false)); header.append(text(" ".repeat(18), HEADER_B, false, true)); return header; } - static MutableComponent banner(String title) { + static NativeCommandText banner(String title) { int pad = Math.max(1, 44 - (title.length() + 2) - 4); - MutableComponent banner = Component.empty(); + NativeCommandText banner = NativeCommandText.empty(); banner.append(gradientText("[" + " ".repeat(pad) + "(((", HEADER_A, HEADER_B, true)); - banner.append(Component.literal(" ")); + banner.append(NativeCommandText.literal(" ")); banner.append(gradientText(title, PARAMETER, PARAMETER_ALT, false)); - banner.append(Component.literal(" ")); + banner.append(NativeCommandText.literal(" ")); banner.append(gradientText(")))" + " ".repeat(pad) + "]", HEADER_B, HEADER_A, true)); return banner; } - static MutableComponent gradientText(String value, int from, int to, boolean strikethrough) { - MutableComponent out = Component.empty(); + static NativeCommandText gradientText(String value, int from, int to, boolean strikethrough) { + NativeCommandText out = NativeCommandText.empty(); int steps = Math.max(1, value.length() - 1); for (int index = 0; index < value.length(); index++) { double t = index / (double) steps; @@ -120,17 +111,17 @@ final class ModdedCommandFeedback { return out; } - static MutableComponent footer() { + static NativeCommandText footer() { return text(" ".repeat(PAGE_LINE_LENGTH), HEADER_B, false, true); } - static MutableComponent text(String value, int color) { + static NativeCommandText text(String value, int color) { return text(value, color, false, false); } - static MutableComponent text(String value, int color, boolean bold, boolean strikethrough) { - return Component.literal(value).withStyle((Style style) -> { - Style next = style.withColor(TextColor.fromRgb(color)); + static NativeCommandText text(String value, int color, boolean bold, boolean strikethrough) { + return NativeCommandText.literal(value).withStyle((NativeCommandText.TextStyle style) -> { + NativeCommandText.TextStyle next = style.withColor(color); if (bold) { next = next.withBold(true); } @@ -141,18 +132,17 @@ final class ModdedCommandFeedback { }); } - static MutableComponent button(String label, String command, String hover, boolean runCommand) { - ClickEvent clickEvent = runCommand ? new ClickEvent.RunCommand(command) : new ClickEvent.SuggestCommand(command); - MutableComponent hoverText = text(hover, DESCRIPTION); - return text(label, PARAMETER_ALT, true, false).withStyle((Style style) -> style - .withClickEvent(clickEvent) - .withHoverEvent(new HoverEvent.ShowText(hoverText))); + static NativeCommandText button(String label, String command, String hover, boolean runCommand) { + NativeCommandText hoverText = text(hover, DESCRIPTION); + return text(label, PARAMETER_ALT, true, false).withStyle((NativeCommandText.TextStyle style) -> style + .hover(hoverText)) + .withStyle(style -> runCommand ? style.runCommand(command) : style.suggestCommand(command)); } - static MutableComponent progressBar(double percent, int width) { + static NativeCommandText progressBar(double percent, int width) { double clamped = Math.max(0D, Math.min(100D, percent)); int filled = (int) Math.round((clamped / 100D) * width); - MutableComponent bar = Component.empty(); + NativeCommandText bar = NativeCommandText.empty(); bar.append(text("[", DARK_GREEN)); for (int i = 0; i < width; i++) { bar.append(text(i < filled ? "|" : "·", i < filled ? PARAMETER : OPTIONAL)); @@ -161,35 +151,33 @@ final class ModdedCommandFeedback { return bar; } - static void tab(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); - if (player == null || !claim(TAB_SOUNDS, player.getUUID(), TAB_SOUND_COOLDOWN_MS)) { + static void tab(NativeCommandSource source) { + UUID player = source.playerId(); + if (player == null || !claim(TAB_SOUNDS, player, TAB_SOUND_COOLDOWN_MS)) { return; } - player.level().playSound(null, player.blockPosition(), SoundEvents.ITEM_FRAME_ROTATE_ITEM, SoundSource.PLAYERS, 0.25F, 1.7F); + source.playSound("minecraft:entity.item_frame.rotate_item", 0.25F, 1.7F); } - private static void playSuccess(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); - if (player == null || !claim(MESSAGE_SOUNDS, player.getUUID(), MESSAGE_SOUND_COOLDOWN_MS)) { + private static void playSuccess(NativeCommandSource source) { + UUID player = source.playerId(); + if (player == null || !claim(MESSAGE_SOUNDS, player, MESSAGE_SOUND_COOLDOWN_MS)) { return; } - ServerLevel level = player.level(); - level.playSound(null, player.blockPosition(), SoundEvents.AMETHYST_CLUSTER_BREAK, SoundSource.PLAYERS, 0.77F, 1.65F); - level.playSound(null, player.blockPosition(), SoundEvents.RESPAWN_ANCHOR_CHARGE, SoundSource.PLAYERS, 0.125F, 2.99F); + source.playSound("minecraft:block.amethyst_cluster.break", 0.77F, 1.65F); + source.playSound("minecraft:block.respawn_anchor.charge", 0.125F, 2.99F); } - private static void playFailure(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); - if (player == null || !claim(MESSAGE_SOUNDS, player.getUUID(), MESSAGE_SOUND_COOLDOWN_MS)) { + private static void playFailure(NativeCommandSource source) { + UUID player = source.playerId(); + if (player == null || !claim(MESSAGE_SOUNDS, player, MESSAGE_SOUND_COOLDOWN_MS)) { return; } - ServerLevel level = player.level(); - level.playSound(null, player.blockPosition(), SoundEvents.AMETHYST_CLUSTER_BREAK, SoundSource.PLAYERS, 0.77F, 0.25F); - level.playSound(null, player.blockPosition(), SoundEvents.BEACON_DEACTIVATE, SoundSource.PLAYERS, 0.2F, 0.45F); + source.playSound("minecraft:block.amethyst_cluster.break", 0.77F, 0.25F); + source.playSound("minecraft:block.beacon.deactivate", 0.2F, 0.45F); } private static boolean claim(Map sounds, UUID uuid, long cooldownMs) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandHelp.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandHelp.java index e7e93426b..36474a93f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandHelp.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandHelp.java @@ -24,12 +24,9 @@ import art.arcane.iris.modded.localization.ModdedHelpMessages; import art.arcane.volmlib.util.director.help.DirectorHelpMessages; import art.arcane.volmlib.util.localization.MessageArgument; import art.arcane.volmlib.util.localization.TextKey; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.MutableComponent; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -224,7 +221,7 @@ final class ModdedCommandHelp { return false; } - static int send(CommandSourceStack source, String path) { + static int send(NativeCommandSource source, String path) { Request request = parse(path); List entries = SECTIONS.get(request.section()); if (entries == null) { @@ -237,7 +234,7 @@ final class ModdedCommandHelp { ModdedCommandFeedback.clear(source); - if (source.getPlayer() == null) { + if (source.playerId() == null) { return sendConsole(source, request.section()); } @@ -247,7 +244,7 @@ final class ModdedCommandHelp { int to = Math.min(entries.size(), from + PAGE_SIZE); sendHeader(source, request.section(), page, totalPages); - if (!Commands.hasPermission(Commands.LEVEL_GAMEMASTERS).test(source)) { + if (!NativeCommandRegistration.GAMEMASTERS.test(source)) { ModdedCommandFeedback.send(source, opNotice()); } if (!request.section().isEmpty()) { @@ -260,13 +257,13 @@ final class ModdedCommandHelp { return 1; } - private static int sendConsole(CommandSourceStack source, String section) { + private static int sendConsole(NativeCommandSource source, String section) { sendHeader(source, section, 0, 1); - if (!Commands.hasPermission(Commands.LEVEL_GAMEMASTERS).test(source)) { + if (!NativeCommandRegistration.GAMEMASTERS.test(source)) { ModdedCommandFeedback.send(source, opNotice()); } for (String line : consoleLines(section)) { - ModdedCommandFeedback.send(source, Component.literal(line)); + ModdedCommandFeedback.send(source, NativeCommandText.literal(line)); } return 1; } @@ -299,7 +296,7 @@ final class ModdedCommandHelp { return line.append(" - ").append(IrisLanguage.plain(entry.description())).toString(); } - private static void sendHeader(CommandSourceStack source, String path, int page, int totalPages) { + private static void sendHeader(NativeCommandSource source, String path, int page, int totalPages) { String title = path.isEmpty() ? "/iris" : "/iris " + path; if (totalPages > 1) { title += " {" + (page + 1) + "/" + totalPages + "}"; @@ -307,63 +304,60 @@ final class ModdedCommandHelp { ModdedCommandFeedback.send(source, ModdedCommandFeedback.banner(title)); } - private static MutableComponent backButton(String path) { + private static NativeCommandText backButton(String path) { String parent = parentPath(path); String command = parent.isEmpty() ? "/iris" : "/iris help " + parent; - MutableComponent hover = Component.empty() + NativeCommandText hover = NativeCommandText.empty() .append(text(IrisLanguage.plain( IrisMessages.MODDED_HELP_BACK_HOVER, MessageArgument.untrusted("parent", parent.isEmpty() ? "Iris" : parent) ), DARK_GREEN)); return text("〈 " + IrisLanguage.plain(DirectorHelpMessages.BACK), BACK).withStyle((style) -> style - .withClickEvent(new ClickEvent.RunCommand(command)) - .withHoverEvent(new HoverEvent.ShowText(hover))); + .runCommand(command) + .hover(hover)); } - private static MutableComponent line(String path, Entry entry) { - MutableComponent row = Component.empty(); + private static NativeCommandText line(String path, Entry entry) { + NativeCommandText row = NativeCommandText.empty(); row.append(clickableCommand(path, entry)); row.append(nodes(entry)); return row; } - private static MutableComponent clickableCommand(String path, Entry entry) { + private static NativeCommandText clickableCommand(String path, Entry entry) { String parent = path.isEmpty() ? "/iris" : "/iris " + path; String command = parent + " " + entry.name(); String suggestion = entry.usage().isBlank() ? command : command + " " + entry.usage(); - ClickEvent clickEvent = entry.group() ? new ClickEvent.RunCommand(command) : new ClickEvent.SuggestCommand(suggestion); - MutableComponent hover = entryHover(entry, suggestion); - MutableComponent display = Component.empty(); + NativeCommandText hover = entryHover(entry, suggestion); + NativeCommandText display = NativeCommandText.empty(); display.append(text("⇀", DARK_GREEN)); - display.append(Component.literal(" ")); + display.append(NativeCommandText.literal(" ")); display.append(ModdedCommandFeedback.gradientText(entry.name(), PARAMETER, PARAMETER_ALT, false)); - return display.withStyle((style) -> style - .withClickEvent(clickEvent) - .withHoverEvent(new HoverEvent.ShowText(hover))); + return display.withStyle(style -> (entry.group() ? style.runCommand(command) : style.suggestCommand(suggestion)).hover(hover)); } - private static MutableComponent nodes(Entry entry) { + private static NativeCommandText nodes(Entry entry) { if (entry.group()) { return text(" - " + IrisLanguage.plain(DirectorHelpMessages.CATEGORY), CATEGORY); } List tokens = usageTokens(entry.usage()); if (tokens.isEmpty()) { - return Component.empty(); + return NativeCommandText.empty(); } - MutableComponent nodes = Component.empty(); + NativeCommandText nodes = NativeCommandText.empty(); for (String token : tokens) { - nodes.append(Component.literal(" ")); + nodes.append(NativeCommandText.literal(" ")); nodes.append(parameter(token)); } return nodes; } - private static MutableComponent parameter(String token) { + private static NativeCommandText parameter(String token) { String name = parameterName(token); boolean required = token.startsWith("<"); - MutableComponent title = Component.empty(); + NativeCommandText title = NativeCommandText.empty(); if (required) { title.append(text("[", REQUIRED, true, false)); title.append(text(name, PARAMETER, false, false)); @@ -374,12 +368,12 @@ final class ModdedCommandHelp { title.append(text("⊱", OPTIONAL)); } - MutableComponent hover = Component.empty(); + NativeCommandText hover = NativeCommandText.empty(); hover.append(text(name, PARAMETER)); - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); hover.append(text("✎ ", DESCRIPTION_ICON)); hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_COMMAND_PARAMETER), DESCRIPTION)); - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); if (required) { hover.append(text("⚠ ", REQUIRED)); hover.append(text(IrisLanguage.plain(DirectorHelpMessages.REQUIRED), REQUIRED_TEXT)); @@ -387,20 +381,20 @@ final class ModdedCommandHelp { hover.append(text("✔ ", DESCRIPTION_ICON)); hover.append(text(IrisLanguage.plain(DirectorHelpMessages.OPTIONAL), USAGE)); } - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); hover.append(text("✢ ", DARK_GREEN)); hover.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_BRIGADIER_TEXT), HOVER_TYPE)); - return title.withStyle((style) -> style.withHoverEvent(new HoverEvent.ShowText(hover))); + return title.withStyle((style) -> style.hover(hover)); } - private static MutableComponent entryHover(Entry entry, String suggestion) { - MutableComponent hover = Component.empty(); + private static NativeCommandText entryHover(Entry entry, String suggestion) { + NativeCommandText hover = NativeCommandText.empty(); hover.append(text(names(entry), PARAMETER)); - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); hover.append(text("✎ ", DESCRIPTION_ICON)); hover.append(text(IrisLanguage.plain(entry.description()), DESCRIPTION)); - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); hover.append(text("✒ ", USAGE_ICON)); if (entry.group()) { hover.append(text(IrisLanguage.plain(DirectorHelpMessages.COMMAND_GROUP), USAGE)); @@ -408,15 +402,15 @@ final class ModdedCommandHelp { hover.append(text(IrisLanguage.plain(DirectorHelpMessages.NO_PARAMETERS), USAGE)); } else { hover.append(text(IrisLanguage.plain(DirectorHelpMessages.PARAMETERS_HOVER), USAGE)); - hover.append(Component.literal("\n")); + hover.append(NativeCommandText.literal("\n")); hover.append(text("✦ ", EXAMPLE_ICON)); hover.append(text(suggestion, PARAMETER)); } return hover; } - private static MutableComponent opNotice() { - MutableComponent notice = Component.empty(); + private static NativeCommandText opNotice() { + NativeCommandText notice = NativeCommandText.empty(); notice.append(text("⚠ ", REQUIRED)); notice.append(text(IrisLanguage.plain(IrisMessages.MODDED_HELP_OPERATOR_NOTICE) + " ", REQUIRED_TEXT)); notice.append(text(IrisLanguage.plain( @@ -426,8 +420,8 @@ final class ModdedCommandHelp { return notice; } - private static MutableComponent footer(String section, int page, int totalPages) { - MutableComponent footer = Component.empty(); + private static NativeCommandText footer(String section, int page, int totalPages) { + NativeCommandText footer = NativeCommandText.empty(); int fill = ModdedCommandFeedback.PAGE_LINE_LENGTH; boolean hasPrevious = page > 0; boolean hasNext = page + 1 < totalPages; @@ -435,7 +429,7 @@ final class ModdedCommandHelp { if (hasPrevious) { fill -= PAGE_BUTTON_WIDTH; footer.append(pageButton(section, page, false)); - footer.append(Component.literal(" ")); + footer.append(NativeCommandText.literal(" ")); } if (hasNext) { fill -= PAGE_BUTTON_WIDTH; @@ -449,29 +443,29 @@ final class ModdedCommandHelp { )); if (hasNext) { - footer.append(Component.literal(" ")); + footer.append(NativeCommandText.literal(" ")); footer.append(pageButton(section, page + 2, true)); } return footer; } - private static MutableComponent pageButton(String section, int target, boolean next) { + private static NativeCommandText pageButton(String section, int target, boolean next) { String command = "/iris help " + (section.isEmpty() ? "" : section + " ") + target; String pageWord = IrisLanguage.plain(DirectorHelpMessages.PAGE); String label = next ? pageWord + " " + target + " ❭" : "〈 " + pageWord + " " + target; - MutableComponent hover = text(IrisLanguage.plain(next + NativeCommandText hover = text(IrisLanguage.plain(next ? DirectorHelpMessages.NEXT_PAGE : DirectorHelpMessages.PREVIOUS_PAGE), DESCRIPTION); return text(label, next ? PARAMETER_ALT : PARAMETER).withStyle((style) -> style - .withClickEvent(new ClickEvent.RunCommand(command)) - .withHoverEvent(new HoverEvent.ShowText(hover))); + .runCommand(command) + .hover(hover)); } - private static MutableComponent text(String value, int color) { + private static NativeCommandText text(String value, int color) { return ModdedCommandFeedback.text(value, color, false, false); } - private static MutableComponent text(String value, int color, boolean bold, boolean strikethrough) { + private static NativeCommandText text(String value, int color, boolean bold, boolean strikethrough) { return ModdedCommandFeedback.text(value, color, bold, strikethrough); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandSuggestions.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandSuggestions.java index c31134ef6..5e7424711 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandSuggestions.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandSuggestions.java @@ -32,18 +32,14 @@ import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.modded.ModdedEngineBootstrap; -import art.arcane.iris.modded.ModdedServerLevels; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.levelgen.structure.Structure; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; import java.io.File; import java.util.ArrayList; @@ -60,14 +56,14 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; final class ModdedCommandSuggestions { - static final SuggestionProvider BIOME_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestBiomeKeys(context, builder); - static final SuggestionProvider REGION_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestRegionKeys(context, builder); - static final SuggestionProvider OBJECT_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestObjectKeys(context, builder); - static final SuggestionProvider STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestStructureKeys(context, builder); - static final SuggestionProvider POI_TYPES = (CommandContext context, SuggestionsBuilder builder) -> SharedSuggestionProvider.suggest(List.of("buried_treasure"), builder); - static final SuggestionProvider HYDROLOGY_TYPES = (CommandContext context, SuggestionsBuilder builder) -> suggestHydrologyTypes(context, builder); - static final SuggestionProvider PACK_NAMES = (CommandContext context, SuggestionsBuilder builder) -> suggestPackNames(context, builder); - static final SuggestionProvider DIMENSION_NAMES = (CommandContext context, SuggestionsBuilder builder) -> suggestDimensionNames(context, builder); + static final SuggestionProvider BIOME_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestBiomeKeys(context, builder); + static final SuggestionProvider REGION_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestRegionKeys(context, builder); + static final SuggestionProvider OBJECT_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestObjectKeys(context, builder); + static final SuggestionProvider STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestStructureKeys(context, builder); + static final SuggestionProvider POI_TYPES = (CommandContext context, SuggestionsBuilder builder) -> NativeCommandRegistration.suggest(List.of("buried_treasure"), builder); + static final SuggestionProvider HYDROLOGY_TYPES = (CommandContext context, SuggestionsBuilder builder) -> suggestHydrologyTypes(context, builder); + static final SuggestionProvider PACK_NAMES = (CommandContext context, SuggestionsBuilder builder) -> suggestPackNames(context, builder); + static final SuggestionProvider DIMENSION_NAMES = (CommandContext context, SuggestionsBuilder builder) -> suggestDimensionNames(context, builder); private static final int TAB_FAILURE_KEYS_MAX = 256; private static final Set REPORTED_TAB_FAILURES = ConcurrentHashMap.newKeySet(); @@ -78,12 +74,12 @@ final class ModdedCommandSuggestions { private ModdedCommandSuggestions() { } - private static CompletableFuture suggestBiomeKeys(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestBiomeKeys(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null) { - return SharedSuggestionProvider.suggest(reachableBiomeKeys(engine), builder); + return NativeCommandRegistration.suggest(reachableBiomeKeys(engine), builder); } } catch (Throwable e) { warnTabFailure("biome keys", context.getSource(), e); @@ -92,20 +88,20 @@ final class ModdedCommandSuggestions { } private static CompletableFuture suggestHydrologyTypes( - CommandContext context, + CommandContext context, SuggestionsBuilder builder ) { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null && engine.getComplex().getHydrologyRuntime() != null) { - return SharedSuggestionProvider.suggest( + return NativeCommandRegistration.suggest( engine.getComplex().getHydrologyRuntime().featureQueryKeys(), builder); } } catch (Throwable error) { warnTabFailure("hydrology types", context.getSource(), error); } - return SharedSuggestionProvider.suggest( + return NativeCommandRegistration.suggest( HydrologyFeatureQuery.suggestions(List.of()), builder); } @@ -131,12 +127,12 @@ final class ModdedCommandSuggestions { return biomeKey != null && reachableBiomeKeys(biomes).contains(biomeKey.trim()); } - private static CompletableFuture suggestRegionKeys(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestRegionKeys(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null) { - return SharedSuggestionProvider.suggest(GenerationFindCatalog.regions(engine).stream() + return NativeCommandRegistration.suggest(GenerationFindCatalog.regions(engine).stream() .map(region -> region.getLoadKey()), builder); } } catch (Throwable e) { @@ -145,12 +141,12 @@ final class ModdedCommandSuggestions { return builder.buildFuture(); } - private static CompletableFuture suggestObjectKeys(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestObjectKeys(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null) { - return SharedSuggestionProvider.suggest(GenerationFindCatalog.objectKeys(engine), builder); + return NativeCommandRegistration.suggest(GenerationFindCatalog.objectKeys(engine), builder); } } catch (Throwable e) { warnTabFailure("object keys", context.getSource(), e); @@ -158,24 +154,23 @@ final class ModdedCommandSuggestions { return builder.buildFuture(); } - static CompletableFuture suggestStructureKeys(CommandContext context, SuggestionsBuilder builder) { - CommandSourceStack source = context.getSource(); + static CompletableFuture suggestStructureKeys(CommandContext context, SuggestionsBuilder builder) { + NativeCommandSource source = context.getSource(); ModdedCommandFeedback.tab(source); try { - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { return builder.buildFuture(); } boolean nativeGenerationEnabled = - source.getServer().getWorldGenSettings().options().generateStructures(); + source.server().generateStructures(); Collection irisKeys = IrisStructureLocator.locatableEditableKeys(engine); Set reachableNativeKeys = StructureReachability.reachableKeys(engine); - Registry registry = source.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE); - List nativeKeys = new ArrayList<>(registry.keySet().size()); - Set registeredKeys = new HashSet<>(registry.keySet().size()); - for (Identifier identifier : registry.keySet()) { - String key = identifier.toString(); + List registered = source.server().structureKeys(); + List nativeKeys = new ArrayList<>(registered.size()); + Set registeredKeys = new HashSet<>(registered.size()); + for (String key : registered) { registeredKeys.add(normalizeKey(key)); IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, key, false); boolean nativePlacement = IrisStructureLocator.hasNativePlacement(engine, key); @@ -194,7 +189,7 @@ final class ModdedCommandSuggestions { irisKeys, registeredKeys, (String candidate) -> IrisStructureLocator.hasNativePlacement(engine, candidate)); nativeKeys.addAll(GenerationFindCatalog.retainedStructureKeys(engine)); - return SharedSuggestionProvider.suggest( + return NativeCommandRegistration.suggest( combineStructureKeys(unregisteredIrisKeys, nativeKeys), builder); } catch (Throwable e) { warnTabFailure("structure keys", source, e); @@ -216,7 +211,7 @@ final class ModdedCommandSuggestions { && (reachable || nativePlacement && locatableNativePlacement); } - static void warnTabFailure(String suggestion, CommandSourceStack source, Throwable error) { + static void warnTabFailure(String suggestion, NativeCommandSource source, Throwable error) { String origin = tabOrigin(source); if (!REPORTED_TAB_FAILURES.add(suggestion + '|' + origin + '|' + error.getClass().getName())) { return; @@ -227,12 +222,12 @@ final class ModdedCommandSuggestions { ModdedIrisLog.warn("Iris tab-complete for {} in {} failed; suggestions will be empty", suggestion, origin, error); } - private static String tabOrigin(CommandSourceStack source) { + private static String tabOrigin(NativeCommandSource source) { if (source == null) { return ""; } try { - return source.getLevel().dimension().identifier().toString(); + return source.world().name(); } catch (Throwable originFailure) { return ""; } @@ -273,14 +268,14 @@ final class ModdedCommandSuggestions { return key == null ? "" : key.trim().toLowerCase(Locale.ROOT); } - private static CompletableFuture suggestPackNames(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestPackNames(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); // Suggestion packets arrive per keystroke; a short-lived snapshot keeps the directory // walk off the hot path without ever serving stale names for more than a few seconds. long now = System.currentTimeMillis(); Set cached = cachedPackNames; if (cached != null && now - cachedPackNamesAt < PACK_NAME_CACHE_TTL_MS) { - return SharedSuggestionProvider.suggest(cached, builder); + return NativeCommandRegistration.suggest(cached, builder); } Set names = new TreeSet<>(); names.add("overworld"); @@ -305,17 +300,17 @@ final class ModdedCommandSuggestions { } cachedPackNames = names; cachedPackNamesAt = now; - return SharedSuggestionProvider.suggest(names, builder); + return NativeCommandRegistration.suggest(names, builder); } - private static CompletableFuture suggestDimensionNames(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestDimensionNames(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); List names = new ArrayList<>(); - for (ServerLevel level : ModdedServerLevels.levels(context.getSource().getServer())) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator) { - names.add(level.dimension().identifier().toString()); + for (NativeWorld level : context.getSource().server().worlds()) { + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) != null) { + names.add(level.name()); } } - return SharedSuggestionProvider.suggest(names, builder); + return NativeCommandRegistration.suggest(names, builder); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandTree.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandTree.java index 555e785d4..b8120e1ec 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandTree.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedCommandTree.java @@ -21,7 +21,6 @@ package art.arcane.iris.modded.command; import art.arcane.volmlib.util.localization.LanguageAudience; import com.mojang.brigadier.Command; import com.mojang.brigadier.arguments.IntegerArgumentType; -import net.minecraft.server.level.ServerPlayer; import com.mojang.brigadier.arguments.LongArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.ArgumentBuilder; @@ -29,32 +28,29 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.commands.arguments.DimensionArgument; -import net.minecraft.commands.arguments.EntityArgument; -import net.minecraft.resources.Identifier; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandArguments; import java.util.List; import java.util.function.Predicate; final class ModdedCommandTree { - private static final SuggestionProvider DOWNLOAD_SOURCES = + private static final SuggestionProvider DOWNLOAD_SOURCES = (context, builder) -> - SharedSuggestionProvider.suggest( + NativeCommandRegistration.suggest( List.of("pack=overworld", "pack=underworld", "link=", "pack=overworld overwrite=true", "pack=underworld overwrite=true"), builder ); - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; /** * SP-4: read-only inspection must work for an unopped player in a no-cheats singleplayer world, where the * what/height overlays are the only way to see what Iris generated. Everything that mutates a world, * downloads, opens studio or starts a pregen stays on GATE, and so does the world seed: it is the one * read-only value a plain player must not be handed, so /iris seed is gated and info/worlds omit the seed - * field for anyone who fails {@link #isGamemaster(CommandSourceStack)}. + * field for anyone who fails {@link #isGamemaster(NativeCommandSource)}. */ - private static final Predicate READ_ONLY = Commands.hasPermission(Commands.LEVEL_ALL); + private static final Predicate READ_ONLY = NativeCommandRegistration.ALL; private ModdedCommandTree() { } @@ -62,24 +58,24 @@ final class ModdedCommandTree { /** * Same gate the mutating subtrees use, for output that mixes gated and ungated fields in one command. */ - static boolean isGamemaster(CommandSourceStack source) { + static boolean isGamemaster(NativeCommandSource source) { return GATE.test(source); } - static LiteralArgumentBuilder rootTree() { - LiteralArgumentBuilder root = Commands.literal("iris"); + static LiteralArgumentBuilder rootTree() { + LiteralArgumentBuilder root = NativeCommandRegistration.literal("iris"); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), ""))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), ""))); root.then(helpTree()); root.then(ModdedLanguageCommands.tree()); - root.then(Commands.literal("version").requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.version(context.getSource())))); + root.then(NativeCommandRegistration.literal("version").requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.version(context.getSource())))); - root.then(Commands.literal("info").requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null))) - .then(Commands.argument("dimension", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), StringArgumentType.getString(context, "dimension")))))); + root.then(NativeCommandRegistration.literal("info").requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null))) + .then(NativeCommandRegistration.argument("dimension", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), StringArgumentType.getString(context, "dimension")))))); // ModdedWhatCommands.tree() gates itself at LEVEL_GAMEMASTERS and keeps that gate: // "what markers" drives lease-gated 9x9 mantle scans, and the Bukkit twin puts the @@ -89,28 +85,28 @@ final class ModdedCommandTree { root.then(teleportTree("teleport")); root.then(teleportTree("tp")); - root.then(Commands.literal("evacuate").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.evacuate(context.getSource(), null))) - .then(Commands.argument("dimension", DimensionArgument.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.evacuate(context.getSource(), DimensionArgument.getDimension(context, "dimension")))))); + root.then(NativeCommandRegistration.literal("evacuate").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.evacuate(context.getSource(), null))) + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.evacuate(context.getSource(), NativeCommandArguments.getDimension(context, "dimension")))))); - root.then(Commands.literal("debug").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.debug(context.getSource())))); + root.then(NativeCommandRegistration.literal("debug").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.debug(context.getSource())))); - root.then(Commands.literal("reload").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.reload(context.getSource())))); - root.then(Commands.literal("height").requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.height(context.getSource())))); - root.then(Commands.literal("worlds").requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null)))); - root.then(Commands.literal("accesslist").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null)))); + root.then(NativeCommandRegistration.literal("reload").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.reload(context.getSource())))); + root.then(NativeCommandRegistration.literal("height").requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.height(context.getSource())))); + root.then(NativeCommandRegistration.literal("worlds").requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null)))); + root.then(NativeCommandRegistration.literal("accesslist").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.info(context.getSource(), null)))); root.then(gotoTree("goto")); root.then(gotoTree("find")); - root.then(Commands.literal("seed").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.seed(context.getSource())))); + root.then(NativeCommandRegistration.literal("seed").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.seed(context.getSource())))); root.then(goldenhashTree("goldenhash")); root.then(goldenhashTree("gold")); @@ -127,12 +123,12 @@ final class ModdedCommandTree { root.then(pregenTree("pregen")); root.then(pregenTree("pregenerate")); - root.then(Commands.literal("wand").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveWand(context.getSource())))); - root.then(Commands.literal("dust").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveDust(context.getSource())))); - root.then(Commands.literal("d").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveDust(context.getSource())))); + root.then(NativeCommandRegistration.literal("wand").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveWand(context.getSource())))); + root.then(NativeCommandRegistration.literal("dust").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveDust(context.getSource())))); + root.then(NativeCommandRegistration.literal("d").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedObjectCommands.giveDust(context.getSource())))); root.then(ModdedObjectCommands.tree("object")); root.then(ModdedObjectCommands.tree("o")); root.then(editTree()); @@ -159,138 +155,137 @@ final class ModdedCommandTree { return root; } - static Command localized(Command command) { + static Command localized(Command command) { return context -> { - ServerPlayer player = context.getSource().getPlayer(); - try (LanguageAudience.Scope audience = LanguageAudience.open(player == null ? null : player.getUUID())) { + try (LanguageAudience.Scope audience = LanguageAudience.open(context.getSource().playerId())) { return command.run(context); } }; } - private static LiteralArgumentBuilder createTree(String name) { - return Commands.literal(name).requires(GATE) - .then(Commands.argument("name", StringArgumentType.word()) - .executes(ModdedCommandTree.localized((CommandContext context) -> + private static LiteralArgumentBuilder createTree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .then(NativeCommandRegistration.argument("name", StringArgumentType.word()) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedWorldCommands.createWorld( context.getSource(), StringArgumentType.getString(context, "name"), "overworld", 1337L))) - .then(Commands.argument("pack", StringArgumentType.string()).suggests(ModdedCommandSuggestions.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedWorldCommands.createWorld(context.getSource(), + .then(NativeCommandRegistration.argument("pack", StringArgumentType.string()).suggests(ModdedCommandSuggestions.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedWorldCommands.createWorld(context.getSource(), StringArgumentType.getString(context, "name"), StringArgumentType.getString(context, "pack"), 1337L))) - .then(Commands.argument("seed", LongArgumentType.longArg()) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedWorldCommands.createWorld(context.getSource(), + .then(NativeCommandRegistration.argument("seed", LongArgumentType.longArg()) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedWorldCommands.createWorld(context.getSource(), StringArgumentType.getString(context, "name"), StringArgumentType.getString(context, "pack"), LongArgumentType.getLong(context, "seed"))))))); } - private static LiteralArgumentBuilder teleportTree(String name) { - return Commands.literal(name).requires(GATE) - .then(Commands.argument("dimension", DimensionArgument.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> - IrisModdedCommands.tp(context.getSource(), DimensionArgument.getDimension(context, "dimension"), null))) - .then(Commands.argument("player", EntityArgument.player()) - .executes(ModdedCommandTree.localized((CommandContext context) -> - IrisModdedCommands.tp(context.getSource(), DimensionArgument.getDimension(context, "dimension"), - EntityArgument.getPlayer(context, "player")))))); + private static LiteralArgumentBuilder teleportTree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> + IrisModdedCommands.tp(context.getSource(), NativeCommandArguments.getDimension(context, "dimension"), null))) + .then(NativeCommandRegistration.argument("player", NativeCommandArguments.player()) + .executes(ModdedCommandTree.localized((CommandContext context) -> + IrisModdedCommands.tp(context.getSource(), NativeCommandArguments.getDimension(context, "dimension"), + NativeCommandArguments.getPlayer(context, "player")))))); } - private static LiteralArgumentBuilder helpTree() { - return Commands.literal("help").requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), ""))) - .then(Commands.argument("section", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), StringArgumentType.getString(context, "section"))))); + private static LiteralArgumentBuilder helpTree() { + return NativeCommandRegistration.literal("help").requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), ""))) + .then(NativeCommandRegistration.argument("section", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), StringArgumentType.getString(context, "section"))))); } - private static LiteralArgumentBuilder downloadTree(String name) { - return Commands.literal(name).requires(GATE) - .then(Commands.argument("source", StringArgumentType.greedyString()).suggests(DOWNLOAD_SOURCES) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.download( + private static LiteralArgumentBuilder downloadTree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .then(NativeCommandRegistration.argument("source", StringArgumentType.greedyString()).suggests(DOWNLOAD_SOURCES) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.download( context.getSource(), StringArgumentType.getString(context, "source") )))); } - private static LiteralArgumentBuilder metricsTree(String name) { - return Commands.literal(name).requires(READ_ONLY) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.metrics(context.getSource()))); + private static LiteralArgumentBuilder metricsTree(String name) { + return NativeCommandRegistration.literal(name).requires(READ_ONLY) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.metrics(context.getSource()))); } - private static LiteralArgumentBuilder regenTree(String name) { - return Commands.literal(name).requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.regen(context.getSource(), 0))) - .then(Commands.argument("radius", IntegerArgumentType.integer(0, 64)) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.regen(context.getSource(), IntegerArgumentType.getInteger(context, "radius"))))); + private static LiteralArgumentBuilder regenTree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.regen(context.getSource(), 0))) + .then(NativeCommandRegistration.argument("radius", IntegerArgumentType.integer(0, 64)) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.regen(context.getSource(), IntegerArgumentType.getInteger(context, "radius"))))); } - private static LiteralArgumentBuilder gotoTree(String name) { - return Commands.literal(name).requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))) - .then(Commands.literal("biome") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("region") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("object") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoObject(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("river") - .then(Commands.argument("type", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.HYDROLOGY_TYPES) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoRiver(context.getSource(), StringArgumentType.getString(context, "type")))))) - .then(Commands.literal("unregistered") - .executes(ModdedCommandTree.localized((CommandContext context) -> + private static LiteralArgumentBuilder gotoTree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))) + .then(NativeCommandRegistration.literal("biome") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("region") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("object") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoObject(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("river") + .then(NativeCommandRegistration.argument("type", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.HYDROLOGY_TYPES) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoRiver(context.getSource(), StringArgumentType.getString(context, "type")))))) + .then(NativeCommandRegistration.literal("unregistered") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedUnregisteredStructures.print(context.getSource())))) - .then(Commands.literal("structure") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.STRUCTURE_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoStructure(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("poi") - .then(Commands.argument("type", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.POI_TYPES) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoPoi(context.getSource(), StringArgumentType.getString(context, "type")))))); + .then(NativeCommandRegistration.literal("structure") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.STRUCTURE_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoStructure(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("poi") + .then(NativeCommandRegistration.argument("type", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.POI_TYPES) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedLocateCommands.gotoPoi(context.getSource(), StringArgumentType.getString(context, "type")))))); } - private static LiteralArgumentBuilder pregenTree(String name) { - RequiredArgumentBuilder radius = Commands.argument("radius", IntegerArgumentType.integer(1, 100000)) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, false, false, false, false, false))); + private static LiteralArgumentBuilder pregenTree(String name) { + RequiredArgumentBuilder radius = NativeCommandRegistration.argument("radius", IntegerArgumentType.integer(1, 100000)) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, false, false, false, false, false))); attachPregenCenter(radius, false); attachPregenFlags(radius, false, false, false, false, false); - RequiredArgumentBuilder dimension = Commands.argument("dimension", DimensionArgument.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, true, false, false, false, false))); + RequiredArgumentBuilder dimension = NativeCommandRegistration.argument("dimension", NativeCommandArguments.dimension()).suggests(ModdedCommandSuggestions.DIMENSION_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, true, false, false, false, false))); attachPregenCenter(dimension, true); attachPregenFlags(dimension, true, false, false, false, false); radius.then(dimension); - return Commands.literal(name).requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))) - .then(Commands.literal("start") + return NativeCommandRegistration.literal(name).requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))) + .then(NativeCommandRegistration.literal("start") .then(radius)) - .then(Commands.literal("stop") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStop(context.getSource())))) - .then(Commands.literal("x") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStop(context.getSource())))) - .then(Commands.literal("pause") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenPause(context.getSource())))) - .then(Commands.literal("resume") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenPause(context.getSource())))) - .then(Commands.literal("status") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStatus(context.getSource())))); + .then(NativeCommandRegistration.literal("stop") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStop(context.getSource())))) + .then(NativeCommandRegistration.literal("x") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStop(context.getSource())))) + .then(NativeCommandRegistration.literal("pause") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenPause(context.getSource())))) + .then(NativeCommandRegistration.literal("resume") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenPause(context.getSource())))) + .then(NativeCommandRegistration.literal("status") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStatus(context.getSource())))); } - private static void attachPregenCenter(ArgumentBuilder node, boolean withDimension) { - RequiredArgumentBuilder z = Commands.argument("z", IntegerArgumentType.integer()) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, withDimension, true, false, false, false))); + private static void attachPregenCenter(ArgumentBuilder node, boolean withDimension) { + RequiredArgumentBuilder z = NativeCommandRegistration.argument("z", IntegerArgumentType.integer()) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, withDimension, true, false, false, false))); attachPregenFlags(z, withDimension, true, false, false, false); - node.then(Commands.literal("at") - .then(Commands.argument("x", IntegerArgumentType.integer()) + node.then(NativeCommandRegistration.literal("at") + .then(NativeCommandRegistration.argument("x", IntegerArgumentType.integer()) .then(z))); } - private static void attachPregenFlags(ArgumentBuilder node, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) { + private static void attachPregenFlags(ArgumentBuilder node, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) { if (!gui) { node.then(pregenFlagNode("gui", withDimension, withCenter, true, sync, nocache)); } @@ -302,25 +297,25 @@ final class ModdedCommandTree { } } - private static LiteralArgumentBuilder pregenFlagNode(String name, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) { - LiteralArgumentBuilder flag = Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, withDimension, withCenter, gui, sync, nocache))); + private static LiteralArgumentBuilder pregenFlagNode(String name, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) { + LiteralArgumentBuilder flag = NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedPregenCommands.pregenStart(context, withDimension, withCenter, gui, sync, nocache))); attachPregenFlags(flag, withDimension, withCenter, gui, sync, nocache); return flag; } - private static LiteralArgumentBuilder goldenhashTree(String name) { - LiteralArgumentBuilder radiusAndThreads = Commands.literal(name).requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), 8, 8, ModdedGoldenHash.Mode.AUTO))); - attachModes(radiusAndThreads, (CommandContext context) -> 8, (CommandContext context) -> 8); + private static LiteralArgumentBuilder goldenhashTree(String name) { + LiteralArgumentBuilder radiusAndThreads = NativeCommandRegistration.literal(name).requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), 8, 8, ModdedGoldenHash.Mode.AUTO))); + attachModes(radiusAndThreads, (CommandContext context) -> 8, (CommandContext context) -> 8); - com.mojang.brigadier.builder.RequiredArgumentBuilder radius = Commands.argument("radius", IntegerArgumentType.integer(0, 256)) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), IntegerArgumentType.getInteger(context, "radius"), 8, ModdedGoldenHash.Mode.AUTO))); - attachModes(radius, (CommandContext context) -> IntegerArgumentType.getInteger(context, "radius"), (CommandContext context) -> 8); + com.mojang.brigadier.builder.RequiredArgumentBuilder radius = NativeCommandRegistration.argument("radius", IntegerArgumentType.integer(0, 256)) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), IntegerArgumentType.getInteger(context, "radius"), 8, ModdedGoldenHash.Mode.AUTO))); + attachModes(radius, (CommandContext context) -> IntegerArgumentType.getInteger(context, "radius"), (CommandContext context) -> 8); - com.mojang.brigadier.builder.RequiredArgumentBuilder threads = Commands.argument("threads", IntegerArgumentType.integer(1, 64)) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), IntegerArgumentType.getInteger(context, "radius"), IntegerArgumentType.getInteger(context, "threads"), ModdedGoldenHash.Mode.AUTO))); - attachModes(threads, (CommandContext context) -> IntegerArgumentType.getInteger(context, "radius"), (CommandContext context) -> IntegerArgumentType.getInteger(context, "threads")); + com.mojang.brigadier.builder.RequiredArgumentBuilder threads = NativeCommandRegistration.argument("threads", IntegerArgumentType.integer(1, 64)) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), IntegerArgumentType.getInteger(context, "radius"), IntegerArgumentType.getInteger(context, "threads"), ModdedGoldenHash.Mode.AUTO))); + attachModes(threads, (CommandContext context) -> IntegerArgumentType.getInteger(context, "radius"), (CommandContext context) -> IntegerArgumentType.getInteger(context, "threads")); radius.then(threads); radiusAndThreads.then(radius); @@ -328,38 +323,38 @@ final class ModdedCommandTree { } private interface IntExtractor { - int extract(CommandContext context); + int extract(CommandContext context); } - private static void attachModes(com.mojang.brigadier.builder.ArgumentBuilder node, IntExtractor radius, IntExtractor threads) { - node.then(Commands.literal("capture") - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), radius.extract(context), threads.extract(context), ModdedGoldenHash.Mode.CAPTURE)))); - node.then(Commands.literal("verify") - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), radius.extract(context), threads.extract(context), ModdedGoldenHash.Mode.VERIFY)))); + private static void attachModes(com.mojang.brigadier.builder.ArgumentBuilder node, IntExtractor radius, IntExtractor threads) { + node.then(NativeCommandRegistration.literal("capture") + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), radius.extract(context), threads.extract(context), ModdedGoldenHash.Mode.CAPTURE)))); + node.then(NativeCommandRegistration.literal("verify") + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.goldenhash(context.getSource(), radius.extract(context), threads.extract(context), ModdedGoldenHash.Mode.VERIFY)))); } - private static LiteralArgumentBuilder editTree() { - return Commands.literal("edit").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), "edit"))) - .then(Commands.literal("biome") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), null))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("b") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), null))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("region") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), null))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("r") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), null))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) - .then(Commands.literal("dimension") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editDimension(context.getSource())))) - .then(Commands.literal("d") - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editDimension(context.getSource())))); + private static LiteralArgumentBuilder editTree() { + return NativeCommandRegistration.literal("edit").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), "edit"))) + .then(NativeCommandRegistration.literal("biome") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), null))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("b") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), null))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.BIOME_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editBiome(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("region") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), null))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("r") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), null))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ModdedCommandSuggestions.REGION_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editRegion(context.getSource(), StringArgumentType.getString(context, "key")))))) + .then(NativeCommandRegistration.literal("dimension") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editDimension(context.getSource())))) + .then(NativeCommandRegistration.literal("d") + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedEditCommands.editDimension(context.getSource())))); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java index ee7dd9f9e..ddb84d22f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java @@ -20,22 +20,21 @@ package art.arcane.iris.modded.command; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.pack.PackDirectoryResolver; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedServerLevels; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; import art.arcane.volmlib.util.collection.KList; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelResource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldDimensions; import java.io.File; import java.io.IOException; @@ -55,27 +54,27 @@ import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedDatapackCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private static final String WORLD_PACK_NAME = "iris"; private ModdedDatapackCommands() { } - public static LiteralArgumentBuilder tree(String name) { - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + public static LiteralArgumentBuilder tree(String name) { + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); - root.then(Commands.literal("status") - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); + root.then(NativeCommandRegistration.literal("status") + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); - root.then(Commands.literal("install") - .executes(ModdedCommandTree.localized((CommandContext context) -> install(context.getSource())))); + root.then(NativeCommandRegistration.literal("install") + .executes(ModdedCommandTree.localized((CommandContext context) -> install(context.getSource())))); - root.then(Commands.literal("list") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); - root.then(Commands.literal("ls") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("list") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("ls") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); root.then(message("ingest", "Managed Modrinth datapack ingest is Bukkit-only. On modded servers install the datapack folder or zip in world/datapacks, enable it, restart, then use /iris datapack list to confirm it is enabled. Registered structures generate natively in Iris dimensions.")); root.then(message("pull", "Managed Modrinth datapack ingest is Bukkit-only. On modded servers install the datapack folder or zip in world/datapacks, enable it, restart, then use /iris datapack list to confirm it is enabled. Registered structures generate natively in Iris dimensions.")); @@ -86,43 +85,42 @@ public final class ModdedDatapackCommands { return root; } - private static LiteralArgumentBuilder message(String name, String text) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + private static LiteralArgumentBuilder message(String name, String text) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; })) - .then(Commands.argument("args", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + .then(NativeCommandRegistration.argument("args", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; }))); } - private static File worldDatapacksFolder(MinecraftServer server) { - return server.getWorldPath(LevelResource.DATAPACK_DIR).toFile(); + private static File worldDatapacksFolder(NativeModdedServer server) { + return server.datapacks().toFile(); } - private static File overrideFile(MinecraftServer server, String dimensionKey) { + private static File overrideFile(NativeModdedServer server, String dimensionKey) { return new File(worldDatapacksFolder(server), WORLD_PACK_NAME + "/data/irisworldgen/dimension_type/" + IrisDimension.sanitizeDimensionTypeKeyValue(dimensionKey) + ".json"); } - private static int status(CommandSourceStack source) { - MinecraftServer server = source.getServer(); + private static int status(NativeCommandSource source) { + NativeModdedServer server = source.server(); int irisLevels = 0; int mismatches = 0; - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator irisGenerator)) { + for (NativeWorld level : server.worlds()) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { continue; } irisLevels++; - String dimensionId = level.dimension().identifier().toString(); - String typeKey = level.dimensionTypeRegistration().unwrapKey() - .map((net.minecraft.resources.ResourceKey key) -> key.identifier().toString()) - .orElse("inline"); - DimensionType active = level.dimensionType(); - int activeMin = active.minY(); - int activeMax = active.minY() + active.height(); + String dimensionId = level.name(); + NativeWorldDimensions.Settings active = NativeWorldDimensions.settings(level); + String typeKey = active.key(); + int activeMin = active.minimumHeight(); + int activeMax = active.maximumHeight(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null || engine.getDimension() == null) { @@ -151,16 +149,17 @@ public final class ModdedDatapackCommands { return 1; } - private static int install(CommandSourceStack source) { - MinecraftServer server = source.getServer(); + private static int install(NativeCommandSource source) { + NativeModdedServer server = source.server(); List written = new ArrayList<>(); - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator irisGenerator)) { + for (NativeWorld level : server.worlds()) { + IrisModdedChunkGenerator irisGenerator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (irisGenerator == null) { continue; } Engine engine = IrisModdedCommands.engineFor(level); if (engine == null || engine.getDimension() == null) { - IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_ENGINE_NOT_STARTED_CANNOT_DERIVE_ITS_DIMENSION_TYPE_YET, MessageArgument.untrusted("value", level.dimension().identifier()))); + IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_ENGINE_NOT_STARTED_CANNOT_DERIVE_ITS_DIMENSION_TYPE_YET, MessageArgument.untrusted("value", level.name()))); continue; } IrisDimension dimension = engine.getDimension(); @@ -169,7 +168,7 @@ public final class ModdedDatapackCommands { json = dimension.getDimensionType().toJson(DataVersion.V26_2.get()); } catch (Throwable e) { ModdedIrisLog.error("Iris dimension type generation failed for {}", dimension.getLoadKey(), e); - IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_DIMENSION_TYPE_GENERATION_FAILED, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage())))); + IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_DIMENSION_TYPE_GENERATION_FAILED, MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage())))); continue; } File output = overrideFile(server, irisGenerator.dimensionKey()); @@ -214,8 +213,8 @@ public final class ModdedDatapackCommands { return 1; } - private static int list(CommandSourceStack source) { - MinecraftServer server = source.getServer(); + private static int list(NativeCommandSource source) { + NativeModdedServer server = source.server(); LinkedHashSet configured = new LinkedHashSet<>(); File packsRoot = ModdedPackCommands.packsRoot(); for (File pack : PackDirectoryResolver.listVisiblePackDirectories(packsRoot)) { @@ -251,8 +250,8 @@ public final class ModdedDatapackCommands { File[] installed = datapacks.isDirectory() ? datapacks.listFiles(file -> file.isDirectory() || file.isFile() && file.getName().toLowerCase(Locale.ROOT).endsWith(".zip")) : null; - Set availableIds = new HashSet<>(server.getPackRepository().getAvailableIds()); - Set selectedIds = new HashSet<>(server.getPackRepository().getSelectedIds()); + Set availableIds = new HashSet<>(server.availableDatapacks()); + Set selectedIds = new HashSet<>(server.selectedDatapacks()); KList names = new KList<>(); if (installed != null) { for (File installedPack : installed) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDeveloperCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDeveloperCommands.java index f32ad02e2..c3d5ec90c 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDeveloperCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDeveloperCommands.java @@ -21,8 +21,8 @@ package art.arcane.iris.modded.command; import art.arcane.iris.modded.ModdedIrisLog; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; import java.net.InetAddress; import java.net.NetworkInterface; @@ -35,21 +35,21 @@ import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.volmlib.util.localization.MessageArgument; final class ModdedDeveloperCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private ModdedDeveloperCommands() { } - static LiteralArgumentBuilder tree(String name) { - return Commands.literal(name).requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), "developer"))) - .then(Commands.literal("network") - .executes(ModdedCommandTree.localized((CommandContext context) -> network(context.getSource())))) - .then(Commands.literal("ip") - .executes(ModdedCommandTree.localized((CommandContext context) -> network(context.getSource())))); + static LiteralArgumentBuilder tree(String name) { + return NativeCommandRegistration.literal(name).requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), "developer"))) + .then(NativeCommandRegistration.literal("network") + .executes(ModdedCommandTree.localized((CommandContext context) -> network(context.getSource())))) + .then(NativeCommandRegistration.literal("ip") + .executes(ModdedCommandTree.localized((CommandContext context) -> network(context.getSource())))); } - private static int network(CommandSourceStack source) { + private static int network(NativeCommandSource source) { try { Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); for (NetworkInterface networkInterface : Collections.list(interfaces)) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDustRevealer.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDustRevealer.java index e03a2e625..4fb49c990 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDustRevealer.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDustRevealer.java @@ -18,6 +18,9 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; @@ -26,27 +29,13 @@ import art.arcane.iris.generation.runtime.GenerationSessionException; import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedScheduler; import art.arcane.iris.generation.context.IrisContext; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.ChatFormatting; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.ClickEvent; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.HoverEvent; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.level.biome.Biome; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText.Format; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.util.ArrayDeque; import java.util.ArrayList; @@ -63,52 +52,52 @@ import java.util.function.Supplier; public final class ModdedDustRevealer { private static final int MAX_HITS = 2_048; private static final int PARTICLE_BATCH_SIZE = 64; - private static final DustParticleOptions REVEAL_DUST = new DustParticleOptions(0xFFD24A, 1.2F); + private static final NativeEditPlayer.Dust REVEAL_DUST = new NativeEditPlayer.Dust(0xFFD24A, 1.2F); + private static final NativeEditPlayer.ParticleSpread REVEAL_SPREAD = new NativeEditPlayer.ParticleSpread(3, 0.25D, 0.25D, 0.25D, 0.0D); private static final ConcurrentHashMap ACTIVE_RUNS = new ConcurrentHashMap<>(); private ModdedDustRevealer() { } - public static void reveal(ServerPlayer player, ServerLevel level, BlockPos pos) { - Engine engine = IrisModdedCommands.engineFor(level); + public static void reveal(NativeEditPlayer player, NativeEditWorld level, NativeBlockPoint pos) { + Engine engine = IrisModdedCommands.engineFor(level.world()); if (engine == null) { - player.sendSystemMessage(Component.literal( + player.sendSystemMessage(NativeCommandText.literal( IrisLanguage.plain(RuntimeUiMessages.DUST_IRIS_WORLD_REQUIRED))); return; } describe(player, level, engine, pos); - int relativeY = pos.getY() - engine.getMinHeight(); + int relativeY = pos.y() - engine.getMinHeight(); String key = safe( "object lookup at " + coordinates(pos), - () -> engine.getObjectPlacementKey(pos.getX(), relativeY, pos.getZ())); + () -> engine.getObjectPlacementKey(pos.x(), relativeY, pos.z())); if (key == null) { return; } ModdedScheduler scheduler = ModdedEngineBootstrap.schedulerOrNull(); if (scheduler == null) { - player.sendSystemMessage(Component.literal( + player.sendSystemMessage(NativeCommandText.literal( IrisLanguage.plain(RuntimeUiMessages.DUST_REVEAL_FAILED))); return; } - level.playSound(null, pos, SoundEvents.LODESTONE_COMPASS_LOCK, - SoundSource.PLAYERS, 1.0F, 0.1F); - player.sendSystemMessage(Component.literal(IrisLanguage.plain( + level.sound(pos, "minecraft:item.lodestone_compass.lock", new NativeEditWorld.SoundOptions(1.0F, 0.1F)); + player.sendSystemMessage(NativeCommandText.literal(IrisLanguage.plain( RuntimeUiMessages.DUST_FOUND_OBJECT, MessageArgument.untrusted("object", key) ))); RevealRun run = new RevealRun( - player.getUUID(), + player.id(), player, level, engine, - pos.immutable(), + pos, key, - level.getMinY(), - level.getMaxY() + 1, + level.minY(), + level.maxY() + 1, new AtomicBoolean()); - RevealRun previous = ACTIVE_RUNS.put(player.getUUID(), run); + RevealRun previous = ACTIVE_RUNS.put(player.id(), run); if (previous != null) { previous.cancelled().set(true); } @@ -125,7 +114,7 @@ public final class ModdedDustRevealer { private static void discover(ModdedScheduler scheduler, RevealRun run) { try (GenerationSessionLease lease = run.engine().acquireGenerationLease("modded_dust_reveal"); IrisContext.Scope ignored = IrisContext.open(run.engine(), lease.sessionId(), null)) { - List hits = collect(run); + List hits = collect(run); if (!run.cancelled().get()) { scheduler.global(() -> revealBatch(scheduler, run, hits, 0)); } @@ -136,7 +125,7 @@ public final class ModdedDustRevealer { } } - static List collect(RevealRun run) { + static List collect(RevealRun run) { return collect( run.origin(), run.key(), @@ -148,16 +137,16 @@ public final class ModdedDustRevealer { run.engine().getObjectPlacementKey(x, relativeY, z)); } - static List collect(BlockPos origin, String key, int engineMinY, + static List collect(NativeBlockPoint origin, String key, int engineMinY, int minY, int maxYExclusive, AtomicBoolean cancelled, ObjectPlacementLookup lookup) { - List hits = new ArrayList<>(); - Set visited = new HashSet<>(); - Deque frontier = new ArrayDeque<>(); + List hits = new ArrayList<>(); + Set visited = new HashSet<>(); + Deque frontier = new ArrayDeque<>(); frontier.add(origin); visited.add(origin); while (!frontier.isEmpty() && hits.size() < MAX_HITS && !cancelled.get()) { - BlockPos current = frontier.poll(); + NativeBlockPoint current = frontier.poll(); hits.add(current); for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { @@ -165,14 +154,14 @@ public final class ModdedDustRevealer { if (dx == 0 && dy == 0 && dz == 0) { continue; } - BlockPos next = current.offset(dx, dy, dz); - if (next.getY() < minY - || next.getY() >= maxYExclusive + NativeBlockPoint next = current.offset(dx, dy, dz); + if (next.y() < minY + || next.y() >= maxYExclusive || !visited.add(next)) { continue; } String nextKey = lookup.at( - next.getX(), next.getY() - engineMinY, next.getZ()); + next.x(), next.y() - engineMinY, next.z()); if (key.equals(nextKey)) { frontier.add(next); } @@ -184,7 +173,7 @@ public final class ModdedDustRevealer { } private static void revealBatch(ModdedScheduler scheduler, RevealRun run, - List hits, int from) { + List hits, int from) { if (!active(run)) { // Drop the registry entry on abort too, or the run record pins the player, level // and engine until server stop. No-op if a newer run already replaced it. @@ -193,23 +182,20 @@ public final class ModdedDustRevealer { } int to = Math.min(hits.size(), from + PARTICLE_BATCH_SIZE); for (int index = from; index < to; index++) { - BlockPos hit = hits.get(index); - run.level().sendParticles(run.player(), REVEAL_DUST, true, true, - hit.getX() + 0.5D, hit.getY() + 0.5D, hit.getZ() + 0.5D, - 3, 0.25D, 0.25D, 0.25D, 0.0D); + NativeBlockPoint hit = hits.get(index); + run.player().dust(REVEAL_DUST, hit.x() + 0.5D, hit.y() + 0.5D, hit.z() + 0.5D, REVEAL_SPREAD); } if (to > from) { - BlockPos soundAt = hits.get(from); - run.level().playSound(null, soundAt, SoundEvents.AMETHYST_BLOCK_CHIME, - SoundSource.PLAYERS, 0.5F, - ThreadLocalRandom.current().nextFloat(0.2F, 2.0F)); + NativeBlockPoint soundAt = hits.get(from); + run.level().sound(soundAt, "minecraft:block.amethyst_block.chime", + new NativeEditWorld.SoundOptions(0.5F, ThreadLocalRandom.current().nextFloat(0.2F, 2.0F))); } if (to < hits.size()) { scheduler.laterGlobal(() -> revealBatch(scheduler, run, hits, to), 1); return; } ACTIVE_RUNS.remove(run.playerId(), run); - run.player().sendSystemMessage(Component.literal(IrisLanguage.plain( + run.player().sendSystemMessage(NativeCommandText.literal(IrisLanguage.plain( hits.size() >= MAX_HITS ? RuntimeUiMessages.DUST_REVEALED_CAPPED : RuntimeUiMessages.DUST_REVEALED, @@ -221,9 +207,7 @@ public final class ModdedDustRevealer { private static boolean active(RevealRun run) { return !run.cancelled().get() && ACTIVE_RUNS.get(run.playerId()) == run - && !run.player().hasDisconnected() - && !run.player().isRemoved() - && run.player().level() == run.level() + && run.player().activeIn(run.level()) && !run.engine().isClosing() && !run.engine().isClosed(); } @@ -232,16 +216,16 @@ public final class ModdedDustRevealer { ModdedIrisLog.error("Iris dust reveal failed for {} at {}", run.key(), coordinates(run.origin()), error); scheduler.global(() -> { if (ACTIVE_RUNS.remove(run.playerId(), run)) { - run.player().sendSystemMessage(Component.literal( + run.player().sendSystemMessage(NativeCommandText.literal( IrisLanguage.plain(RuntimeUiMessages.DUST_REVEAL_FAILED))); } }); } - private static void describe(ServerPlayer player, ServerLevel level, Engine engine, BlockPos pos) { - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); + private static void describe(NativeEditPlayer player, NativeEditWorld level, Engine engine, NativeBlockPoint pos) { + int x = pos.x(); + int y = pos.y(); + int z = pos.z(); int minHeight = engine.getMinHeight(); int relativeY = y - minHeight; Integer surfaceRelative = safe( @@ -275,7 +259,7 @@ public final class ModdedDustRevealer { ), false)); lines.add(new DustLine(IrisLanguage.plain( RuntimeUiMessages.DUST_BLOCK, - MessageArgument.untrusted("block", ModdedBlockState.serialize(level.getBlockState(pos))) + MessageArgument.untrusted("block", level.block(pos.x(), pos.y(), pos.z()).key()) ), false)); if (offset != null && surfaceY != null) { lines.add(new DustLine(positionLine(offset, surfaceY), true)); @@ -419,36 +403,32 @@ public final class ModdedDustRevealer { return null; } - private static NativeBiome nativeBiome(ServerLevel level, BlockPos pos) { - Holder holder = level.getBiome(pos); - String key = holder.unwrapKey() - .map((ResourceKey resourceKey) -> resourceKey.identifier().toString()) - .orElse(IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED)); - Registry registry = level.registryAccess().lookupOrThrow(Registries.BIOME); - return new NativeBiome(key, registry.getId(holder.value())); + private static NativeBiome nativeBiome(NativeEditWorld level, NativeBlockPoint pos) { + NativeEditWorld.BiomeIdentity biome = level.biome(pos); + return new NativeBiome(biome.key() == null ? IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED) : biome.key(), biome.id()); } - private static void sendReport(ServerPlayer player, List lines) { + private static void sendReport(NativeEditPlayer player, List lines) { StringBuilder payload = new StringBuilder(); for (int index = 0; index < lines.size(); index++) { DustLine line = lines.get(index); - ChatFormatting color = index == 0 - ? ChatFormatting.GOLD - : line.emphasis() ? ChatFormatting.YELLOW : ChatFormatting.WHITE; - player.sendSystemMessage(Component.literal(line.text()).withStyle(color)); + Format color = index == 0 + ? Format.GOLD + : line.emphasis() ? Format.YELLOW : Format.WHITE; + player.sendSystemMessage(NativeCommandText.literal(line.text()).withStyle(color)); if (index > 0) { payload.append('\n'); } payload.append(line.text()); } - MutableComponent hover = Component.literal( + NativeCommandText hover = NativeCommandText.literal( IrisLanguage.plain(RuntimeUiMessages.DUST_COPY_HOVER)); - MutableComponent button = Component.literal( + NativeCommandText button = NativeCommandText.literal( IrisLanguage.plain(RuntimeUiMessages.DUST_COPY_BUTTON)) - .withStyle(ChatFormatting.GREEN) + .withStyle(Format.GREEN) .withStyle(style -> style - .withClickEvent(new ClickEvent.CopyToClipboard(payload.toString())) - .withHoverEvent(new HoverEvent.ShowText(hover))); + .copyToClipboard(payload.toString()) + .hover(hover)); player.sendSystemMessage(button); } @@ -461,8 +441,8 @@ public final class ModdedDustRevealer { } } - private static String coordinates(BlockPos pos) { - return pos.getX() + ", " + pos.getY() + ", " + pos.getZ(); + private static String coordinates(NativeBlockPoint pos) { + return pos.x() + ", " + pos.y() + ", " + pos.z(); } private record DustLine(String text, boolean emphasis) { @@ -478,10 +458,10 @@ public final class ModdedDustRevealer { static record RevealRun( UUID playerId, - ServerPlayer player, - ServerLevel level, + NativeEditPlayer player, + NativeEditWorld level, Engine engine, - BlockPos origin, + NativeBlockPoint origin, String key, int minY, int maxYExclusive, diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedEditCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedEditCommands.java index e2e6fee7c..635067bf0 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedEditCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedEditCommands.java @@ -27,9 +27,9 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; import java.awt.Desktop; import java.io.File; @@ -39,22 +39,22 @@ final class ModdedEditCommands { private ModdedEditCommands() { } - static int editBiome(CommandSourceStack source, String key) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + static int editBiome(NativeCommandSource source, String key) { + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS)); return 0; } IrisBiome biome; if (key == null || key.isBlank()) { - ServerPlayer player = source.getPlayer(); + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_BIOME_IRIS_EDIT_BIOME_KEY)); return 0; } - BlockPos pos = player.blockPosition(); + NativeBlockPoint pos = player.blockPosition(); try { - biome = engine.getBiome(pos.getX(), pos.getY() - engine.getMinHeight(), pos.getZ()); + biome = engine.getBiome(pos.x(), pos.y() - engine.getMinHeight(), pos.z()); } catch (Throwable e) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_BIOME_LOOKUP_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()))); return 0; @@ -69,23 +69,23 @@ final class ModdedEditCommands { return openJson(source, biome); } - static int editRegion(CommandSourceStack source, String key) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + static int editRegion(NativeCommandSource source, String key) { + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_2)); return 0; } IrisRegion region; if (key == null || key.isBlank()) { - ServerPlayer player = source.getPlayer(); + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CONSOLE_MUST_NAME_REGION_IRIS_EDIT_REGION_KEY)); return 0; } - BlockPos pos = player.blockPosition(); + NativeBlockPoint pos = player.blockPosition(); try { region = engine.getRegion( - pos.getX(), pos.getY() - engine.getMinHeight(), pos.getZ()); + pos.x(), pos.y() - engine.getMinHeight(), pos.z()); } catch (Throwable e) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_REGION_LOOKUP_FAILED, MessageArgument.untrusted("value", e.getClass().getSimpleName()))); return 0; @@ -100,8 +100,8 @@ final class ModdedEditCommands { return openJson(source, region); } - static int editDimension(CommandSourceStack source) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + static int editDimension(NativeCommandSource source) { + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_3)); return 0; @@ -109,7 +109,7 @@ final class ModdedEditCommands { return openJson(source, engine.getDimension()); } - private static int openJson(CommandSourceStack source, IrisRegistrant registrant) { + private static int openJson(NativeCommandSource source, IrisRegistrant registrant) { if (!GuiHost.isAvailable() || !Desktop.isDesktopSupported()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CANNOT_OPEN_FILES_HERE, MessageArgument.untrusted("value", ModdedGuiHost.guiUnavailableReason()))); return 0; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGoldenHash.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGoldenHash.java index bca1d37fa..75b255811 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGoldenHash.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGoldenHash.java @@ -24,12 +24,10 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.modded.ModdedBlockBuffer; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; import java.io.File; import java.util.concurrent.atomic.AtomicBoolean; @@ -48,16 +46,14 @@ public final class ModdedGoldenHash { private static final AtomicBoolean ACTIVE = new AtomicBoolean(false); - private final CommandSourceStack source; - private final MinecraftServer server; + private final NativeCommandSource source; private final Engine engine; private final GoldenHashEngine hashEngine; - private ModdedGoldenHash(CommandSourceStack source, ServerLevel level, Engine engine, int radius, int threads, Mode mode) { + private ModdedGoldenHash(NativeCommandSource source, Engine engine, ScanOptions options) { this.source = source; - this.server = source.getServer(); this.engine = engine; - GoldenHashEngine.Mode engineMode = switch (mode) { + GoldenHashEngine.Mode engineMode = switch (options.mode()) { case AUTO -> GoldenHashEngine.Mode.AUTO; case CAPTURE -> GoldenHashEngine.Mode.CAPTURE; case VERIFY -> GoldenHashEngine.Mode.VERIFY; @@ -71,8 +67,8 @@ public final class ModdedGoldenHash { engine.getMaxHeight(), 0, 0, - radius, - threads, + options.radius(), + options.threads(), engineMode, true, false, @@ -80,22 +76,22 @@ public final class ModdedGoldenHash { this.hashEngine = new GoldenHashEngine(engine, request, goldenDir, this::snapshot, feedback(), progress()); } - public static void start(CommandSourceStack source, ServerLevel level, Engine engine, int radius, int threads, Mode mode) { + public static void start(NativeCommandSource source, Engine engine, ScanOptions options) { if (!ACTIVE.compareAndSet(false, true)) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_GOLDEN_HASH_GOLDENHASH_SCAN_IS_ALREADY_RUNNING)); return; } - ModdedGoldenHash scan = new ModdedGoldenHash(source, level, engine, radius, threads, mode); - int boundedRadius = Math.max(0, radius); + ModdedGoldenHash scan = new ModdedGoldenHash(source, engine, options); + int boundedRadius = Math.max(0, options.radius()); int chunks = (boundedRadius * 2 + 1) * (boundedRadius * 2 + 1); scan.ok(IrisLanguage.plain( RuntimeProgressMessages.GOLDEN_STARTED, MessageArgument.trusted("chunks", chunks), - MessageArgument.trusted("threads", Math.max(1, threads)), - MessageArgument.untrusted("mode", mode) + MessageArgument.trusted("threads", Math.max(1, options.threads())), + MessageArgument.untrusted("mode", options.mode()) )); ModdedIrisLog.info("goldenhash start: dim={} seed={} radius={} threads={} mode={} file={}", - engine.getDimension().getLoadKey(), engine.getSeedManager().getSeed(), boundedRadius, Math.max(1, threads), mode, scan.hashEngine.getGoldenFile().getName()); + engine.getDimension().getLoadKey(), engine.getSeedManager().getSeed(), boundedRadius, Math.max(1, options.threads()), options.mode(), scan.hashEngine.getGoldenFile().getName()); Thread thread = new Thread(() -> { try { scan.hashEngine.run(); @@ -110,9 +106,9 @@ public final class ModdedGoldenHash { private GoldenHashEngine.ChunkSnapshot snapshot(int chunkX, int chunkZ) throws Exception { int minY = engine.getMinHeight(); int height = engine.getMaxHeight() - minY; - PlatformBlockState air = IrisPlatforms.get().registries().air(); + NativeBlockState air = IrisPlatforms.get().registries().air(); ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, air); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); engine.generate(chunkX << 4, chunkZ << 4, blocks, biomes, false); return new GoldenHashEngine.ChunkSnapshot() { @Override @@ -126,12 +122,12 @@ public final class ModdedGoldenHash { } @Override - public PlatformBlockState block(int x, int y, int z) { + public NativeBlockState block(int x, int y, int z) { return blocks.get(x, y - minY, z); } @Override - public PlatformBiome biome(int x, int y, int z) { + public NativeBiome biome(int x, int y, int z) { return biomes.get(x, y - minY, z); } }; @@ -191,11 +187,14 @@ public final class ModdedGoldenHash { }; } + public record ScanOptions(int radius, int threads, Mode mode) { + } + private void ok(String message) { - server.execute(() -> IrisModdedCommands.ok(source, message)); + source.execute(() -> IrisModdedCommands.ok(source, message)); } private void fail(String message) { - server.execute(() -> IrisModdedCommands.fail(source, message)); + source.execute(() -> IrisModdedCommands.fail(source, message)); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGuiHost.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGuiHost.java index 535453f5b..b82f4117c 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGuiHost.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedGuiHost.java @@ -22,8 +22,7 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.studio.view.GuiHost; import art.arcane.iris.studio.view.GuiOverlay; import art.arcane.iris.generation.runtime.Engine; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.view.WorldView; import java.util.Map; import java.util.UUID; @@ -32,10 +31,9 @@ import java.util.concurrent.ConcurrentHashMap; public final class ModdedGuiHost implements GuiHost.Provider { private static final ModdedGuiHost INSTANCE = new ModdedGuiHost(); - private final Map levels = new ConcurrentHashMap<>(); + private final Map levels = new ConcurrentHashMap<>(); private final Map openers = new ConcurrentHashMap<>(); private volatile Engine active; - private volatile MinecraftServer server; private ModdedGuiHost() { } @@ -44,8 +42,7 @@ public final class ModdedGuiHost implements GuiHost.Provider { GuiHost.set(INSTANCE); } - public static void bindContext(MinecraftServer server, ServerLevel level, Engine engine, UUID opener) { - INSTANCE.server = server; + public static void bindContext(WorldView level, Engine engine, UUID opener) { INSTANCE.active = engine; INSTANCE.levels.put(engine, level); if (opener == null) { @@ -57,7 +54,7 @@ public final class ModdedGuiHost implements GuiHost.Provider { /** * Drops the GUI binding for an evicted engine. Without this the host pinned every - * GUI-bound Engine, its ServerLevel and transitively the MinecraftServer for the process + * GUI-bound Engine, its WorldView and transitively the MinecraftServer for the process * lifetime — there was no remove path at all. */ public static void unbind(Engine engine) { @@ -75,7 +72,6 @@ public final class ModdedGuiHost implements GuiHost.Provider { INSTANCE.levels.clear(); INSTANCE.openers.clear(); INSTANCE.active = null; - INSTANCE.server = null; } public static boolean isGuiLaunchable() { @@ -98,7 +94,7 @@ public final class ModdedGuiHost implements GuiHost.Provider { if (current != null && !current.isClosed()) { return current; } - for (Map.Entry entry : levels.entrySet()) { + for (Map.Entry entry : levels.entrySet()) { if (!entry.getKey().isClosed()) { return entry.getKey(); } @@ -111,11 +107,11 @@ public final class ModdedGuiHost implements GuiHost.Provider { if (engine == null) { return null; } - ServerLevel level = levels.get(engine); - if (level == null || server == null) { + WorldView level = levels.get(engine); + if (level == null) { return null; } UUID resolvedOpenerId = openerId == null ? openers.get(engine) : openerId; - return new ModdedVisionOverlay(server, level, engine, resolvedOpenerId); + return new ModdedVisionOverlay(new ModdedVisionOverlay.Context(level, engine, resolvedOpenerId)); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLanguageCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLanguageCommands.java index 9eb4becf9..995002b6f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLanguageCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLanguageCommands.java @@ -7,11 +7,9 @@ import art.arcane.volmlib.util.localization.PluginLanguageService; import art.arcane.volmlib.util.localization.VolmitLocales; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -20,21 +18,21 @@ final class ModdedLanguageCommands { private ModdedLanguageCommands() { } - static LiteralArgumentBuilder tree() { - return Commands.literal("language") + static LiteralArgumentBuilder tree() { + return NativeCommandRegistration.literal("language") .executes(ModdedCommandTree.localized(context -> open(context.getSource()))) .then(scope("self", false)) - .then(scope("server", true).requires(Commands.hasPermission(Commands.LEVEL_GAMEMASTERS))); + .then(scope("server", true).requires(NativeCommandRegistration.GAMEMASTERS)); } - private static LiteralArgumentBuilder scope(String name, boolean server) { - return Commands.literal(name) - .then(Commands.argument("locale", StringArgumentType.word()) - .suggests((context, builder) -> SharedSuggestionProvider.suggest(IrisLanguage.availableLocales(), builder)) + private static LiteralArgumentBuilder scope(String name, boolean server) { + return NativeCommandRegistration.literal(name) + .then(NativeCommandRegistration.argument("locale", StringArgumentType.word()) + .suggests((context, builder) -> NativeCommandRegistration.suggest(IrisLanguage.availableLocales(), builder)) .executes(ModdedCommandTree.localized(context -> select(context.getSource(), server, StringArgumentType.getString(context, "locale"))))); } - private static int open(CommandSourceStack source) { + private static int open(NativeCommandSource source) { ModdedCommandFeedback.clear(source); ModdedCommandFeedback.send(source, ModdedCommandFeedback.banner("/iris language")); ModdedCommandFeedback.send(source, ModdedCommandFeedback.button( @@ -47,7 +45,7 @@ final class ModdedLanguageCommands { .append(ModdedCommandFeedback.button(name + " [me]", "/iris language self " + locale, "Use " + name, true)) .append(ModdedCommandTree.isGamemaster(source) ? ModdedCommandFeedback.button(" [server]", "/iris language server " + locale, "Set the server default", true) - : Component.empty())); + : NativeCommandText.empty())); } ModdedCommandFeedback.send(source, ModdedCommandFeedback.button( "Use server default", "/iris language self reset", "Clear your personal language", true)); @@ -55,36 +53,35 @@ final class ModdedLanguageCommands { return 1; } - private static int select(CommandSourceStack source, boolean server, String locale) { + private static int select(NativeCommandSource source, boolean server, String locale) { PluginLanguageService service = IrisLanguage.selections(); if (service == null) { - source.sendFailure(Component.literal("Iris language service is unavailable.")); + source.sendFailure(NativeCommandText.literal("Iris language service is unavailable.")); return 0; } CompletableFuture selection; if (server) { selection = service.selectDefault(locale).thenApply(ignored -> service.defaultLocale()); } else { - ServerPlayer player = source.getPlayer(); - if (player == null) { - source.sendFailure(Component.literal("Personal language selection requires a player.")); + UUID playerId = source.playerId(); + if (playerId == null) { + source.sendFailure(NativeCommandText.literal("Personal language selection requires a player.")); return 0; } - UUID playerId = player.getUUID(); CompletableFuture personal = locale.equals("reset") ? service.clearPlayer(playerId) : service.selectPlayer(playerId, locale); selection = personal.thenApply(ignored -> service.effectiveLocale(playerId)); } - selection.whenComplete((activeLocale, failure) -> source.getServer().execute(() -> { + selection.whenComplete((activeLocale, failure) -> source.execute(() -> { if (failure != null) { IrisLogging.reportError(failure); - source.sendFailure(Component.literal("Language selection failed. See the server log.")); + source.sendFailure(NativeCommandText.literal("Language selection failed. See the server log.")); } else if (!locale.equals("reset") && !locale.replace('-', '_').equalsIgnoreCase(activeLocale.replace('-', '_'))) { - source.sendSuccess(() -> Component.literal("Iris: " + locale + source.sendSuccess(() -> NativeCommandText.literal("Iris: " + locale + " unavailable; using English (" + activeLocale + ")."), false); } else { - source.sendSuccess(() -> Component.literal("Language updated: " + activeLocale), false); + source.sendSuccess(() -> NativeCommandText.literal("Language updated: " + activeLocale), false); } })); return 1; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLocateCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLocateCommands.java index 1a2566623..b3a4b71ce 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLocateCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedLocateCommands.java @@ -18,6 +18,11 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureQueries; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.modded.localization.ModdedCommandMessages; @@ -37,26 +42,11 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.generation.context.IrisContext; import art.arcane.iris.generation.concurrent.MultiBurst; import art.arcane.volmlib.util.localization.MessageArgument; import art.arcane.volmlib.util.math.Position2; -import com.mojang.datafixers.util.Pair; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.HolderSet; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Relative; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.structure.Structure; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; import java.util.Optional; import java.util.Set; @@ -75,13 +65,13 @@ final class ModdedLocateCommands { private ModdedLocateCommands() { } - static int gotoBiome(CommandSourceStack source, String key) { - ServerPlayer player = source.getPlayer(); + static int gotoBiome(NativeCommandSource source, String key) { + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_3)); return 0; } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_8)); @@ -96,13 +86,13 @@ final class ModdedLocateCommands { return 1; } - static int gotoRegion(CommandSourceStack source, String key) { - ServerPlayer player = source.getPlayer(); + static int gotoRegion(NativeCommandSource source, String key) { + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_4)); return 0; } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_9)); @@ -117,8 +107,8 @@ final class ModdedLocateCommands { return 1; } - static int gotoObject(CommandSourceStack source, String keyRaw) { - ServerLevel level = source.getLevel(); + static int gotoObject(NativeCommandSource source, String keyRaw) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_10)); @@ -129,7 +119,7 @@ final class ModdedLocateCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_CONFIGURED_ANY_REGION_BIOME_OBJECT_PLACEMENTS_OBJECT_KEYS, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", engine.getData().getObjectLoader().getPossibleKeys().length))); return 0; } - ServerPlayer player = source.getPlayer(); + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_OBJECT_KEY, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", engine.getData().getObjectLoader().getPossibleKeys().length))); return 0; @@ -138,13 +128,13 @@ final class ModdedLocateCommands { return 1; } - static int gotoRiver(CommandSourceStack source, String type) { - ServerPlayer player = source.getPlayer(); + static int gotoRiver(NativeCommandSource source, String type) { + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, "This command can only be used by a player."); return 0; } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, "This dimension is not generated by Iris."); @@ -158,15 +148,15 @@ final class ModdedLocateCommands { IrisModdedCommands.fail(source, error.getMessage()); return 0; } - int originX = player.blockPosition().getX(); - int originZ = player.blockPosition().getZ(); + int originX = player.blockX(); + int originZ = player.blockZ(); int requestedDistance = runtime == null ? 8192 : Math.min(8192, runtime.settings().routing().tileSize() * 15); int maximumDistance = runtime == null ? requestedDistance : runtime.maximumFeatureSearchDistance(originX, originZ, requestedDistance); - MinecraftServer server = source.getServer(); + NativeModdedServer server = source.server(); IrisModdedCommands.ok(source, "Searching accepted hydrology plans for " + type + "..."); MultiBurst.burst.completeValueAsync(() -> GenerationSemanticQueries.nearestRiver( engine, query, originX, originZ, maximumDistance, @@ -198,8 +188,8 @@ final class ModdedLocateCommands { return 1; } - static int gotoStructure(CommandSourceStack source, String keyRaw) { - ServerLevel level = source.getLevel(); + static int gotoStructure(NativeCommandSource source, String keyRaw) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_11)); @@ -210,7 +200,7 @@ final class ModdedLocateCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NAME_IRIS_NATIVE_STRUCTURE_LOCATE)); return 0; } - ServerPlayer player = source.getPlayer(); + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_5)); return 0; @@ -229,7 +219,7 @@ final class ModdedLocateCommands { NativeStructureTarget target = resolved.get(); IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, target.key(), false); boolean nativeGenerationEnabled = - source.getServer().getWorldGenSettings().options().generateStructures(); + source.server().generateStructures(); boolean nativePlacement = IrisStructureLocator.hasNativePlacement(engine, target.key()); boolean locatableNativePlacement = nativePlacement && IrisStructureLocator.hasLocatableNativePlacement(engine, target.key()); @@ -300,11 +290,11 @@ final class ModdedLocateCommands { + "density or a Y band outside this world's height range."; } - private static void locateIrisStructure(CommandSourceStack source, ServerLevel level, Engine engine, - ServerPlayer player, String key) { - MinecraftServer server = source.getServer(); - int blockX = player.blockPosition().getX(); - int blockZ = player.blockPosition().getZ(); + private static void locateIrisStructure(NativeCommandSource source, NativeWorld level, Engine engine, + NativeProtocolPlayer player, String key) { + NativeModdedServer server = source.server(); + int blockX = player.blockX(); + int blockZ = player.blockZ(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING_IRIS_PLACED_STRUCTURE, MessageArgument.untrusted("key", key))); Thread thread = new Thread(() -> { try { @@ -332,37 +322,31 @@ final class ModdedLocateCommands { thread.start(); } - private static void runNativeStructureLocate(CommandSourceStack source, ServerLevel level, - ServerPlayer player, NativeStructureTarget target) { - MinecraftServer server = source.getServer(); + private static void runNativeStructureLocate(NativeCommandSource source, NativeWorld level, + NativeProtocolPlayer player, NativeStructureTarget target) { + NativeModdedServer server = source.server(); Runnable locateTask = () -> locateNativeStructure(source, level, player, target); - if (Thread.currentThread() == server.getRunningThread()) { + if (server.isServerThread()) { locateTask.run(); return; } server.execute(locateTask); } - private static void locateNativeStructure(CommandSourceStack source, ServerLevel level, - ServerPlayer player, NativeStructureTarget target) { + private static void locateNativeStructure(NativeCommandSource source, NativeWorld level, + NativeProtocolPlayer player, NativeStructureTarget target) { try { - ChunkGenerator generator = level.getChunkSource().getGenerator(); - Pair> found = generator.findNearestMapStructure( - level, - HolderSet.direct(target.holder()), - player.blockPosition(), - NATIVE_STRUCTURE_LOCATE_RADIUS, - false); + NativeStructureQueries queries = new NativeStructureQueries(level); + NativeBlockPoint found = queries.locate(target.holder(), + new NativeBlockPoint(player.blockX(), player.blockY(), player.blockZ()), NATIVE_STRUCTURE_LOCATE_RADIUS); if (found == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_COULD_NOT_FIND_NATIVE_STRUCTURE_WITHIN_CHUNKS, MessageArgument.untrusted("value", target.key()), MessageArgument.untrusted("NATIVESTRUCTURELOCATERADIUS", NATIVE_STRUCTURE_LOCATE_RADIUS))); return; } - BlockPos position = found.getFirst(); - int targetX = position.getX(); - int targetZ = position.getZ(); - level.getChunk(targetX >> 4, targetZ >> 4); - int surfaceY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, targetX, targetZ) + 1; - int targetY = Math.max(level.getMinY() + 1, Math.min(level.getMaxY() - 1, surfaceY)); + int targetX = found.x(); + int targetZ = found.z(); + int surfaceY = queries.surfaceY(targetX, targetZ) + 1; + int targetY = Math.max(level.minHeight() + 1, Math.min(level.maxHeight() - 1, surfaceY)); teleportToStructure(source, level, player, targetX, targetY, targetZ, "native structure " + target.key()); } catch (Throwable e) { @@ -371,20 +355,19 @@ final class ModdedLocateCommands { } } - private static void teleportToStructure(CommandSourceStack source, ServerLevel level, ServerPlayer player, + private static void teleportToStructure(NativeCommandSource source, NativeWorld level, NativeProtocolPlayer player, int targetX, int targetY, int targetZ, String label) { - if (player.hasDisconnected() || player.isRemoved()) { + if (!player.connected()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PLAYER_DISCONNECTED_BEFORE_STRUCTURE_SEARCH_COMPLETED)); return; } - if (player.level() != level) { + if (!player.inWorld(level)) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_YOU_CHANGED_DIMENSIONS_BEFORE_STRUCTURE_SEARCH_COMPLETED_RUN_COMMAND_AGAIN)); return; } - level.getChunk(targetX >> 4, targetZ >> 4); - int clampedY = Math.max(level.getMinY() + 1, Math.min(level.getMaxY() - 1, targetY)); - boolean teleported = player.teleportTo(level, targetX + 0.5D, clampedY, targetZ + 0.5D, - Set.of(), player.getYRot(), player.getXRot(), false); + new NativeStructureQueries(level).loadChunk(targetX >> 4, targetZ >> 4); + int clampedY = Math.max(level.minHeight() + 1, Math.min(level.maxHeight() - 1, targetY)); + boolean teleported = player.teleport(level, targetX + 0.5D, clampedY, targetZ + 0.5D); if (!teleported) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_FOUND_AT_BUT_TELEPORTATION_FAILED, MessageArgument.untrusted("label", label), MessageArgument.untrusted("targetX", targetX), MessageArgument.untrusted("clampedY", clampedY), MessageArgument.untrusted("targetZ", targetZ))); return; @@ -392,8 +375,8 @@ final class ModdedLocateCommands { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_TELEPORTED_AT, MessageArgument.untrusted("label", label), MessageArgument.untrusted("targetX", targetX), MessageArgument.untrusted("clampedY", clampedY), MessageArgument.untrusted("targetZ", targetZ))); } - static int verifyStructures(CommandSourceStack source, String keyRaw) { - ServerLevel level = source.getLevel(); + static int verifyStructures(NativeCommandSource source, String keyRaw) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_12)); @@ -403,19 +386,19 @@ final class ModdedLocateCommands { if (!key.isEmpty()) { return verifyStructure(source, level, engine, key); } - Registry registry = source.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE); + NativeStructureQueries registry = new NativeStructureQueries(level); int available = 0; int disabled = 0; int suppressed = 0; int unreachableBiomes = 0; int unsupported = 0; - for (Identifier identifier : registry.keySet()) { - Optional> holder = registry.get(identifier); + for (String identifier : registry.keys()) { + Optional holder = registry.resolve(identifier); if (holder.isEmpty()) { continue; } NativeStructureAvailability availability = nativeAvailability(source, level, engine, - identifier.toString(), holder.get()); + identifier, holder.get()); switch (availability) { case AVAILABLE -> available++; case WORLD_DISABLED, FILTERED -> disabled++; @@ -429,7 +412,7 @@ final class ModdedLocateCommands { return 1; } - private static int verifyStructure(CommandSourceStack source, ServerLevel level, Engine engine, String key) { + private static int verifyStructure(NativeCommandSource source, NativeWorld level, Engine engine, String key) { Optional target = resolveNativeStructure(source, level, engine, key); if (target.isEmpty()) { if (IrisStructureLocator.isPlaced(engine, key)) { @@ -452,38 +435,33 @@ final class ModdedLocateCommands { return 1; } - private static Optional resolveNativeStructure(CommandSourceStack source, - ServerLevel level, + private static Optional resolveNativeStructure(NativeCommandSource source, + NativeWorld level, Engine engine, String keyRaw) { - Identifier identifier = Identifier.tryParse(keyRaw); - if (identifier == null) { - return Optional.empty(); - } - Registry registry = source.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE); - Optional> holder = registry.get(identifier); + NativeStructureQueries registry = new NativeStructureQueries(level); + Optional holder = registry.resolve(keyRaw); if (holder.isEmpty()) { return Optional.empty(); } - String key = identifier.toString(); + String key = holder.get().key(); NativeStructureAvailability availability = nativeAvailability(source, level, engine, key, holder.get()); return Optional.of(new NativeStructureTarget(key, holder.get(), availability)); } - static NativeStructureAvailability nativeAvailability(CommandSourceStack source, ServerLevel level, + static NativeStructureAvailability nativeAvailability(NativeCommandSource source, NativeWorld level, Engine engine, String key, - Holder.Reference holder) { - boolean worldEnabled = source.getServer().getWorldGenSettings().options().generateStructures(); + NativeStructureQueries.Reference holder) { + boolean worldEnabled = source.server().generateStructures(); IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve(engine, key, false); boolean selected = decision.status() != NativeStructureGenerationStatus.DISABLED_BY_PACK; boolean suppressed = decision.status() == NativeStructureGenerationStatus.REPLACED_BY_IRIS; - boolean biomeFilterEmpty = holder.value().biomes().stream().findAny().isEmpty(); - ChunkGenerator chunkGenerator = level.getChunkSource().getGenerator(); - boolean biomeReachable = chunkGenerator instanceof IrisModdedChunkGenerator irisGenerator - && irisGenerator.isNativeStructureReachable(holder); + boolean biomeFilterEmpty = holder.emptyBiomeFilter(); + NativeStructureQueries queries = new NativeStructureQueries(level); + boolean biomeReachable = queries.biomeReachable(holder); boolean hasPlacement = false; if (worldEnabled && selected && !suppressed && !biomeFilterEmpty && biomeReachable) { - hasPlacement = !level.getChunkSource().getGeneratorState().getPlacementsForStructure(holder).isEmpty(); + hasPlacement = queries.hasPlacement(holder); } return classifyNativeAvailability( worldEnabled, selected, suppressed, biomeFilterEmpty, biomeReachable, hasPlacement); @@ -531,15 +509,15 @@ final class ModdedLocateCommands { }; } - static int gotoPoi(CommandSourceStack source, String typeRaw) { - ServerLevel level = source.getLevel(); + static int gotoPoi(NativeCommandSource source, String typeRaw) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_13)); return 0; } String type = typeRaw.trim(); - ServerPlayer player = source.getPlayer(); + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_POI_TYPE, MessageArgument.untrusted("type", type))); return 0; @@ -548,10 +526,10 @@ final class ModdedLocateCommands { return 1; } - private static void locate(CommandSourceStack source, ServerLevel level, Engine engine, ServerPlayer player, Locator locator, String label) { - MinecraftServer server = source.getServer(); - int chunkX = player.blockPosition().getX() >> 4; - int chunkZ = player.blockPosition().getZ() >> 4; + private static void locate(NativeCommandSource source, NativeWorld level, Engine engine, NativeProtocolPlayer player, Locator locator, String label) { + NativeModdedServer server = source.server(); + int chunkX = player.blockX() >> 4; + int chunkZ = player.blockZ() >> 4; IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_SEARCHING, MessageArgument.untrusted("label", label))); CompletableFuture search; try { @@ -561,7 +539,7 @@ final class ModdedLocateCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_ENGINE_THIS_WORLD_HAS_BEEN_CLOSED_REJOIN_DIMENSION_TRY_AGAIN)); return; } - UUID playerId = player.getUUID(); + UUID playerId = player.id(); CompletableFuture previous = ACTIVE_LOCATE_REQUESTS.put(playerId, search); if (previous != null && previous != search) { previous.cancel(true); @@ -570,8 +548,8 @@ final class ModdedLocateCommands { source, level, engine, player, label, server, playerId, search, at, error)); } - private static void completeLocate(CommandSourceStack source, ServerLevel level, Engine engine, - ServerPlayer player, String label, MinecraftServer server, UUID playerId, + private static void completeLocate(NativeCommandSource source, NativeWorld level, Engine engine, + NativeProtocolPlayer player, String label, NativeModdedServer server, UUID playerId, CompletableFuture search, Position2 at, Throwable error) { if (ACTIVE_LOCATE_REQUESTS.get(playerId) != search) { return; @@ -605,15 +583,15 @@ final class ModdedLocateCommands { }); } - private static void teleportToLocateResult(CommandSourceStack source, ServerLevel level, Engine engine, - ServerPlayer player, String label, Position2 at) { + private static void teleportToLocateResult(NativeCommandSource source, NativeWorld level, Engine engine, + NativeProtocolPlayer player, String label, Position2 at) { // Same liveness guards the structure completion path has: the search can take up to - // two minutes, and the captured ServerPlayer may be gone or elsewhere by then. - if (player.hasDisconnected() || player.isRemoved()) { + // two minutes, and the captured NativeProtocolPlayer may be gone or elsewhere by then. + if (!player.connected()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PLAYER_DISCONNECTED_BEFORE_STRUCTURE_SEARCH_COMPLETED)); return; } - if (player.level() != level) { + if (!player.inWorld(level)) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_YOU_CHANGED_DIMENSIONS_BEFORE_STRUCTURE_SEARCH_COMPLETED_RUN_COMMAND_AGAIN)); return; } @@ -622,15 +600,7 @@ final class ModdedLocateCommands { try (GenerationSessionLease lease = engine.acquireGenerationLease("modded_locator_teleport"); IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { int blockY = engine.getMinHeight() + engine.getHeight(blockX, blockZ, false) + 2; - boolean teleported = player.teleportTo( - level, - blockX + 0.5D, - blockY, - blockZ + 0.5D, - Set.of(), - player.getYRot(), - player.getXRot(), - false); + boolean teleported = player.teleport(level, blockX + 0.5D, blockY, blockZ + 0.5D); if (!teleported) { IrisModdedCommands.fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_FOUND_AT_BUT_TELEPORTATION_FAILED, @@ -665,7 +635,7 @@ final class ModdedLocateCommands { NO_PLACEMENT } - private record NativeStructureTarget(String key, Holder.Reference holder, + private record NativeStructureTarget(String key, NativeStructureQueries.Reference holder, NativeStructureAvailability availability) { } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectCommands.java index 45616a02a..fe8dcf18f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectCommands.java @@ -18,6 +18,11 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileData; +import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.studio.tree.TreePlausibilizeBatch; @@ -27,9 +32,9 @@ import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.structure.object.IrisObjectRotation; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.modded.ModdedTileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.world.task.J; import art.arcane.volmlib.util.math.RNG; import com.mojang.brigadier.arguments.IntegerArgumentType; @@ -38,23 +43,8 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; import java.io.File; import java.io.IOException; @@ -73,17 +63,17 @@ import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedObjectCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private static final long MAX_SAVE_VOLUME = 500000L; private static final long MAX_AUTOSELECT_VOLUME = 100000L; private static final double TARGET_RANGE = 256.0D; - private static final SuggestionProvider OBJECT_KEYS = (CommandContext context, SuggestionsBuilder builder) -> { + private static final SuggestionProvider OBJECT_KEYS = (CommandContext context, SuggestionsBuilder builder) -> { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null) { - return SharedSuggestionProvider.suggest(engine.getData().getObjectLoader().getPossibleKeys(), builder); + return NativeCommandRegistration.suggest(engine.getData().getObjectLoader().getPossibleKeys(), builder); } } catch (Throwable e) { IrisModdedCommands.warnTabFailure("object keys", context.getSource(), e); @@ -91,8 +81,8 @@ public final class ModdedObjectCommands { return builder.buildFuture(); }; - private static final SuggestionProvider ROTATIONS = (CommandContext context, SuggestionsBuilder builder) -> - SharedSuggestionProvider.suggest(List.of("0", "90", "180", "270"), builder); + private static final SuggestionProvider ROTATIONS = (CommandContext context, SuggestionsBuilder builder) -> + NativeCommandRegistration.suggest(List.of("0", "90", "180", "270"), builder); private ModdedObjectCommands() { } @@ -103,25 +93,25 @@ public final class ModdedObjectCommands { SHIFT } - public static LiteralArgumentBuilder tree(String name) { + public static LiteralArgumentBuilder tree(String name) { ModdedObjectUndo.init(); - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); - root.then(Commands.literal("wand") - .executes(ModdedCommandTree.localized((CommandContext context) -> giveWand(context.getSource())))); - root.then(Commands.literal("dust") - .executes(ModdedCommandTree.localized((CommandContext context) -> giveDust(context.getSource())))); - root.then(Commands.literal("d") - .executes(ModdedCommandTree.localized((CommandContext context) -> giveDust(context.getSource())))); + root.then(NativeCommandRegistration.literal("wand") + .executes(ModdedCommandTree.localized((CommandContext context) -> giveWand(context.getSource())))); + root.then(NativeCommandRegistration.literal("dust") + .executes(ModdedCommandTree.localized((CommandContext context) -> giveDust(context.getSource())))); + root.then(NativeCommandRegistration.literal("d") + .executes(ModdedCommandTree.localized((CommandContext context) -> giveDust(context.getSource())))); - root.then(Commands.literal("save") - .then(Commands.literal("overwrite") - .then(Commands.argument("name", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> save(context.getSource(), StringArgumentType.getString(context, "name"), true))))) - .then(Commands.argument("name", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> save(context.getSource(), StringArgumentType.getString(context, "name"), false))))); + root.then(NativeCommandRegistration.literal("save") + .then(NativeCommandRegistration.literal("overwrite") + .then(NativeCommandRegistration.argument("name", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> save(context.getSource(), StringArgumentType.getString(context, "name"), true))))) + .then(NativeCommandRegistration.argument("name", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> save(context.getSource(), StringArgumentType.getString(context, "name"), false))))); root.then(pasteTree()); @@ -130,101 +120,101 @@ public final class ModdedObjectCommands { root.then(resizeTree("-", ResizeOp.CONTRACT)); root.then(resizeTree("shift", ResizeOp.SHIFT)); - root.then(Commands.literal("xpy") - .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), false)))); - root.then(Commands.literal("x+y") - .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), false)))); - root.then(Commands.literal("xay") - .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), true)))); - root.then(Commands.literal("x&y") - .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), true)))); + root.then(NativeCommandRegistration.literal("xpy") + .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), false)))); + root.then(NativeCommandRegistration.literal("x+y") + .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), false)))); + root.then(NativeCommandRegistration.literal("xay") + .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), true)))); + root.then(NativeCommandRegistration.literal("x&y") + .executes(ModdedCommandTree.localized((CommandContext context) -> autoSelect(context.getSource(), true)))); root.then(positionTree("position1", true)); root.then(positionTree("p1", true)); root.then(positionTree("position2", false)); root.then(positionTree("p2", false)); - root.then(Commands.literal("analyze") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> analyze(context.getSource(), StringArgumentType.getString(context, "key")))))); + root.then(NativeCommandRegistration.literal("analyze") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> analyze(context.getSource(), StringArgumentType.getString(context, "key")))))); - root.then(Commands.literal("shrink") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> shrink(context.getSource(), StringArgumentType.getString(context, "key")))))); + root.then(NativeCommandRegistration.literal("shrink") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> shrink(context.getSource(), StringArgumentType.getString(context, "key")))))); - root.then(Commands.literal("undo") - .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), 1))) - .then(Commands.argument("amount", IntegerArgumentType.integer(1, 32)) - .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), IntegerArgumentType.getInteger(context, "amount")))))); - root.then(Commands.literal("u") - .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), 1))) - .then(Commands.argument("amount", IntegerArgumentType.integer(1, 32)) - .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), IntegerArgumentType.getInteger(context, "amount")))))); + root.then(NativeCommandRegistration.literal("undo") + .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), 1))) + .then(NativeCommandRegistration.argument("amount", IntegerArgumentType.integer(1, 32)) + .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), IntegerArgumentType.getInteger(context, "amount")))))); + root.then(NativeCommandRegistration.literal("u") + .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), 1))) + .then(NativeCommandRegistration.argument("amount", IntegerArgumentType.integer(1, 32)) + .executes(ModdedCommandTree.localized((CommandContext context) -> undo(context.getSource(), IntegerArgumentType.getInteger(context, "amount")))))); root.then(bukkitOnly("we", "WorldEdit selection import requires the Bukkit plugin with WorldEdit installed.")); root.then(bukkitOnly("studio", "The object studio world requires the Bukkit studio toolchain; it is not available on modded servers.")); root.then(bukkitOnly("convert", "Schematic conversion (.schem -> .iob) requires the Bukkit plugin.")); - root.then(Commands.literal("plausibilize") - .then(Commands.argument("args", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> plausibilize(context.getSource(), StringArgumentType.getString(context, "args")))))); + root.then(NativeCommandRegistration.literal("plausibilize") + .then(NativeCommandRegistration.argument("args", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> plausibilize(context.getSource(), StringArgumentType.getString(context, "args")))))); return root; } - private static LiteralArgumentBuilder pasteTree() { - LiteralArgumentBuilder paste = Commands.literal("paste"); - paste.then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), 0, null)))); - paste.then(Commands.literal("rotate") - .then(Commands.argument("degrees", IntegerArgumentType.integer(-270, 270)).suggests(ROTATIONS) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), + private static LiteralArgumentBuilder pasteTree() { + LiteralArgumentBuilder paste = NativeCommandRegistration.literal("paste"); + paste.then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), 0, null)))); + paste.then(NativeCommandRegistration.literal("rotate") + .then(NativeCommandRegistration.argument("degrees", IntegerArgumentType.integer(-270, 270)).suggests(ROTATIONS) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), IntegerArgumentType.getInteger(context, "degrees"), null)))))); - paste.then(Commands.literal("at") - .then(Commands.argument("x", IntegerArgumentType.integer()) - .then(Commands.argument("y", IntegerArgumentType.integer()) - .then(Commands.argument("z", IntegerArgumentType.integer()) - .then(Commands.literal("rotate") - .then(Commands.argument("degrees", IntegerArgumentType.integer(-270, 270)).suggests(ROTATIONS) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), + paste.then(NativeCommandRegistration.literal("at") + .then(NativeCommandRegistration.argument("x", IntegerArgumentType.integer()) + .then(NativeCommandRegistration.argument("y", IntegerArgumentType.integer()) + .then(NativeCommandRegistration.argument("z", IntegerArgumentType.integer()) + .then(NativeCommandRegistration.literal("rotate") + .then(NativeCommandRegistration.argument("degrees", IntegerArgumentType.integer(-270, 270)).suggests(ROTATIONS) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), IntegerArgumentType.getInteger(context, "degrees"), - new BlockPos(IntegerArgumentType.getInteger(context, "x"), IntegerArgumentType.getInteger(context, "y"), IntegerArgumentType.getInteger(context, "z")))))))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), 0, - new BlockPos(IntegerArgumentType.getInteger(context, "x"), IntegerArgumentType.getInteger(context, "y"), IntegerArgumentType.getInteger(context, "z")))))))))); + new NativeBlockPoint(IntegerArgumentType.getInteger(context, "x"), IntegerArgumentType.getInteger(context, "y"), IntegerArgumentType.getInteger(context, "z")))))))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(OBJECT_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> paste(context.getSource(), StringArgumentType.getString(context, "key"), 0, + new NativeBlockPoint(IntegerArgumentType.getInteger(context, "x"), IntegerArgumentType.getInteger(context, "y"), IntegerArgumentType.getInteger(context, "z")))))))))); return paste; } - private static LiteralArgumentBuilder resizeTree(String name, ResizeOp op) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> resize(context.getSource(), 1, op))) - .then(Commands.argument("amount", IntegerArgumentType.integer(1, 256)) - .executes(ModdedCommandTree.localized((CommandContext context) -> resize(context.getSource(), IntegerArgumentType.getInteger(context, "amount"), op)))); + private static LiteralArgumentBuilder resizeTree(String name, ResizeOp op) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> resize(context.getSource(), 1, op))) + .then(NativeCommandRegistration.argument("amount", IntegerArgumentType.integer(1, 256)) + .executes(ModdedCommandTree.localized((CommandContext context) -> resize(context.getSource(), IntegerArgumentType.getInteger(context, "amount"), op)))); } - private static LiteralArgumentBuilder positionTree(String name, boolean first) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> position(context.getSource(), first, false))) - .then(Commands.literal("look") - .executes(ModdedCommandTree.localized((CommandContext context) -> position(context.getSource(), first, true)))); + private static LiteralArgumentBuilder positionTree(String name, boolean first) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> position(context.getSource(), first, false))) + .then(NativeCommandRegistration.literal("look") + .executes(ModdedCommandTree.localized((CommandContext context) -> position(context.getSource(), first, true)))); } - private static LiteralArgumentBuilder bukkitOnly(String name, String message) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + private static LiteralArgumentBuilder bukkitOnly(String name, String message) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), message); return 0; })); } - public static int giveWand(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + public static int giveWand(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_WAND_IS)); return 0; } - if (!player.getInventory().add(ModdedWandService.createWand())) { + if (!player.give(ModdedWandService.createWand())) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOUR_INVENTORY_IS_FULL)); return 0; } @@ -232,13 +222,13 @@ public final class ModdedObjectCommands { return 1; } - static int giveDust(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + static int giveDust(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_DUST_IS)); return 0; } - if (!player.getInventory().add(ModdedWandService.createDust())) { + if (!player.give(ModdedWandService.createDust())) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOUR_INVENTORY_IS_FULL_2)); return 0; } @@ -246,14 +236,14 @@ public final class ModdedObjectCommands { return 1; } - private static int save(CommandSourceStack source, String nameRaw, boolean overwrite) { - ServerPlayer player = source.getPlayer(); + private static int save(NativeCommandSource source, String nameRaw, boolean overwrite) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_SAVING_CAPTURES)); return 0; } - ServerLevel level = player.level(); - Engine engine = IrisModdedCommands.engineFor(level); + NativeEditWorld level = player.world(); + Engine engine = IrisModdedCommands.engineFor(level.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_OBJECTS_SAVE_INTO)); return 0; @@ -272,11 +262,11 @@ public final class ModdedObjectCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_INVALID_OBJECT_NAME, MessageArgument.untrusted("nameRaw", nameRaw))); return 0; } - BlockPos min = selection.min(); - BlockPos max = selection.max(); - int w = max.getX() - min.getX() + 1; - int h = max.getY() - min.getY() + 1; - int d = max.getZ() - min.getZ() + 1; + NativeBlockPoint min = selection.min(); + NativeBlockPoint max = selection.max(); + int w = max.x() - min.x() + 1; + int h = max.y() - min.y() + 1; + int d = max.z() - min.z() + 1; long volume = (long) w * h * d; if (volume > MAX_SAVE_VOLUME) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SELECTION_TOO_LARGE_BLOCKS_MAX, MessageArgument.untrusted("volume", volume), MessageArgument.untrusted("MAXSAVEVOLUME", MAX_SAVE_VOLUME))); @@ -305,7 +295,6 @@ public final class ModdedObjectCommands { // capture() must stay on the server thread (getBlockState/getBlockEntity are not // async-safe), but the disk write of a local, unshared object is not tick work. IrisObject object = capture(level, min, max, w, h, d, tilesSkipped, tilesSaved); - MinecraftServer server = source.getServer(); boolean finalClaimed = claimed; J.a(() -> { try { @@ -316,7 +305,7 @@ public final class ModdedObjectCommands { // Never leave a 0-byte claim file permanently blocking non-overwrite saves. file.delete(); } - server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_FAILED_SAVE_OBJECT, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))))); + source.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_FAILED_SAVE_OBJECT, MessageArgument.untrusted("value", String.valueOf(e.getMessage()))))); return; } StringBuilder tileNote = new StringBuilder(); @@ -329,61 +318,21 @@ public final class ModdedObjectCommands { } else if (tilesSkipped[0] > 0) { tileNote.append(" (").append(tilesSkipped[0]).append(" tile state(s) could not be captured)"); } - server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SAVED_OBJECTS_IOB_X_X_BLOCK_S, MessageArgument.untrusted("value", engine.getData().getDataFolder().getName()), MessageArgument.untrusted("name", name), MessageArgument.untrusted("w", w), MessageArgument.untrusted("h", h), MessageArgument.untrusted("d", d), MessageArgument.untrusted("value2", object.getBlocks().size()), MessageArgument.untrusted("tileNote", tileNote)))); + source.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SAVED_OBJECTS_IOB_X_X_BLOCK_S, MessageArgument.untrusted("value", engine.getData().getDataFolder().getName()), MessageArgument.untrusted("name", name), MessageArgument.untrusted("w", w), MessageArgument.untrusted("h", h), MessageArgument.untrusted("d", d), MessageArgument.untrusted("value2", object.getBlocks().size()), MessageArgument.untrusted("tileNote", tileNote)))); ModdedIrisLog.info("Iris object save: {} {}x{}x{} blocks={} tilesSaved={} tilesSkipped={} -> {}", name, w, h, d, object.getBlocks().size(), tilesSaved[0], tilesSkipped[0], file.getAbsolutePath()); }); return 1; } - private static IrisObject capture(ServerLevel level, BlockPos min, BlockPos max, int w, int h, int d, int[] tilesSkipped, int[] tilesSaved) { + private static IrisObject capture(NativeEditWorld level, NativeBlockPoint min, NativeBlockPoint max, int w, int h, int d, int[] tilesSkipped, int[] tilesSaved) { IrisObject object = new IrisObject(w, h, d); - HolderLookup.Provider provider = level.registryAccess(); - BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos(); - for (int x = min.getX(); x <= max.getX(); x++) { - for (int y = min.getY(); y <= max.getY(); y++) { - for (int z = min.getZ(); z <= max.getZ(); z++) { - BlockState state = level.getBlockState(cursor.set(x, y, z)); - if (state.is(Blocks.AIR)) { - continue; - } - int ox = x - min.getX(); - int oy = y - min.getY(); - int oz = z - min.getZ(); - object.setUnsigned(ox, oy, oz, ModdedBlockState.of(state, null)); - if (state.hasBlockEntity()) { - TileData tile = captureTile(level, provider, cursor.immutable(), state); - if (tile != null) { - object.setUnsignedTile(ox, oy, oz, tile); - tilesSaved[0]++; - } else { - tilesSkipped[0]++; - } - } - } - } - } + level.capture(new NativeEditWorld.Bounds(min, max), new ObjectCapture(object, tilesSkipped, tilesSaved)); return object; } - private static TileData captureTile(ServerLevel level, HolderLookup.Provider provider, BlockPos pos, BlockState state) { - BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity == null) { - return null; - } - try { - CompoundTag tag = blockEntity.saveWithFullMetadata(provider); - String snbt = NbtUtils.structureToSnbt(tag); - String blockKey = BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString(); - return ModdedTileData.capture(blockKey, snbt); - } catch (Throwable e) { - ModdedIrisLog.error("Iris tile capture failed at {} {} {}", pos.getX(), pos.getY(), pos.getZ(), e); - return null; - } - } - - private static int paste(CommandSourceStack source, String keyRaw, int rotation, BlockPos at) { - ServerLevel level = source.getLevel(); - Engine engine = IrisModdedCommands.engineFor(level); + private static int paste(NativeCommandSource source, String keyRaw, int rotation, NativeBlockPoint at) { + NativeEditWorld level = new NativeEditWorld(source.world()); + Engine engine = IrisModdedCommands.engineFor(level.world()); String key = keyRaw.trim(); IrisObject object = null; try { @@ -396,44 +345,44 @@ public final class ModdedObjectCommands { return 0; } - ServerPlayer player = source.getPlayer(); - BlockPos target = at; + NativeEditPlayer player = source.editingPlayer(); + NativeBlockPoint target = at; if (target == null) { if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_CONSOLE_MUST_SPECIFY_COORDINATES_IRIS_OBJECT_PASTE_AT_X_Y, MessageArgument.untrusted("key", key))); return 0; } - HitResult hit = player.pick(TARGET_RANGE, 1.0F, false); - if (hit.getType() != HitResult.Type.BLOCK || !(hit instanceof BlockHitResult blockHit)) { + NativeBlockPoint hit = player.pickBlock(TARGET_RANGE); + if (hit == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOU_ARE_NOT_LOOKING_AT_BLOCK_WITHIN_BLOCKS, MessageArgument.untrusted("value", (int) TARGET_RANGE))); return 0; } - target = blockHit.getBlockPos().above(); + target = hit.above(); } IrisObjectPlacement placement = new IrisObjectPlacement(); placement.setRotation(IrisObjectRotation.of(0, rotation, 0)); - ModdedObjectPlacer placer = new ModdedObjectPlacer(level, engine); + ModdedObjectPlacer placer = new ModdedObjectPlacer(level.world(), engine); try { object = placement.scaleObject(new RNG(), object, engine == null ? null : engine.getDimension()); - object.place(target.getX(), target.getY() + object.getCenter().getY(), target.getZ(), placer, placement, new RNG(), object.getLoader()); + object.place(target.x(), target.y() + object.getCenter().getY(), target.z(), placer, placement, new RNG(), object.getLoader()); } catch (Throwable e) { ModdedIrisLog.error("Iris paste failed for {}", key, e); - ModdedObjectUndo.record(player == null ? ModdedObjectUndo.CONSOLE : player.getUUID(), level, placer.undoSnapshot()); + ModdedObjectUndo.record(player == null ? ModdedObjectUndo.CONSOLE : player.id(), placer.undoSnapshot()); IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PASTE_FAILED_PARTIAL_CHANGES_RECORDED_UNDO, MessageArgument.untrusted("value", e.getClass().getSimpleName()))); return 0; } - UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.getUUID(); - ModdedObjectUndo.record(owner, level, placer.undoSnapshot()); + UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.id(); + ModdedObjectUndo.record(owner, placer.undoSnapshot()); String tileNote = tileNote(placer); - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PLACED_AT_ROT_WRITE_S_NON_AIR, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", target.getX()), MessageArgument.untrusted("value2", target.getY()), MessageArgument.untrusted("value3", target.getZ()), MessageArgument.untrusted("rotation", rotation), MessageArgument.untrusted("value4", placer.writes()), MessageArgument.untrusted("value5", placer.nonAirWrites()), MessageArgument.untrusted("tileNote", tileNote))); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_PLACED_AT_ROT_WRITE_S_NON_AIR, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", target.x()), MessageArgument.untrusted("value2", target.y()), MessageArgument.untrusted("value3", target.z()), MessageArgument.untrusted("rotation", rotation), MessageArgument.untrusted("value4", placer.writes()), MessageArgument.untrusted("value5", placer.nonAirWrites()), MessageArgument.untrusted("tileNote", tileNote))); ModdedIrisLog.info("Iris paste: {} at {},{},{} rot={} writes={} nonAir={} tilesRestored={} tilesSkipped={}", - key, target.getX(), target.getY(), target.getZ(), rotation, placer.writes(), placer.nonAirWrites(), placer.restoredTiles(), placer.skippedTiles()); + key, target.x(), target.y(), target.z(), rotation, placer.writes(), placer.nonAirWrites(), placer.restoredTiles(), placer.skippedTiles()); return placer.writes() > 0 ? 1 : 0; } - private static int resize(CommandSourceStack source, int amount, ResizeOp op) { - ServerPlayer player = source.getPlayer(); + private static int resize(NativeCommandSource source, int amount, ResizeOp op) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS)); return 0; @@ -447,15 +396,11 @@ public final class ModdedObjectCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_AREA_SELECTED)); return 0; } - Direction direction = Direction.getApproximateNearest(player.getLookAngle()); - int[] mins = {selection.min().getX(), selection.min().getY(), selection.min().getZ()}; - int[] maxs = {selection.max().getX(), selection.max().getY(), selection.max().getZ()}; - int axis = switch (direction.getAxis()) { - case X -> 0; - case Y -> 1; - case Z -> 2; - }; - int step = direction.getAxisDirection().getStep(); + NativeEditPlayer.LookDirection direction = player.lookDirection(); + int[] mins = {selection.min().x(), selection.min().y(), selection.min().z()}; + int[] maxs = {selection.max().x(), selection.max().y(), selection.max().z()}; + int axis = direction.axis(); + int step = direction.step(); switch (op) { case EXPAND -> { if (step > 0) { @@ -476,15 +421,15 @@ public final class ModdedObjectCommands { maxs[axis] += step * amount; } } - BlockPos first = new BlockPos(mins[0], mins[1], mins[2]); - BlockPos second = new BlockPos(maxs[0], maxs[1], maxs[2]); + NativeBlockPoint first = new NativeBlockPoint(mins[0], mins[1], mins[2]); + NativeBlockPoint second = new NativeBlockPoint(maxs[0], maxs[1], maxs[2]); ModdedWandService.setSelection(player, first, second); - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_MESSAGE, MessageArgument.untrusted("value", op.name().toLowerCase(Locale.ROOT)), MessageArgument.untrusted("amount", amount), MessageArgument.untrusted("value2", direction.getName()), MessageArgument.untrusted("value3", describe(first, second)))); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_MESSAGE, MessageArgument.untrusted("value", op.name().toLowerCase(Locale.ROOT)), MessageArgument.untrusted("amount", amount), MessageArgument.untrusted("value2", direction.name()), MessageArgument.untrusted("value3", describe(first, second)))); return 1; } - private static int position(CommandSourceStack source, boolean first, boolean look) { - ServerPlayer player = source.getPlayer(); + private static int position(NativeCommandSource source, boolean first, boolean look) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2)); return 0; @@ -493,31 +438,31 @@ public final class ModdedObjectCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_READY_YOUR_WAND_IRIS_WAND)); return 0; } - BlockPos pos; + NativeBlockPoint pos; if (look) { - HitResult hit = player.pick(TARGET_RANGE, 1.0F, false); - if (hit.getType() != HitResult.Type.BLOCK || !(hit instanceof BlockHitResult blockHit)) { + NativeBlockPoint hit = player.pickBlock(TARGET_RANGE); + if (hit == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_YOU_ARE_NOT_LOOKING_AT_BLOCK)); return 0; } - pos = blockHit.getBlockPos(); + pos = hit; } else { pos = player.blockPosition().below(); } ModdedWandService.Selection selection = ModdedWandService.selection(player); - BlockPos other = selection == null ? null : (first ? selection.second() : selection.first()); - BlockPos fallback = other == null ? pos : other; + NativeBlockPoint other = selection == null ? null : (first ? selection.second() : selection.first()); + NativeBlockPoint fallback = other == null ? pos : other; if (first) { ModdedWandService.setSelection(player, pos, fallback); } else { ModdedWandService.setSelection(player, fallback, pos); } - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_POSITION_SET, MessageArgument.untrusted("value", (first ? 1 : 2)), MessageArgument.untrusted("value2", pos.getX()), MessageArgument.untrusted("value3", pos.getY()), MessageArgument.untrusted("value4", pos.getZ()))); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_POSITION_SET, MessageArgument.untrusted("value", (first ? 1 : 2)), MessageArgument.untrusted("value2", pos.x()), MessageArgument.untrusted("value3", pos.y()), MessageArgument.untrusted("value4", pos.z()))); return 1; } - private static int autoSelect(CommandSourceStack source, boolean down) { - ServerPlayer player = source.getPlayer(); + private static int autoSelect(NativeCommandSource source, boolean down) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_3)); return 0; @@ -531,40 +476,40 @@ public final class ModdedObjectCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NO_AREA_SELECTED_2)); return 0; } - ServerLevel level = player.level(); - BlockPos min = selection.min(); - BlockPos max = selection.max(); - long volume = (long) (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1) * (max.getZ() - min.getZ() + 1); + NativeEditWorld level = player.world(); + NativeBlockPoint min = selection.min(); + NativeBlockPoint max = selection.max(); + long volume = (long) (max.x() - min.x() + 1) * (max.y() - min.y() + 1) * (max.z() - min.z() + 1); if (volume > MAX_AUTOSELECT_VOLUME) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_SELECTION_TOO_LARGE_AUTO_SELECT_BLOCKS_MAX, MessageArgument.untrusted("volume", volume), MessageArgument.untrusted("MAXAUTOSELECTVOLUME", MAX_AUTOSELECT_VOLUME))); return 0; } - int levelMinY = level.getMinY(); - int levelMaxY = levelMinY + level.getHeight() - 1; + int levelMinY = level.minY(); + int levelMaxY = levelMinY + level.height() - 1; - int topMinY = min.getY(); - int topMaxY = max.getY(); - while (topMaxY < levelMaxY && !boxOnlyAir(level, min.getX(), topMinY, min.getZ(), max.getX(), topMaxY, max.getZ())) { + int topMinY = min.y(); + int topMaxY = max.y(); + while (topMaxY < levelMaxY && !boxOnlyAir(level, min.x(), topMinY, min.z(), max.x(), topMaxY, max.z())) { topMinY++; topMaxY++; } topMaxY--; - int bottomY = min.getY(); + int bottomY = min.y(); if (down) { - int lowMinY = min.getY(); - int lowMaxY = max.getY(); - while (lowMinY > levelMinY && !boxOnlyAir(level, min.getX(), lowMinY, min.getZ(), max.getX(), lowMaxY, max.getZ())) { + int lowMinY = min.y(); + int lowMaxY = max.y(); + while (lowMinY > levelMinY && !boxOnlyAir(level, min.x(), lowMinY, min.z(), max.x(), lowMaxY, max.z())) { lowMinY--; lowMaxY--; } bottomY = lowMinY + 1; } - int minX = min.getX(); - int maxX = max.getX(); - int minZ = min.getZ(); - int maxZ = max.getZ(); + int minX = min.x(); + int maxX = max.x(); + int minZ = min.z(); + int maxZ = max.z(); while (minX < maxX && boxOnlyAir(level, minX, bottomY, minZ, minX, topMaxY, maxZ)) { minX++; } @@ -578,30 +523,20 @@ public final class ModdedObjectCommands { maxZ--; } - BlockPos first = new BlockPos(minX, bottomY, minZ); - BlockPos second = new BlockPos(maxX, topMaxY, maxZ); + NativeBlockPoint first = new NativeBlockPoint(minX, bottomY, minZ); + NativeBlockPoint second = new NativeBlockPoint(maxX, topMaxY, maxZ); ModdedWandService.setSelection(player, first, second); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_AUTO_SELECT_COMPLETE, MessageArgument.untrusted("value", describe(first, second)))); return 1; } - private static boolean boxOnlyAir(ServerLevel level, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { - BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos(); - for (int x = minX; x <= maxX; x++) { - for (int y = minY; y <= maxY; y++) { - for (int z = minZ; z <= maxZ; z++) { - if (!level.getBlockState(cursor.set(x, y, z)).isAir()) { - return false; - } - } - } - } - return true; + private static boolean boxOnlyAir(NativeEditWorld level, int minX, int minY, int minZ, int maxX, int maxY, int maxZ) { + return level.boxOnlyAir(new NativeEditWorld.Bounds(new NativeBlockPoint(minX, minY, minZ), new NativeBlockPoint(maxX, maxY, maxZ))); } - private static int analyze(CommandSourceStack source, String keyRaw) { - ServerLevel level = source.getLevel(); - Engine engine = IrisModdedCommands.engineFor(level); + private static int analyze(NativeCommandSource source, String keyRaw) { + NativeEditWorld level = new NativeEditWorld(source.world()); + Engine engine = IrisModdedCommands.engineFor(level.world()); String key = keyRaw.trim(); IrisObject object = null; try { @@ -616,9 +551,9 @@ public final class ModdedObjectCommands { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_OBJECT_SIZE, MessageArgument.untrusted("value", object.getW()), MessageArgument.untrusted("value2", object.getH()), MessageArgument.untrusted("value3", object.getD()))); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_BLOCKS_USED, MessageArgument.untrusted("value", object.getBlocks().size()))); Map counts = new HashMap<>(); - Iterator values = object.getBlocks().values(); + Iterator values = object.getBlocks().values(); while (values.hasNext()) { - PlatformBlockState state = values.next(); + NativeBlockState state = values.next(); counts.merge(state.key(), 1, Integer::sum); } List> sorted = new ArrayList<>(counts.entrySet()); @@ -639,9 +574,9 @@ public final class ModdedObjectCommands { return 1; } - private static int shrink(CommandSourceStack source, String keyRaw) { - ServerLevel level = source.getLevel(); - Engine engine = IrisModdedCommands.engineFor(level); + private static int shrink(NativeCommandSource source, String keyRaw) { + NativeEditWorld level = new NativeEditWorld(source.world()); + Engine engine = IrisModdedCommands.engineFor(level.world()); String key = keyRaw.trim(); IrisObject object = null; try { @@ -671,8 +606,8 @@ public final class ModdedObjectCommands { return 1; } - private static int plausibilize(CommandSourceStack source, String raw) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + private static int plausibilize(NativeCommandSource source, String raw) { + Engine engine = IrisModdedCommands.engineFor(source.world()); IrisData data = engine == null ? null : engine.getData(); boolean dryRun = false; int reach = TreePlausibilizer.DEFAULT_REACH; @@ -711,15 +646,14 @@ public final class ModdedObjectCommands { )); boolean dry = dryRun; int reachFinal = reach; - MinecraftServer server = source.getServer(); J.a(() -> TreePlausibilizeBatch.run(targets, dry, reachFinal, data, (TreePlausibilizeBatch.Output output) -> - server.execute(() -> IrisModdedCommands.ok(source, output.text())))); + source.execute(() -> IrisModdedCommands.ok(source, output.text())))); return 1; } - private static int undo(CommandSourceStack source, int amount) { - ServerPlayer player = source.getPlayer(); - UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.getUUID(); + private static int undo(NativeCommandSource source, int amount) { + NativeEditPlayer player = source.editingPlayer(); + UUID owner = player == null ? ModdedObjectUndo.CONSOLE : player.id(); int available = ModdedObjectUndo.size(owner); if (available == 0) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_OBJECT_COMMANDS_NOTHING_UNDO)); @@ -730,9 +664,9 @@ public final class ModdedObjectCommands { return 1; } - private static String describe(BlockPos first, BlockPos second) { - return "(" + first.getX() + "," + first.getY() + "," + first.getZ() + ") -> (" - + second.getX() + "," + second.getY() + "," + second.getZ() + ")"; + private static String describe(NativeBlockPoint first, NativeBlockPoint second) { + return "(" + first.x() + "," + first.y() + "," + first.z() + ") -> (" + + second.x() + "," + second.y() + "," + second.z() + ")"; } static String tileNote(ModdedObjectPlacer placer) { @@ -745,4 +679,32 @@ public final class ModdedObjectCommands { } return note.toString(); } + + private record ObjectCapture(IrisObject object, int[] skipped, int[] saved) implements NativeEditWorld.CaptureTarget { + @Override + public void block(int x, int y, int z, NativeBlockState state) { + object.setUnsigned(x, y, z, state); + } + + @Override + public void tile(int x, int y, int z, NativeTileData tile) { + if (tile == null) { + skipped[0]++; + } else { + object.setUnsignedTile(x, y, z, ModdedTileData.wrap(tile)); + saved[0]++; + } + } + + @Override + public void warning(String message) { + IrisLogging.warn(message); + } + + @Override + public void failure(NativeEditWorld.BlockFailure failure) { + NativeBlockPoint pos = failure.position(); + ModdedIrisLog.error("Iris tile capture failed at {} {} {}", pos.x(), pos.y(), pos.z(), failure.error()); + } + } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectPlacer.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectPlacer.java index 9771bac5b..6ac61f5b3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectPlacer.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectPlacer.java @@ -18,47 +18,41 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.modded.ModdedBlockResolution; -import art.arcane.iris.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.modded.ModdedTileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.mantle.runtime.Mantle; import art.arcane.volmlib.util.matter.Matter; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -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.level.levelgen.Heightmap; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; final class ModdedObjectPlacer implements IObjectPlacer { private static final int DEFAULT_FLUID_HEIGHT = 63; - private final ServerLevel level; + private final NativeEditWorld level; private final Engine engine; - private final Map undo = new HashMap<>(); + private final NativeEditWorld.EditSession undo; private int writes = 0; private int nonAirWrites = 0; private int skippedTiles = 0; private int restoredTiles = 0; - ModdedObjectPlacer(ServerLevel level, @Nullable Engine engine) { - this.level = level; + ModdedObjectPlacer(NativeWorld level, @Nullable Engine engine) { + this.level = new NativeEditWorld(level); + this.undo = this.level.editSession(new NativeEditWorld.EditOptions(level.minHeight() + 1, level.maxHeight(), true)); this.engine = engine; } - Map undoSnapshot() { + NativeEditWorld.EditSession undoSnapshot() { return undo; } @@ -80,36 +74,29 @@ final class ModdedObjectPlacer implements IObjectPlacer { @Override public int getHighest(int x, int z, IrisData data) { - return level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) - 1; + return level.highest(x, z, false); } @Override public int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { - return level.getHeight(ignoreFluid ? Heightmap.Types.OCEAN_FLOOR : Heightmap.Types.MOTION_BLOCKING, x, z) - 1; + return level.highest(x, z, ignoreFluid); } @Override - public void set(int x, int y, int z, PlatformBlockState s) { - if (y <= level.getMinY() || y >= level.getMinY() + level.getHeight()) { + public void set(int x, int y, int z, NativeBlockState s) { + NativeEditWorld.WriteResult result = undo.set(x, y, z, s); + if (result == NativeEditWorld.WriteResult.SKIPPED) { return; } - BlockPos pos = new BlockPos(x, y, z); - BlockState current = level.getBlockState(pos); - if (current.is(Blocks.BEDROCK)) { - return; - } - BlockState target = (BlockState) s.nativeHandle(); - undo.putIfAbsent(pos, current); - level.setBlock(pos, target, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); writes++; - if (!target.isAir()) { + if (result == NativeEditWorld.WriteResult.BLOCK) { nonAirWrites++; } } @Override - public PlatformBlockState get(int x, int y, int z) { - return ModdedBlockState.of(level.getBlockState(new BlockPos(x, y, z)), null); + public NativeBlockState get(int x, int y, int z) { + return level.block(x, y, z); } @Override @@ -136,7 +123,7 @@ final class ModdedObjectPlacer implements IObjectPlacer { @Override public boolean isSolid(int x, int y, int z) { - return ModdedBlockResolution.isSolid(level.getBlockState(new BlockPos(x, y, z))); + return level.solid(x, y, z); } /** @@ -170,34 +157,10 @@ final class ModdedObjectPlacer implements IObjectPlacer { skippedTiles++; return; } - BlockPos pos = new BlockPos(xx, yy, zz); - BlockState state = level.getBlockState(pos); - BlockState adjusted = moddedTile.adjustBlockState(state); - if (adjusted != state) { - level.setBlock(pos, adjusted, Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); - state = adjusted; - } - if (!state.hasBlockEntity()) { - skippedTiles++; - return; - } - try { - BlockEntity restored = level.getBlockEntity(pos); - if (restored == null && state.getBlock() instanceof EntityBlock entityBlock) { - restored = entityBlock.newBlockEntity(pos, state); - } - if (restored == null) { - skippedTiles++; - return; - } - level.setBlockEntity(restored); - if (!moddedTile.apply(restored, level)) { - skippedTiles++; - return; - } + if (level.restoreTile(new NativeBlockPoint(xx, yy, zz), moddedTile.nativeData(), + error -> ModdedIrisLog.error("Iris tile restore failed at {} {} {}", xx, yy, zz, error))) { restoredTiles++; - } catch (Throwable e) { - ModdedIrisLog.error("Iris tile restore failed at {} {} {}", xx, yy, zz, e); + } else { skippedTiles++; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectUndo.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectUndo.java index c3f5426ee..f802c0234 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectUndo.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedObjectUndo.java @@ -19,11 +19,8 @@ package art.arcane.iris.modded.command; import art.arcane.iris.modded.ModdedIrisLog; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.state.BlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.util.ArrayDeque; import java.util.Deque; @@ -41,7 +38,7 @@ public final class ModdedObjectUndo { private ModdedObjectUndo() { } - private record Entry(ServerLevel level, Map blocks) { + private record Entry(NativeEditWorld.EditSession blocks) { } public static void init() { @@ -50,13 +47,13 @@ public final class ModdedObjectUndo { } } - public static void record(UUID owner, ServerLevel level, Map oldBlocks) { - if (oldBlocks == null || oldBlocks.isEmpty()) { + public static void record(UUID owner, NativeEditWorld.EditSession oldBlocks) { + if (oldBlocks == null || oldBlocks.empty()) { return; } Deque queue = UNDOS.computeIfAbsent(owner, (UUID key) -> new ArrayDeque<>()); synchronized (queue) { - queue.addLast(new Entry(level, oldBlocks)); + queue.addLast(new Entry(oldBlocks)); while (queue.size() > MAX_ENTRIES_PER_OWNER) { queue.pollFirst(); } @@ -89,22 +86,14 @@ public final class ModdedObjectUndo { } // Identity check, not just null: a studio closed and reopened under the same // dimension id must never have blocks replayed into the dead ServerLevel. - MinecraftServer server = entry.level().getServer(); - if (server == null || server.getLevel(entry.level().dimension()) != entry.level()) { + if (!entry.blocks().world().current()) { ModdedIrisLog.warn("Iris object undo: skipped a stale entry for removed dimension {}", - entry.level().dimension().identifier()); + entry.blocks().world().key()); continue; } - int writes = 0; - for (Map.Entry block : entry.blocks().entrySet()) { - try { - entry.level().setBlock(block.getKey(), block.getValue(), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); - writes++; - } catch (Throwable e) { - ModdedIrisLog.error("Iris object undo: failed to revert a block at {}", block.getKey(), e); - } - } - ModdedIrisLog.info("Iris object undo: reverted {} block(s) in {}", writes, entry.level().dimension().identifier()); + int writes = entry.blocks().restore(failure -> ModdedIrisLog.error( + "Iris object undo: failed to revert a block at {}", failure.position(), failure.error())); + ModdedIrisLog.info("Iris object undo: reverted {} block(s) in {}", writes, entry.blocks().world().key()); reverted++; } return reverted; @@ -114,14 +103,14 @@ public final class ModdedObjectUndo { * Drops every entry recorded against the given level. Called on dimension removal so a * closed studio releases its block snapshots and the ServerLevel reference. */ - public static void forget(ServerLevel level) { + public static void forget(NativeWorld level) { if (level == null) { return; } UNDOS.entrySet().removeIf((Map.Entry> ownerEntry) -> { Deque queue = ownerEntry.getValue(); synchronized (queue) { - queue.removeIf((Entry entry) -> entry.level() == level); + queue.removeIf((Entry entry) -> entry.blocks().world().represents(level)); return queue.isEmpty(); } }); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPackCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPackCommands.java index 37f44db6a..f0046bae8 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPackCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPackCommands.java @@ -30,9 +30,8 @@ import art.arcane.iris.modded.ModdedEngineBootstrap; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; import java.io.File; import java.util.ArrayList; @@ -44,64 +43,64 @@ import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedPackCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private ModdedPackCommands() { } - public static LiteralArgumentBuilder tree(String name) { - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + public static LiteralArgumentBuilder tree(String name) { + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); - root.then(Commands.literal("validate") - .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("v") - .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("validate") + .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("v") + .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> validate(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("cleanup") - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), false))) - .then(Commands.literal("apply") - .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); - root.then(Commands.literal("c") - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), false))) - .then(Commands.literal("apply") - .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); + root.then(NativeCommandRegistration.literal("cleanup") + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), false))) + .then(NativeCommandRegistration.literal("apply") + .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); + root.then(NativeCommandRegistration.literal("c") + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), false))) + .then(NativeCommandRegistration.literal("apply") + .executes(ModdedCommandTree.localized((CommandContext context) -> cleanup(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); - root.then(Commands.literal("restore") - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), false))) - .then(Commands.literal("apply") - .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); - root.then(Commands.literal("r") - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), false))) - .then(Commands.literal("apply") - .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); + root.then(NativeCommandRegistration.literal("restore") + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), false))) + .then(NativeCommandRegistration.literal("apply") + .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); + root.then(NativeCommandRegistration.literal("r") + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), false))) + .then(NativeCommandRegistration.literal("apply") + .executes(ModdedCommandTree.localized((CommandContext context) -> restore(context.getSource(), StringArgumentType.getString(context, "pack"), true)))))); - root.then(Commands.literal("status") - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("s") - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("status") + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("s") + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("compat") - .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("cp") - .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("compat") + .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("cp") + .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> compat(context.getSource(), StringArgumentType.getString(context, "pack")))))); return root; } @@ -110,7 +109,7 @@ public final class ModdedPackCommands { return IrisPlatforms.get().packsFolderNoCreate(); } - private static int validate(CommandSourceStack source, String pack) { + private static int validate(NativeCommandSource source, String pack) { File packsRoot = packsRoot(); if (!packsRoot.isDirectory()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACKS_FOLDER_NOT_FOUND, MessageArgument.untrusted("value", packsRoot.getAbsolutePath()))); @@ -134,7 +133,6 @@ public final class ModdedPackCommands { targets.add(target); } - MinecraftServer server = source.getServer(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATING_PACK_S, MessageArgument.untrusted("value", targets.size()))); Thread thread = new Thread(() -> { int broken = 0; @@ -145,35 +143,34 @@ public final class ModdedPackCommands { if (!result.isLoadable()) { broken++; } - server.execute(() -> report(source, result)); + source.execute(() -> report(source, result)); } catch (Throwable e) { ModdedIrisLog.error("Iris pack validation failed for {}", target.getName(), e); - server.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_FAILED, MessageArgument.untrusted("value", target.getName()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))))); + source.execute(() -> IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_FAILED, MessageArgument.untrusted("value", target.getName()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage()))))); broken++; } } int brokenTotal = broken; - server.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_COMPLETE_BROKEN_PACKS, MessageArgument.untrusted("brokenTotal", brokenTotal), MessageArgument.untrusted("value", targets.size())))); + source.execute(() -> IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_VALIDATION_COMPLETE_BROKEN_PACKS, MessageArgument.untrusted("brokenTotal", brokenTotal), MessageArgument.untrusted("value", targets.size())))); }, "Iris Pack Validator"); thread.setDaemon(true); thread.start(); return 1; } - private static int cleanup(CommandSourceStack source, String pack, boolean apply) { + private static int cleanup(NativeCommandSource source, String pack, boolean apply) { File packFolder = PackDirectoryResolver.resolveExisting(packsRoot(), pack); if (packFolder == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_NOT_FOUND_UNDER_2, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", packsRoot().getAbsolutePath()))); return 0; } - MinecraftServer server = source.getServer(); Thread thread = new Thread(() -> { if (apply) { PackResourceCleanup.ApplyResult result = PackResourceCleanup.apply(packFolder); - server.execute(() -> reportCleanupApply(source, pack, result)); + source.execute(() -> reportCleanupApply(source, pack, result)); } else { PackResourceCleanup.Preview result = PackResourceCleanup.preview(packFolder); - server.execute(() -> reportCleanupPreview(source, pack, result)); + source.execute(() -> reportCleanupPreview(source, pack, result)); } }, "Iris Pack Cleanup"); thread.setDaemon(true); @@ -181,20 +178,19 @@ public final class ModdedPackCommands { return 1; } - private static int restore(CommandSourceStack source, String pack, boolean apply) { + private static int restore(NativeCommandSource source, String pack, boolean apply) { File packFolder = PackDirectoryResolver.resolveExisting(packsRoot(), pack); if (packFolder == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_NOT_FOUND_UNDER_3, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", packsRoot().getAbsolutePath()))); return 0; } - MinecraftServer server = source.getServer(); Thread thread = new Thread(() -> { if (apply) { PackResourceCleanup.RestoreResult result = PackResourceCleanup.restoreLatest(packFolder); - server.execute(() -> reportRestoreApply(source, pack, result)); + source.execute(() -> reportRestoreApply(source, pack, result)); } else { PackResourceCleanup.RestorePreview result = PackResourceCleanup.previewRestore(packFolder); - server.execute(() -> reportRestorePreview(source, pack, result)); + source.execute(() -> reportRestorePreview(source, pack, result)); } }, "Iris Pack Restore"); thread.setDaemon(true); @@ -202,7 +198,7 @@ public final class ModdedPackCommands { return 1; } - private static int status(CommandSourceStack source, String pack) { + private static int status(NativeCommandSource source, String pack) { if (pack == null || pack.isBlank()) { Map snapshot = PackValidationRegistry.snapshot(); if (snapshot.isEmpty()) { @@ -225,7 +221,7 @@ public final class ModdedPackCommands { return 1; } - private static int compat(CommandSourceStack source, String pack) { + private static int compat(NativeCommandSource source, String pack) { if (pack == null || pack.isBlank()) { Map snapshot = PackValidationRegistry.snapshot(); if (snapshot.isEmpty()) { @@ -296,7 +292,7 @@ public final class ModdedPackCommands { return persisted == null || persisted.isBlank() ? "unknown" : persisted; } - private static void report(CommandSourceStack source, PackValidationResult result) { + private static void report(NativeCommandSource source, PackValidationResult result) { if (result.isLoadable()) { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_PACK_IS_LOADABLE_WARNINGS, MessageArgument.untrusted("value", result.getPackName()), MessageArgument.untrusted("value2", result.getWarnings().size()))); } else { @@ -314,7 +310,7 @@ public final class ModdedPackCommands { } } - private static void reportCleanupPreview(CommandSourceStack source, String pack, PackResourceCleanup.Preview result) { + private static void reportCleanupPreview(NativeCommandSource source, String pack, PackResourceCleanup.Preview result) { if (!result.success()) { IrisModdedCommands.fail(source, result.error()); return; @@ -328,7 +324,7 @@ public final class ModdedPackCommands { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RUN_IRIS_PACK_CLEANUP_APPLY_QUARANTINE_AFTER_FRESH_SCAN, MessageArgument.untrusted("pack", pack))); } - private static void reportCleanupApply(CommandSourceStack source, String pack, PackResourceCleanup.ApplyResult result) { + private static void reportCleanupApply(NativeCommandSource source, String pack, PackResourceCleanup.ApplyResult result) { if (!result.success()) { IrisModdedCommands.fail(source, result.error()); reportPaths(source, result.quarantinedPaths(), "still quarantined"); @@ -342,7 +338,7 @@ public final class ModdedPackCommands { reportPaths(source, result.quarantinedPaths(), "quarantined"); } - private static void reportRestorePreview(CommandSourceStack source, String pack, PackResourceCleanup.RestorePreview result) { + private static void reportRestorePreview(NativeCommandSource source, String pack, PackResourceCleanup.RestorePreview result) { if (!result.success()) { IrisModdedCommands.fail(source, result.error()); return; @@ -361,7 +357,7 @@ public final class ModdedPackCommands { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RUN_IRIS_PACK_RESTORE_APPLY_RESTORE_AFTER_FRESH_CONFLICT_CHECK, MessageArgument.untrusted("pack", pack))); } - private static void reportRestoreApply(CommandSourceStack source, String pack, PackResourceCleanup.RestoreResult result) { + private static void reportRestoreApply(NativeCommandSource source, String pack, PackResourceCleanup.RestoreResult result) { if (!result.conflicts().isEmpty()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_RESTORE_REFUSED_BECAUSE_DESTINATION_S_ALREADY_EXIST, MessageArgument.untrusted("value", result.conflicts().size()))); reportPaths(source, result.conflicts(), "conflict"); @@ -379,7 +375,7 @@ public final class ModdedPackCommands { reportPaths(source, result.restoredPaths(), "restored"); } - private static void reportPaths(CommandSourceStack source, List paths, String label) { + private static void reportPaths(NativeCommandSource source, List paths, String label) { int max = Math.min(10, paths.size()); for (int i = 0; i < max; i++) { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_PACK_COMMANDS_MESSAGE_3, MessageArgument.untrusted("label", label), MessageArgument.untrusted("value", paths.get(i)))); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenBossBar.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenBossBar.java index aecd11925..df1dddd25 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenBossBar.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenBossBar.java @@ -10,11 +10,10 @@ import art.arcane.iris.spi.protocol.IrisProtocol; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.localization.MessageArgument; import art.arcane.volmlib.util.localization.TextKey; -import net.minecraft.network.chat.Component; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerBossEvent; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.BossEvent; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandBossBar; import java.util.UUID; @@ -22,35 +21,25 @@ public final class ModdedPregenBossBar { private static final UUID BAR_ID = UUID.fromString("14150000-1e15-4a2b-9c3d-1115e9a1cafe"); private static final int UPDATE_INTERVAL_TICKS = 10; - private static volatile ServerBossEvent bar; - private static volatile UUID viewer; + private static volatile NativeCommandBossBar bar; private static int sinceUpdate; private ModdedPregenBossBar() { } - public static synchronized void begin(ServerPlayer player) { + public static synchronized void begin(NativeCommandSource source) { clear(); - if (player == null) { + UUID player = source.playerId(); + if (player == null || hasClientPregenHud(player)) { return; } - if (hasClientPregenHud(player)) { - return; - } - viewer = player.getUUID(); - bar = new ServerBossEvent( - BAR_ID, - Component.literal(IrisLanguage.plain(RuntimeUiMessages.PREGEN_STARTING)), - BossEvent.BossBarColor.GREEN, - BossEvent.BossBarOverlay.PROGRESS - ); - bar.setProgress(0.0F); - bar.addPlayer(player); + bar = new NativeCommandBossBar(source, new NativeCommandBossBar.Options( + BAR_ID, NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.PREGEN_STARTING)))); sinceUpdate = UPDATE_INTERVAL_TICKS; } - public static void tick(MinecraftServer server) { - ServerBossEvent active = bar; + public static void tick() { + NativeCommandBossBar active = bar; if (active == null) { return; } @@ -63,24 +52,10 @@ public final class ModdedPregenBossBar { return; } sinceUpdate = 0; - reattach(server, active); - active.setProgress((float) clamp01(progress.percent() / 100.0D)); - active.setColor(progress.paused() ? BossEvent.BossBarColor.YELLOW : BossEvent.BossBarColor.GREEN); - active.setName(nameFor(progress)); + active.update(nameFor(progress), (float) clamp01(progress.percent() / 100.0D), progress.paused()); } - private static void reattach(MinecraftServer server, ServerBossEvent active) { - UUID id = viewer; - if (id == null || server == null) { - return; - } - ServerPlayer player = server.getPlayerList().getPlayer(id); - if (player != null && !active.getPlayers().contains(player)) { - active.addPlayer(player); - } - } - - private static Component nameFor(PregeneratorJob.PregenProgress progress) { + private static NativeCommandText nameFor(PregeneratorJob.PregenProgress progress) { TextKey message = progress.paused() ? RuntimeUiMessages.PREGEN_BOSSBAR_PAUSED : RuntimeUiMessages.PREGEN_BOSSBAR_RUNNING; @@ -115,12 +90,12 @@ public final class ModdedPregenBossBar { ), ModdedCommandFeedback.DARK_GREEN); } - private static boolean hasClientPregenHud(ServerPlayer player) { + private static boolean hasClientPregenHud(UUID player) { IrisProtocolServer protocol = IrisServices.getOrNull(IrisProtocolServer.class); if (protocol == null) { return false; } - IrisSession session = protocol.registry().get(player.getUUID().toString()); + IrisSession session = protocol.registry().get(player.toString()); return session != null && session.isReady() && session.hasCapability(IrisProtocol.CAPABILITY_PREGEN); } @@ -135,13 +110,11 @@ public final class ModdedPregenBossBar { } public static synchronized void clear() { - ServerBossEvent existing = bar; + NativeCommandBossBar existing = bar; if (existing != null) { - existing.removeAllPlayers(); - existing.setVisible(false); + existing.close(); } bar = null; - viewer = null; sinceUpdate = 0; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenCommands.java index 063ad2422..82cc9753e 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenCommands.java @@ -27,34 +27,35 @@ import art.arcane.volmlib.util.localization.MessageArgument; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.arguments.DimensionArgument; -import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandArguments; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; final class ModdedPregenCommands { private ModdedPregenCommands() { } - static int pregenStart(CommandContext context, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) throws CommandSyntaxException { - CommandSourceStack source = context.getSource(); + static int pregenStart(CommandContext context, boolean withDimension, boolean withCenter, boolean gui, boolean sync, boolean nocache) throws CommandSyntaxException { + NativeCommandSource source = context.getSource(); int radius = IntegerArgumentType.getInteger(context, "radius"); int centerX = withCenter ? IntegerArgumentType.getInteger(context, "x") : 0; int centerZ = withCenter ? IntegerArgumentType.getInteger(context, "z") : 0; - ServerLevel level = withDimension ? DimensionArgument.getDimension(context, "dimension") : source.getLevel(); + NativeWorld level = withDimension ? NativeCommandArguments.getDimension(context, "dimension") : source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { if (withDimension) { - IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_SEE_IRIS_INFO_LOADED_IRIS, MessageArgument.untrusted("value", level.dimension().identifier()))); + IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_IS_NOT_GENERATED_BY_IRIS_SEE_IRIS_INFO_LOADED_IRIS, MessageArgument.untrusted("value", level.name()))); } else { - IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CURRENT_DIMENSION_IS_NOT_GENERATED_BY_IRIS_NAME_ONE_EXPLICITLY, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("radius", radius))); + IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_CURRENT_DIMENSION_IS_NOT_GENERATED_BY_IRIS_NAME_ONE_EXPLICITLY, MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("radius", radius))); } return 0; } boolean showGui = gui && ModdedGuiHost.isGuiLaunchable(); boolean started; try { - started = ModdedPregenJob.start(source.getServer(), level, engine, radius, centerX, centerZ, showGui, sync, !nocache); + started = ModdedPregenJob.start(level, engine, new ModdedPregenJob.PregenOptions( + radius, centerX, centerZ, showGui, sync, !nocache)); } catch (IllegalArgumentException failure) { IrisModdedCommands.fail(source, failure.getMessage()); return 0; @@ -63,7 +64,7 @@ final class ModdedPregenCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(IrisMessages.PREGEN_ALREADY_RUNNING)); return 0; } - ModdedPregenBossBar.begin(source.getPlayer()); + ModdedPregenBossBar.begin(source); String guiNote; if (!gui) { guiNote = ""; @@ -73,11 +74,11 @@ final class ModdedPregenCommands { guiNote = " (GUI requested but unavailable: " + ModdedGuiHost.guiUnavailableReason() + ")"; } String modeNote = " Mode: " + (sync ? "sync" : "async") + (nocache ? ", cache disabled." : ", resumable (checkpoint cache)."); - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PREGEN_STARTED_BY_BLOCKS_FROM_PROGRESS_LOGS_CONSOLE_SEE_IRIS, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", (radius * 2)), MessageArgument.untrusted("value3", (radius * 2)), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ), MessageArgument.untrusted("modeNote", modeNote), MessageArgument.untrusted("guiNote", guiNote))); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_PREGEN_STARTED_BY_BLOCKS_FROM_PROGRESS_LOGS_CONSOLE_SEE_IRIS, MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("value2", (radius * 2)), MessageArgument.untrusted("value3", (radius * 2)), MessageArgument.untrusted("centerX", centerX), MessageArgument.untrusted("centerZ", centerZ), MessageArgument.untrusted("modeNote", modeNote), MessageArgument.untrusted("guiNote", guiNote))); return 1; } - static int pregenStop(CommandSourceStack source) { + static int pregenStop(NativeCommandSource source) { if (ModdedPregenJob.stop()) { ModdedPregenBossBar.clear(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_STOPPING_PREGENERATION_FINISHING_UP_CURRENT_REGION)); @@ -87,7 +88,7 @@ final class ModdedPregenCommands { return 0; } - static int pregenPause(CommandSourceStack source) { + static int pregenPause(NativeCommandSource source) { Boolean paused = ModdedPregenJob.pauseResume(); if (paused == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NO_ACTIVE_PREGENERATION_TASK_PAUSE_RESUME)); @@ -97,8 +98,8 @@ final class ModdedPregenCommands { return 1; } - static int pregenStatus(CommandSourceStack source) { - Component status = ModdedPregenJob.statusComponent(); + static int pregenStatus(NativeCommandSource source) { + NativeCommandText status = ModdedPregenJob.statusComponent(); if (status == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.IRIS_MODDED_COMMANDS_NO_ACTIVE_PREGENERATION_TASK)); return 0; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenJob.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenJob.java index e8ff62109..9c774329a 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenJob.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenJob.java @@ -19,6 +19,9 @@ package art.arcane.iris.modded.command; import art.arcane.iris.studio.view.PregeneratorJob; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedPregenRuntime; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.iris.world.pregen.PregenPerformanceProfile; @@ -31,12 +34,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.Position2; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.network.chat.Component; -import net.minecraft.network.chat.MutableComponent; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.dimension.DimensionType; -import net.minecraft.world.level.storage.LevelResource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.io.File; import java.util.Objects; @@ -51,27 +49,28 @@ public final class ModdedPregenJob { private ModdedPregenJob() { } - public static boolean start(MinecraftServer server, ServerLevel level, Engine engine, int radiusBlocks, int centerBlockX, int centerBlockZ, boolean gui, boolean sync, boolean cached) { + public static boolean start(NativeWorld world, Engine engine, PregenOptions options) { if (PregeneratorJob.getInstance() != null) { return false; } + NativePregenRuntime runtime = NativeModdedPregenRuntime.from(world); PregenTask task = PregenTask.builder() - .gui(gui) - .center(new Position2(centerBlockX, centerBlockZ)) - .radiusX(radiusBlocks) - .radiusZ(radiusBlocks) + .gui(options.gui()) + .center(new Position2(options.centerBlockX(), options.centerBlockZ())) + .radiusX(options.radiusBlocks()) + .radiusZ(options.radiusBlocks()) .build(); - ModdedPregenMethod moddedMethod = new ModdedPregenMethod(level, engine, sync); + ModdedPregenMethod moddedMethod = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, options.sync())); PregeneratorMethod method = moddedMethod; - if (cached) { + if (options.cached()) { method = new CachedPregenMethod(new CachedPregenMethod.Configuration(method, - PregenCache.create(cacheDirectory(level)).sync(), task, + PregenCache.create(cacheDirectory(runtime)).sync(), task, PregenSavedChunkStatus.fromWorld(engine.getWorld().worldFolder().toPath()))); } ActivePregen active = new ActivePregen(engine, moddedMethod); ACTIVE.set(active); - dimension = level.dimension().identifier().toString(); + dimension = runtime.worldIdentity(); try { PregeneratorJob job = new PregeneratorJob(new PregeneratorJob.Configuration( task, @@ -90,8 +89,8 @@ public final class ModdedPregenJob { } } - private static File cacheDirectory(ServerLevel level) { - File worldFolder = DimensionType.getStorageFolder(level.dimension(), level.getServer().getWorldPath(LevelResource.ROOT)).toFile(); + private static File cacheDirectory(NativePregenRuntime runtime) { + File worldFolder = runtime.worldFolder(); return new File(worldFolder, "iris" + File.separator + "pregen"); } @@ -121,29 +120,29 @@ public final class ModdedPregenJob { return PregeneratorJob.isPaused(); } - public static Component statusComponent() { + public static NativeCommandText statusComponent() { PregeneratorJob.PregenProgress progress = PregeneratorJob.progressSnapshot(); if (progress == null) { return null; } - MutableComponent status = Component.empty(); + NativeCommandText status = NativeCommandText.empty(); status.append(ModdedCommandFeedback.header(IrisLanguage.plain(RuntimeUiMessages.PREGEN_HEADER))); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.text(IrisLanguage.plain( RuntimeUiMessages.PREGEN_STATUS_CONTEXT, MessageArgument.untrusted("dimension", dimension), MessageArgument.untrusted("method", progress.method()) ), ModdedCommandFeedback.DARK_GREEN)); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.progressBar(progress.percent(), 32)); status.append(ModdedCommandFeedback.text(" " + IrisLanguage.plain( RuntimeUiMessages.PREGEN_STATUS_PROGRESS, MessageArgument.trusted("percent", String.format("%.1f", progress.percent())) ), ModdedCommandFeedback.USAGE)); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.text(chunksStatus(progress), ModdedCommandFeedback.DARK_GREEN)); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.text(IrisLanguage.plain( progress.paused() ? RuntimeUiMessages.PREGEN_STATUS_TIME_PAUSED @@ -151,7 +150,7 @@ public final class ModdedPregenJob { MessageArgument.trusted("eta", Form.duration(progress.eta(), 2)), MessageArgument.trusted("elapsed", Form.duration(progress.elapsed(), 2)) ), ModdedCommandFeedback.DARK_GREEN)); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.button( IrisLanguage.plain(RuntimeUiMessages.PREGEN_PAUSE_BUTTON), "/iris pregen pause", @@ -165,7 +164,7 @@ public final class ModdedPregenJob { IrisLanguage.plain(RuntimeUiMessages.PREGEN_STOP_HOVER), true )); - status.append(Component.literal("\n")); + status.append(NativeCommandText.literal("\n")); status.append(ModdedCommandFeedback.footer()); return status; } @@ -246,6 +245,10 @@ public final class ModdedPregenJob { return new IllegalStateException("Iris modded pregenerator shutdown failed", failure); } + public record PregenOptions(int radiusBlocks, int centerBlockX, int centerBlockZ, + boolean gui, boolean sync, boolean cached) { + } + private record ActivePregen(Engine engine, ModdedPregenMethod method) { private boolean targets(String worldIdentity) { return engine != null diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java index b410f12ba..011fed5e3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java @@ -19,6 +19,8 @@ package art.arcane.iris.modded.command; import art.arcane.iris.modded.ModdedIrisLog; +import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime; +import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime.ChunkLoad; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.world.pregen.PregenListener; import art.arcane.iris.world.pregen.PregenMantleBackpressure; @@ -26,19 +28,10 @@ import art.arcane.iris.world.pregen.PregeneratorMethod; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.modded.ModdedGenPool; import art.arcane.volmlib.util.mantle.runtime.Mantle; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.dedicated.DedicatedServer; -import net.minecraft.server.level.ChunkResult; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.TicketType; -import net.minecraft.world.level.ChunkPos; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Semaphore; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -47,14 +40,13 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; public final class ModdedPregenMethod implements PregeneratorMethod { - private static final TicketType PREGEN_TICKET = new TicketType(TicketType.NO_TIMEOUT, TicketType.FLAG_LOADING | TicketType.FLAG_KEEP_DIMENSION_ACTIVE); private static final int ADAPTIVE_TIMEOUT_STEP = 3; private static final long ADAPTIVE_RECOVERY_INTERVAL = 64L; private static final long FINAL_SAVE_TIMEOUT_MILLIS = 10_000L; private static final long FINAL_SAVE_POLL_MILLIS = 50L; private static final AtomicBoolean SERVER_DEAD_LOGGED = new AtomicBoolean(); - private final ServerLevel level; + private final NativePregenRuntime runtime; private final Engine engine; private final boolean sync; private final int maxInFlight; @@ -76,15 +68,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod { private final PregenMantleBackpressure backpressure; private final PauseWhenEmptyGuard pauseGuard; - public ModdedPregenMethod(ServerLevel level, Engine engine) { - this(level, engine, false); - } - - public ModdedPregenMethod(ServerLevel level, Engine engine, boolean sync) { - this.level = level; - this.engine = engine; - this.sync = sync; - this.pauseGuard = new PauseWhenEmptyGuard(level.getServer()); + public ModdedPregenMethod(Configuration configuration) { + runtime = configuration.runtime(); + engine = configuration.engine(); + sync = configuration.sync(); + pauseGuard = new PauseWhenEmptyGuard(runtime); IrisSettings.IrisSettingsPregen pregen = IrisSettings.get().getPregen(); this.maxInFlight = Math.max(8, pregen.getModdedPregenInFlight()); this.minInFlight = Math.max(4, Math.min(16, maxInFlight / 4)); @@ -98,14 +86,14 @@ public final class ModdedPregenMethod implements PregeneratorMethod { pregen.getMantleBackpressureTimeoutMs(), () -> { }, - () -> "dim=" + level.dimension().identifier()); + () -> "dim=" + runtime.worldIdentity()); } @Override public void init() { pauseGuard.suspend(); ModdedIrisLog.info("Iris modded pregen init: dim={} mode={} inFlightCap={} timeout={}s workerPool={} chunkSystem={}", - level.dimension().identifier(), + runtime.worldIdentity(), sync ? "sync" : "async", sync ? 1 : maxInFlight, timeoutSeconds, @@ -127,7 +115,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } } ModdedIrisLog.info("Iris modded pregen done: dim={} completed={} peakInFlight={} finalLimit={}", - level.dimension().identifier(), completed.get(), inFlightPeak.get(), adaptiveLimit.get()); + runtime.worldIdentity(), completed.get(), inFlightPeak.get(), adaptiveLimit.get()); if (deferFinalSaveIfRequested()) { return; } @@ -143,7 +131,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } boolean deferFinalSaveToServerThread() { - if (!level.getServer().isSameThread()) { + if (!runtime.isServerThread()) { return false; } finalSaveOnServerThread.set(true); @@ -185,8 +173,8 @@ public final class ModdedPregenMethod implements PregeneratorMethod { saveFinalLevel(); return; } - MinecraftServer server = level.getServer(); - if (server.isSameThread()) { + NativePregenRuntime server = runtime; + if (server.isServerThread()) { saveLevelOnServerThread(); return; } @@ -194,8 +182,8 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } private void saveFinalLevel() { - MinecraftServer server = level.getServer(); - if (server.isSameThread()) { + NativePregenRuntime server = runtime; + if (server.isServerThread()) { saveLevelOnServerThread(); finalSaveCompleted.set(true); return; @@ -207,7 +195,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { FinalSaveRequest request = new FinalSaveRequest(); if (!queuedFinalSave.compareAndSet(null, request)) { throw new IllegalStateException("Iris pregen final save is already queued for " - + level.dimension().identifier()); + + runtime.worldIdentity()); } try { server.execute(() -> executeFinalSave(request)); @@ -243,7 +231,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } long remainingNanos = deadline - System.nanoTime(); if (remainingNanos <= 0L) { - ModdedIrisLog.warn("Iris pregen level save did not complete in time for {}", level.dimension().identifier()); + ModdedIrisLog.warn("Iris pregen level save did not complete in time for {}", runtime.worldIdentity()); return; } long waitMillis = Math.max(1L, Math.min(FINAL_SAVE_POLL_MILLIS, @@ -258,9 +246,9 @@ public final class ModdedPregenMethod implements PregeneratorMethod { continue; } catch (ExecutionException e) { Throwable cause = e.getCause() == null ? e : e.getCause(); - ModdedIrisLog.error("Iris pregen level save failed for {}", level.dimension().identifier(), cause); + ModdedIrisLog.error("Iris pregen level save failed for {}", runtime.worldIdentity(), cause); throw new IllegalStateException("Iris pregen level save failed for " - + level.dimension().identifier(), cause); + + runtime.worldIdentity(), cause); } } } @@ -285,13 +273,13 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } private void saveLevelOnServerThread() { - level.save(null, false, false); + runtime.save(); } private void requireServerThreadForFinalSave() { - if (!level.getServer().isSameThread()) { + if (!runtime.isServerThread()) { throw new IllegalStateException("Iris pregen final save must run on the Minecraft server thread for " - + level.dimension().identifier()); + + runtime.worldIdentity()); } } @@ -327,15 +315,12 @@ public final class ModdedPregenMethod implements PregeneratorMethod { private void generateChunkSync(int x, int z, PregenListener listener) { listener.onChunkGenerating(x, z); - ChunkPos pos = new ChunkPos(x, z); - CompletableFuture loadFuture = CompletableFuture - .supplyAsync(() -> level.getChunkSource().addTicketAndLoadWithRadius(PREGEN_TICKET, pos, 0), level.getServer()) - .thenCompose((CompletableFuture inner) -> inner); + CompletableFuture loadFuture = runtime.loadChunk(x, z); markSubmitted(); try { - Object result = loadFuture.get(timeoutSeconds, TimeUnit.SECONDS); - if (result instanceof ChunkResult chunkResult && !chunkResult.isSuccess()) { - ModdedIrisLog.warn("Iris pregen chunk {},{} returned no chunk: {}", x, z, chunkResult.getError()); + ChunkLoad result = loadFuture.get(timeoutSeconds, TimeUnit.SECONDS); + if (result != null && !result.successful()) { + ModdedIrisLog.warn("Iris pregen chunk {},{} returned no chunk: {}", x, z, result.error()); listener.onChunkFailed(x, z); return; } @@ -352,16 +337,16 @@ public final class ModdedPregenMethod implements PregeneratorMethod { listener.onChunkFailed(x, z); } finally { markFinished(); - level.getServer().execute(() -> level.getChunkSource().removeTicketWithRadius(PREGEN_TICKET, pos, 0)); + runtime.releaseChunk(x, z); } } private void generateChunkAsync(int x, int z, PregenListener listener) { // A stopped server executes submitted tasks inline and its chunk futures never complete; without // this abort the pregen thread spins hot against the dead chunk source until the JVM dies. - if (level.getServer().isStopped() || !level.getServer().isRunning()) { + if (!runtime.isRunning()) { if (SERVER_DEAD_LOGGED.compareAndSet(false, true)) { - ModdedIrisLog.error("Iris pregen aborting: the server is no longer running (dim={})", level.dimension().identifier()); + ModdedIrisLog.error("Iris pregen aborting: the server is no longer running (dim={})", runtime.worldIdentity()); } listener.onChunkFailed(x, z); ModdedPregenJob.stop(); @@ -381,13 +366,10 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } markSubmitted(); - ChunkPos pos = new ChunkPos(x, z); - CompletableFuture loadFuture = CompletableFuture - .supplyAsync(() -> level.getChunkSource().addTicketAndLoadWithRadius(PREGEN_TICKET, pos, 0), level.getServer()) - .thenCompose((CompletableFuture inner) -> inner); + CompletableFuture loadFuture = runtime.loadChunk(x, z); - loadFuture.orTimeout(timeoutSeconds, TimeUnit.SECONDS).whenComplete((Object result, Throwable error) -> { - level.getServer().execute(() -> level.getChunkSource().removeTicketWithRadius(PREGEN_TICKET, pos, 0)); + loadFuture.orTimeout(timeoutSeconds, TimeUnit.SECONDS).whenComplete((ChunkLoad result, Throwable error) -> { + runtime.releaseChunk(x, z); try { if (error != null) { if (unwrap(error) instanceof TimeoutException) { @@ -397,8 +379,8 @@ public final class ModdedPregenMethod implements PregeneratorMethod { listener.onChunkFailed(x, z); return; } - if (result instanceof ChunkResult chunkResult && !chunkResult.isSuccess()) { - ModdedIrisLog.warn("Iris pregen chunk {},{} returned no chunk: {}", x, z, chunkResult.getError()); + if (result != null && !result.successful()) { + ModdedIrisLog.warn("Iris pregen chunk {},{} returned no chunk: {}", x, z, result.error()); listener.onChunkFailed(x, z); return; } @@ -501,17 +483,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } private String describeWorkerPool() { - Executor exec = level.getServer().executor; - if (exec == null) { - return "unknown"; - } - if (exec instanceof ThreadPoolExecutor tpe) { - return "ThreadPoolExecutor(core=" + tpe.getCorePoolSize() + ",max=" + tpe.getMaximumPoolSize() + ")"; - } - if (exec instanceof ForkJoinPool fjp) { - return "ForkJoinPool(parallelism=" + fjp.getParallelism() + ")"; - } - return exec.getClass().getSimpleName(); + return runtime.workerPoolDescription(); } private static Throwable unwrap(Throwable error) { @@ -546,21 +518,21 @@ public final class ModdedPregenMethod implements PregeneratorMethod { private static final class PauseWhenEmptyGuard { private static final int NOT_SUSPENDED = -1; - private final MinecraftServer server; + private final NativePregenRuntime server; private final AtomicInteger suspendedFrom = new AtomicInteger(NOT_SUSPENDED); private final AtomicReference crashRestoreHook = new AtomicReference<>(); - private PauseWhenEmptyGuard(MinecraftServer server) { + private PauseWhenEmptyGuard(NativePregenRuntime server) { this.server = server; } private void suspend() { - if (!(server instanceof DedicatedServer dedicated)) { + if (!server.supportsPauseWhenEmpty()) { return; } int current; try { - current = dedicated.pauseWhenEmptySeconds(); + current = server.pauseWhenEmptySeconds(); } catch (Throwable e) { ModdedIrisLog.warn("Iris pregen could not read pause-when-empty-seconds: {}", e.toString()); return; @@ -569,14 +541,14 @@ public final class ModdedPregenMethod implements PregeneratorMethod { return; } try { - dedicated.setPauseWhenEmptySeconds(0); + server.pauseWhenEmptySeconds(0); } catch (Throwable e) { refuse(current, e.toString()); return; } int applied; try { - applied = dedicated.pauseWhenEmptySeconds(); + applied = server.pauseWhenEmptySeconds(); } catch (Throwable e) { refuse(current, e.toString()); return; @@ -597,11 +569,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod { private void restoreOnce(String what) { int previous = suspendedFrom.getAndSet(NOT_SUSPENDED); - if (previous == NOT_SUSPENDED || !(server instanceof DedicatedServer dedicated)) { + if (previous == NOT_SUSPENDED || !server.supportsPauseWhenEmpty()) { return; } try { - dedicated.setPauseWhenEmptySeconds(previous); + server.pauseWhenEmptySeconds(previous); ModdedIrisLog.info("Iris pregen: {} pause-when-empty ({}s)", what, previous); } catch (Throwable e) { ModdedIrisLog.error("Iris pregen could not restore pause-when-empty-seconds={}: {}. Set pause-when-empty-seconds={} in server.properties.", @@ -637,11 +609,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod { * True when the server can still stop ticking under a running job. */ private boolean pauseStillArmed() { - if (suspendedFrom.get() != NOT_SUSPENDED || !(server instanceof DedicatedServer dedicated)) { + if (suspendedFrom.get() != NOT_SUSPENDED || !server.supportsPauseWhenEmpty()) { return false; } try { - return dedicated.pauseWhenEmptySeconds() > 0 && server.getPlayerCount() == 0; + return server.pauseWhenEmptySeconds() > 0 && server.playerCount() == 0; } catch (Throwable e) { return false; } @@ -660,6 +632,9 @@ public final class ModdedPregenMethod implements PregeneratorMethod { } } + public record Configuration(NativePregenRuntime runtime, Engine engine, boolean sync) { + } + private static final class FinalSaveRequest { private final CompletableFuture completion = new CompletableFuture<>(); private final AtomicBoolean active = new AtomicBoolean(true); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedRegen.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedRegen.java index 09171b09f..b28a0b777 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedRegen.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedRegen.java @@ -24,36 +24,19 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.modded.ModdedBlockBuffer; -import art.arcane.iris.modded.ModdedEngineBootstrap; -import art.arcane.iris.modded.service.ModdedChunkUpdateService; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.ChunkSpiral; import art.arcane.iris.generation.concurrent.MultiBurst; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.format.Form; import art.arcane.volmlib.util.math.M; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.core.BlockPos; -import net.minecraft.core.SectionPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.block.Blocks; -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.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.phys.AABB; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkRegeneration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; @@ -69,19 +52,17 @@ public final class ModdedRegen { private static final long FINAL_APPLY_TIMEOUT_MILLIS = 300000L; private static final AtomicBoolean ACTIVE = new AtomicBoolean(false); - private final CommandSourceStack source; - private final MinecraftServer server; - private final ServerLevel level; + private final NativeCommandSource source; + private final NativeChunkRegeneration regeneration; private final IrisModdedChunkGenerator generator; private final Engine engine; private final int centerX; private final int centerZ; private final int radius; - private ModdedRegen(CommandSourceStack source, ServerLevel level, IrisModdedChunkGenerator generator, Engine engine, int centerX, int centerZ, int radius) { + private ModdedRegen(NativeCommandSource source, NativeWorld level, IrisModdedChunkGenerator generator, Engine engine, int centerX, int centerZ, int radius) { this.source = source; - this.server = source.getServer(); - this.level = level; + this.regeneration = new NativeChunkRegeneration(level); this.generator = generator; this.engine = engine; this.centerX = centerX; @@ -89,18 +70,18 @@ public final class ModdedRegen { this.radius = Math.max(0, radius); } - public static void start(CommandSourceStack source, ServerLevel level, IrisModdedChunkGenerator generator, Engine engine, ServerPlayer player, int radius) { + public static void start(NativeCommandSource source, NativeWorld level, IrisModdedChunkGenerator generator, Engine engine, NativeProtocolPlayer player, int radius) { if (!ACTIVE.compareAndSet(false, true)) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_REGEN_REGEN_IS_ALREADY_RUNNING)); return; } - int centerX = player.blockPosition().getX() >> 4; - int centerZ = player.blockPosition().getZ() >> 4; + int centerX = player.blockX() >> 4; + int centerZ = player.blockZ() >> 4; ModdedRegen job = new ModdedRegen(source, level, generator, engine, centerX, centerZ, radius); int chunks = (job.radius * 2 + 1) * (job.radius * 2 + 1); job.ok("Regen started: " + chunks + " chunk(s) around " + centerX + "," + centerZ + ". Deleting and regenerating in place."); ModdedIrisLog.info("Iris regen start: dim={} center={},{} radius={} chunks={}", - level.dimension().identifier(), centerX, centerZ, job.radius, chunks); + level.name(), centerX, centerZ, job.radius, chunks); Thread thread = new Thread(job::run, "Iris Regenerate"); thread.setDaemon(true); thread.start(); @@ -146,7 +127,7 @@ public final class ModdedRegen { int total = targets.size(); int stride = total <= 64 ? 1 : 32; int height = engine.getMaxHeight() - engine.getMinHeight(); - PlatformBlockState air = IrisPlatforms.get().registries().air(); + NativeBlockState air = IrisPlatforms.get().registries().air(); for (int[] target : targets) { int chunkX = target[0]; @@ -167,7 +148,7 @@ public final class ModdedRegen { return; } ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, air); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); try { engine.generate(chunkX << 4, chunkZ << 4, blocks, biomes, false); } catch (Throwable e) { @@ -178,7 +159,7 @@ public final class ModdedRegen { allApplied.countDown(); return; } - server.execute(() -> { + source.execute(() -> { boolean success = false; try { if (aborted.get()) { @@ -217,90 +198,18 @@ public final class ModdedRegen { return applied.get(); } - private void apply(int chunkX, int chunkZ, ModdedBlockBuffer blocks, Hunk biomes) { - LevelChunk chunk = level.getChunk(chunkX, chunkZ); - ChunkPos pos = chunk.getPos(); - discardEntities(pos); - for (BlockPos blockEntityPos : new ArrayList<>(chunk.getBlockEntities().keySet())) { - chunk.removeBlockEntity(blockEntityPos); - } - - int dimMinY = engine.getMinHeight(); - int height = engine.getMaxHeight() - dimMinY; - int baseX = pos.getMinBlockX(); - int baseZ = pos.getMinBlockZ(); - BlockState airState = Blocks.AIR.defaultBlockState(); - ThreadedLevelLightEngine lightEngine = (ThreadedLevelLightEngine) level.getChunkSource().getLightEngine(); - List lightChecks = new ArrayList<>(); - - for (int i = 0; i < chunk.getSectionsCount(); i++) { - LevelChunkSection section = chunk.getSection(i); - int sectionBaseY = chunk.getSectionYFromSectionIndex(i) << 4; - section.acquire(); - try { - for (int y = 0; y < 16; y++) { - int worldY = sectionBaseY + y; - int bufferY = worldY - dimMinY; - boolean inRange = bufferY >= 0 && bufferY < height; - for (int x = 0; x < 16; x++) { - for (int z = 0; z < 16; z++) { - BlockState target = airState; - if (inRange && !blocks.isAir(x, bufferY, z)) { - target = (BlockState) blocks.getRaw(x, bufferY, z).nativeHandle(); - } - BlockState previous = section.setBlockState(x, y, z, target, false); - if (previous != target && (previous.getLightEmission() != target.getLightEmission() - || previous.getLightDampening() != target.getLightDampening() - || previous.propagatesSkylightDown() != target.propagatesSkylightDown())) { - lightChecks.add(new BlockPos(baseX + x, worldY, baseZ + z)); - } - if (target.hasBlockEntity() && target.getBlock() instanceof EntityBlock entityBlock) { - BlockEntity entity = entityBlock.newBlockEntity(new BlockPos(baseX + x, worldY, baseZ + z), target); - if (entity != null) { - chunk.setBlockEntity(entity); - } - } - } - } - } - } finally { - section.release(); - } - lightEngine.updateSectionStatus(SectionPos.of(pos, chunk.getSectionYFromSectionIndex(i)), section.hasOnlyAir()); - } - - ModdedChunkUpdateService updateService = ModdedEngineBootstrap.services().service(ModdedChunkUpdateService.class); - if (updateService == null) { - throw new IllegalStateException("Iris chunk update service is unavailable during regeneration"); - } - updateService.updateRegeneratedChunk(engine, level, chunkX, chunkZ); - Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); - chunk.fillBiomesFromNoise(generator.regenBiomeResolver(), level.getChunkSource().randomState().sampler()); - chunk.markUnsaved(); - - for (BlockPos check : lightChecks) { - lightEngine.checkBlock(check); - } - - for (ServerPlayer tracking : level.getChunkSource().chunkMap.getPlayers(pos, false)) { - tracking.connection.chunkSender.dropChunk(tracking, pos); - tracking.connection.chunkSender.markChunkPendingToSend(chunk); - } - } - - private void discardEntities(ChunkPos pos) { - AABB box = new AABB(pos.getMinBlockX(), level.getMinY(), pos.getMinBlockZ(), - pos.getMaxBlockX() + 1, level.getMaxY() + 1, pos.getMaxBlockZ() + 1); - for (Entity entity : level.getEntities((Entity) null, box, (Entity e) -> !(e instanceof ServerPlayer))) { - entity.discard(); - } + private void apply(int chunkX, int chunkZ, ModdedBlockBuffer blocks, Hunk biomes) { + regeneration.apply(new NativeChunkRegeneration.Replacement(chunkX, chunkZ, + engine.getMinHeight(), engine.getMaxHeight() - engine.getMinHeight(), + blocks::rawOrNull, generator.regenBiomeResolver()), + () -> generator.updateRegeneratedChunk(chunkX, chunkZ)); } private void ok(String message) { - server.execute(() -> IrisModdedCommands.ok(source, message)); + source.execute(() -> IrisModdedCommands.ok(source, message)); } private void fail(String message) { - server.execute(() -> IrisModdedCommands.fail(source, message)); + source.execute(() -> IrisModdedCommands.fail(source, message)); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStructureCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStructureCommands.java index ca432b154..c01e159c9 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStructureCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStructureCommands.java @@ -36,11 +36,10 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; import java.io.File; import java.util.List; @@ -52,34 +51,34 @@ import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedStructureCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; - private static final SuggestionProvider IRIS_STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestIrisStructureKeys(context, builder); - private static final SuggestionProvider ALL_STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> IrisModdedCommands.suggestStructureKeys(context, builder); + private static final SuggestionProvider IRIS_STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> suggestIrisStructureKeys(context, builder); + private static final SuggestionProvider ALL_STRUCTURE_KEYS = (CommandContext context, SuggestionsBuilder builder) -> IrisModdedCommands.suggestStructureKeys(context, builder); private ModdedStructureCommands() { } - public static LiteralArgumentBuilder tree(String name) { - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + public static LiteralArgumentBuilder tree(String name) { + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); - root.then(Commands.literal("list") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); - root.then(Commands.literal("ls") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("list") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("ls") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); - root.then(Commands.literal("info") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> info(context.getSource(), StringArgumentType.getString(context, "key")))))); + root.then(NativeCommandRegistration.literal("info") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> info(context.getSource(), StringArgumentType.getString(context, "key")))))); - root.then(Commands.literal("place") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> place(context.getSource(), StringArgumentType.getString(context, "key")))))); - root.then(Commands.literal("p") - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> place(context.getSource(), StringArgumentType.getString(context, "key")))))); + root.then(NativeCommandRegistration.literal("place") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> place(context.getSource(), StringArgumentType.getString(context, "key")))))); + root.then(NativeCommandRegistration.literal("p") + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(IRIS_STRUCTURE_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> place(context.getSource(), StringArgumentType.getString(context, "key")))))); root.then(message("import", "Structure import rebuilds vanilla & datapack structures as editable Iris resources through Bukkit/NMS template managers; run /iris structure import on a Bukkit server against this pack, then copy the pack folder over.")); root.then(message("import-all", "Structure import rebuilds vanilla & datapack structures as editable Iris resources through Bukkit/NMS template managers; run /iris structure import on a Bukkit server against this pack, then copy the pack folder over.")); @@ -94,29 +93,29 @@ public final class ModdedStructureCommands { return root; } - private static LiteralArgumentBuilder verifyTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.verifyStructures(context.getSource(), null))) - .then(Commands.argument("key", StringArgumentType.greedyString()).suggests(ALL_STRUCTURE_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.verifyStructures( + private static LiteralArgumentBuilder verifyTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.verifyStructures(context.getSource(), null))) + .then(NativeCommandRegistration.argument("key", StringArgumentType.greedyString()).suggests(ALL_STRUCTURE_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> IrisModdedCommands.verifyStructures( context.getSource(), StringArgumentType.getString(context, "key"))))); } - private static LiteralArgumentBuilder message(String name, String text) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + private static LiteralArgumentBuilder message(String name, String text) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; })) - .then(Commands.argument("args", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + .then(NativeCommandRegistration.argument("args", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; }))); } - private static IrisData dataFor(CommandSourceStack source) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + private static IrisData dataFor(NativeCommandSource source) { + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_RUN_THIS_FROM)); return null; @@ -124,7 +123,7 @@ public final class ModdedStructureCommands { return engine.getData(); } - private static int list(CommandSourceStack source) { + private static int list(NativeCommandSource source) { IrisData data = dataFor(source); if (data == null) { return 0; @@ -134,7 +133,7 @@ public final class ModdedStructureCommands { return 1; } - private static int info(CommandSourceStack source, String keyRaw) { + private static int info(NativeCommandSource source, String keyRaw) { IrisData data = dataFor(source); if (data == null) { return 0; @@ -167,13 +166,13 @@ public final class ModdedStructureCommands { return 1; } - private static int place(CommandSourceStack source, String keyRaw) { - ServerPlayer player = source.getPlayer(); + private static int place(NativeCommandSource source, String keyRaw) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_STRUCTURE_IS)); return 0; } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); IrisData data = dataFor(source); if (data == null) { @@ -185,9 +184,9 @@ public final class ModdedStructureCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_NO_IRIS_STRUCTURE_THIS_PACK_2, MessageArgument.untrusted("key", key))); return 0; } - int originX = player.blockPosition().getX(); - int originY = player.blockPosition().getY(); - int originZ = player.blockPosition().getZ(); + int originX = player.blockPosition().x(); + int originY = player.blockPosition().y(); + int originZ = player.blockPosition().z(); StructureAssembler assembler = StructureAssembler.forData( data, structure, new IrisPosition(originX, originY, originZ)); RNG rng = new RNG((long) originX * 341873128712L + originZ); @@ -198,7 +197,7 @@ public final class ModdedStructureCommands { return 0; } ModdedObjectPlacer placer = new ModdedObjectPlacer(level, engine); - UUID owner = player.getUUID(); + UUID owner = player.id(); try { for (PlacedStructurePiece piece : pieces) { IrisObjectPlacement config = new IrisObjectPlacement(); @@ -212,22 +211,22 @@ public final class ModdedStructureCommands { } } catch (Throwable e) { ModdedIrisLog.error("Iris structure place failed for {}", key, e); - ModdedObjectUndo.record(owner, level, placer.undoSnapshot()); + ModdedObjectUndo.record(owner, placer.undoSnapshot()); IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_PLACE_FAILED_PARTIAL_CHANGES_RECORDED_UNDO, MessageArgument.untrusted("value", e.getClass().getSimpleName()))); return 0; } - ModdedObjectUndo.record(owner, level, placer.undoSnapshot()); + ModdedObjectUndo.record(owner, placer.undoSnapshot()); String tileNote = ModdedObjectCommands.tileNote(placer); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STRUCTURE_COMMANDS_PLACED_PIECES_WRITE_S_AT_YOUR_LOCATION_IRIS_OBJECT_UNDO, MessageArgument.untrusted("key", key), MessageArgument.untrusted("value", pieces.size()), MessageArgument.untrusted("value2", placer.writes()), MessageArgument.untrusted("tileNote", tileNote))); return 1; } - private static CompletableFuture suggestIrisStructureKeys(CommandContext context, SuggestionsBuilder builder) { + private static CompletableFuture suggestIrisStructureKeys(CommandContext context, SuggestionsBuilder builder) { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null && engine.getData().getStructureLoader() != null) { - return SharedSuggestionProvider.suggest(engine.getData().getStructureLoader().getPossibleKeys(), builder); + return NativeCommandRegistration.suggest(engine.getData().getStructureLoader().getPossibleKeys(), builder); } } catch (Throwable ignored) { } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStudioCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStudioCommands.java index 71a691aaf..2a1ae18b6 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStudioCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStudioCommands.java @@ -18,6 +18,9 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.view.WorldView; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldView; + import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.studio.view.GuiHost; @@ -62,12 +65,12 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldTeleport; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; import org.zeroturnaround.zip.ZipUtil; import java.awt.Desktop; @@ -91,7 +94,7 @@ import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedStudioCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private static final Pattern PROJECT_NAME = Pattern.compile("[a-z0-9_-]+"); private static final Pattern STUDIO_ID_SANITIZER = Pattern.compile("[^a-z0-9_-]"); private static final String STUDIO_NAMESPACE = "irisworldgen"; @@ -100,12 +103,12 @@ public final class ModdedStudioCommands { private static final UUID CONSOLE_OWNER = new UUID(0L, 0L); private static final Map STUDIOS = new ConcurrentHashMap<>(); private static final ModdedStudioTransitionQueue TRANSITIONS = new ModdedStudioTransitionQueue(); - private static final SuggestionProvider GENERATOR_KEYS = (CommandContext context, SuggestionsBuilder builder) -> { + private static final SuggestionProvider GENERATOR_KEYS = (CommandContext context, SuggestionsBuilder builder) -> { ModdedCommandFeedback.tab(context.getSource()); try { - Engine engine = IrisModdedCommands.engineFor(context.getSource().getLevel()); + Engine engine = IrisModdedCommands.engineFor(context.getSource().world()); if (engine != null) { - return SharedSuggestionProvider.suggest(engine.getData().getGeneratorLoader().getPossibleKeys(), builder); + return NativeCommandRegistration.suggest(engine.getData().getGeneratorLoader().getPossibleKeys(), builder); } } catch (Throwable e) { IrisModdedCommands.warnTabFailure("generator keys", context.getSource(), e); @@ -116,10 +119,10 @@ public final class ModdedStudioCommands { private ModdedStudioCommands() { } - public static LiteralArgumentBuilder tree(String name) { - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + public static LiteralArgumentBuilder tree(String name) { + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); root.then(createTree("create")); root.then(createTree("+")); @@ -127,28 +130,28 @@ public final class ModdedStudioCommands { root.then(packageTree("package")); root.then(packageTree("pkg")); - root.then(Commands.literal("version") - .executes(ModdedCommandTree.localized((CommandContext context) -> version(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> version(context.getSource(), StringArgumentType.getString(context, "pack")))))); + root.then(NativeCommandRegistration.literal("version") + .executes(ModdedCommandTree.localized((CommandContext context) -> version(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> version(context.getSource(), StringArgumentType.getString(context, "pack")))))); - root.then(Commands.literal("regions") - .executes(ModdedCommandTree.localized((CommandContext context) -> regions(context.getSource(), 500))) - .then(Commands.argument("radius", IntegerArgumentType.integer(8, 1000)) - .executes(ModdedCommandTree.localized((CommandContext context) -> regions(context.getSource(), IntegerArgumentType.getInteger(context, "radius")))))); + root.then(NativeCommandRegistration.literal("regions") + .executes(ModdedCommandTree.localized((CommandContext context) -> regions(context.getSource(), 500))) + .then(NativeCommandRegistration.argument("radius", IntegerArgumentType.integer(8, 1000)) + .executes(ModdedCommandTree.localized((CommandContext context) -> regions(context.getSource(), IntegerArgumentType.getInteger(context, "radius")))))); root.then(openTree("open")); root.then(openTree("o")); - root.then(Commands.literal("close") - .executes(ModdedCommandTree.localized((CommandContext context) -> close(context.getSource())))); - root.then(Commands.literal("x") - .executes(ModdedCommandTree.localized((CommandContext context) -> close(context.getSource())))); - root.then(Commands.literal("tpstudio") - .executes(ModdedCommandTree.localized((CommandContext context) -> tpStudio(context.getSource())))); - root.then(Commands.literal("stp") - .executes(ModdedCommandTree.localized((CommandContext context) -> tpStudio(context.getSource())))); - root.then(Commands.literal("status") - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); + root.then(NativeCommandRegistration.literal("close") + .executes(ModdedCommandTree.localized((CommandContext context) -> close(context.getSource())))); + root.then(NativeCommandRegistration.literal("x") + .executes(ModdedCommandTree.localized((CommandContext context) -> close(context.getSource())))); + root.then(NativeCommandRegistration.literal("tpstudio") + .executes(ModdedCommandTree.localized((CommandContext context) -> tpStudio(context.getSource())))); + root.then(NativeCommandRegistration.literal("stp") + .executes(ModdedCommandTree.localized((CommandContext context) -> tpStudio(context.getSource())))); + root.then(NativeCommandRegistration.literal("status") + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); root.then(workspaceTree("vscode", true)); root.then(workspaceTree("vsc", true)); root.then(workspaceTree("update", false)); @@ -171,27 +174,27 @@ public final class ModdedStudioCommands { return root; } - private static LiteralArgumentBuilder createTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> + private static LiteralArgumentBuilder createTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> create(context.getSource(), "studio", DEFAULT_TEMPLATE))) - .then(Commands.argument("name", StringArgumentType.word()) - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.argument("name", StringArgumentType.word()) + .executes(ModdedCommandTree.localized((CommandContext context) -> create(context.getSource(), StringArgumentType.getString(context, "name"), DEFAULT_TEMPLATE))) - .then(Commands.argument("template", StringArgumentType.word()) + .then(NativeCommandRegistration.argument("template", StringArgumentType.word()) .suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> + .executes(ModdedCommandTree.localized((CommandContext context) -> create(context.getSource(), StringArgumentType.getString(context, "name"), StringArgumentType.getString(context, "template")))))); } - private static LiteralArgumentBuilder packageTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> pkg(context.getSource(), null))) - .then(Commands.argument("pack", StringArgumentType.word()) + private static LiteralArgumentBuilder packageTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> pkg(context.getSource(), null))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()) .suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> + .executes(ModdedCommandTree.localized((CommandContext context) -> pkg(context.getSource(), StringArgumentType.getString(context, "pack"))))); } @@ -200,49 +203,49 @@ public final class ModdedStudioCommands { STUDIOS.clear(); } - private static LiteralArgumentBuilder openTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> openHelp(context.getSource()))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), 1337L))) - .then(Commands.argument("seed", LongArgumentType.longArg()) - .executes(ModdedCommandTree.localized((CommandContext context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), LongArgumentType.getLong(context, "seed")))))); + private static LiteralArgumentBuilder openTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> openHelp(context.getSource()))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), 1337L))) + .then(NativeCommandRegistration.argument("seed", LongArgumentType.longArg()) + .executes(ModdedCommandTree.localized((CommandContext context) -> open(context.getSource(), StringArgumentType.getString(context, "pack"), LongArgumentType.getLong(context, "seed")))))); } - private static int openHelp(CommandSourceStack source) { + private static int openHelp(NativeCommandSource source) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PROVIDE_DIMENSION_PACK_IRIS_STUDIO_OPEN_PACK_SEED)); return 0; } - private static LiteralArgumentBuilder noiseTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), null, 12345L))) - .then(Commands.argument("generator", StringArgumentType.word()).suggests(GENERATOR_KEYS) - .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), StringArgumentType.getString(context, "generator"), 12345L))) - .then(Commands.argument("seed", LongArgumentType.longArg()) - .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), StringArgumentType.getString(context, "generator"), LongArgumentType.getLong(context, "seed")))))); + private static LiteralArgumentBuilder noiseTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), null, 12345L))) + .then(NativeCommandRegistration.argument("generator", StringArgumentType.word()).suggests(GENERATOR_KEYS) + .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), StringArgumentType.getString(context, "generator"), 12345L))) + .then(NativeCommandRegistration.argument("seed", LongArgumentType.longArg()) + .executes(ModdedCommandTree.localized((CommandContext context) -> noise(context.getSource(), StringArgumentType.getString(context, "generator"), LongArgumentType.getLong(context, "seed")))))); } - private static LiteralArgumentBuilder mapTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> map(context.getSource()))); + private static LiteralArgumentBuilder mapTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> map(context.getSource()))); } - private static LiteralArgumentBuilder imageMapTree(String name) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> imageMap(context.getSource()))); + private static LiteralArgumentBuilder imageMapTree(String name) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> imageMap(context.getSource()))); } - private static int noise(CommandSourceStack source, String generatorKey, long seed) { - ServerLevel level = source.getLevel(); + private static int noise(NativeCommandSource source, String generatorKey, long seed) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (!GuiHost.isAvailable() || !IrisSettings.get().getGui().isUseServerLaunchedGuis()) { IrisModdedCommands.fail(source, guiUnavailableMessage()); return 0; } if (engine != null) { - ServerPlayer player = source.getPlayer(); - ModdedGuiHost.bindContext(source.getServer(), level, engine, player == null ? null : player.getUUID()); + NativeProtocolPlayer player = source.player(); + ModdedGuiHost.bindContext(worldView(source.server(), level), engine, player == null ? null : player.id()); } if (generatorKey == null || generatorKey.isBlank()) { NoiseExplorerGUI.launch(); @@ -264,8 +267,8 @@ public final class ModdedStudioCommands { return 1; } - private static int map(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + private static int map(NativeCommandSource source) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_STAND_IRIS_STUDIO)); @@ -275,15 +278,15 @@ public final class ModdedStudioCommands { IrisModdedCommands.fail(source, guiUnavailableMessage()); return 0; } - ServerPlayer player = source.getPlayer(); - ModdedGuiHost.bindContext(source.getServer(), level, engine, player == null ? null : player.getUUID()); - VisionGUI.launch(engine, player == null ? null : player.getUUID()); - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_VISION_MAP_ON_SERVER_DISPLAY, MessageArgument.untrusted("value", level.dimension().identifier()))); + NativeProtocolPlayer player = source.player(); + ModdedGuiHost.bindContext(worldView(source.server(), level), engine, player == null ? null : player.id()); + VisionGUI.launch(engine, player == null ? null : player.id()); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_VISION_MAP_ON_SERVER_DISPLAY, MessageArgument.untrusted("value", level.name()))); return 1; } - private static int imageMap(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + private static int imageMap(NativeCommandSource source) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( @@ -295,24 +298,24 @@ public final class ModdedStudioCommands { IrisModdedCommands.fail(source, guiUnavailableMessage()); return 0; } - ServerPlayer player = source.getPlayer(); - ModdedGuiHost.bindContext(source.getServer(), level, engine, player == null ? null : player.getUUID()); + NativeProtocolPlayer player = source.player(); + ModdedGuiHost.bindContext(worldView(source.server(), level), engine, player == null ? null : player.id()); ImageMapStudioGUI.launch(engine); IrisModdedCommands.ok(source, IrisLanguage.plain( ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_IMAGE_MAP_STUDIO_ON_SERVER_DISPLAY, - MessageArgument.untrusted("value", level.dimension().identifier()) + MessageArgument.untrusted("value", level.name()) )); return 1; } - private static LiteralArgumentBuilder workspaceTree(String name, boolean open) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> workspace(context.getSource(), null, open))) - .then(Commands.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> workspace(context.getSource(), StringArgumentType.getString(context, "pack"), open)))); + private static LiteralArgumentBuilder workspaceTree(String name, boolean open) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> workspace(context.getSource(), null, open))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.word()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> workspace(context.getSource(), StringArgumentType.getString(context, "pack"), open)))); } - private static int workspace(CommandSourceStack source, String pack, boolean open) { + private static int workspace(NativeCommandSource source, String pack, boolean open) { File folder = resolvePack(source, pack); if (folder == null) { return 0; @@ -354,23 +357,23 @@ public final class ModdedStudioCommands { return "Server-launched GUIs are disabled (gui.useServerLaunchedGuis=false in Iris settings)."; } - private static LiteralArgumentBuilder message(String name, String text) { - return Commands.literal(name) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + private static LiteralArgumentBuilder message(String name, String text) { + return NativeCommandRegistration.literal(name) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; })) - .then(Commands.argument("args", StringArgumentType.greedyString()) - .executes(ModdedCommandTree.localized((CommandContext context) -> { + .then(NativeCommandRegistration.argument("args", StringArgumentType.greedyString()) + .executes(ModdedCommandTree.localized((CommandContext context) -> { IrisModdedCommands.fail(context.getSource(), text); return 0; }))); } - private static File resolvePack(CommandSourceStack source, String pack) { + private static File resolvePack(NativeCommandSource source, String pack) { String name = pack; if (name == null || name.isBlank()) { - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null || engine.getDimension() == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_SPECIFY_PACK_NAME)); return null; @@ -385,10 +388,10 @@ public final class ModdedStudioCommands { return folder; } - private static String studioDimensionId(ServerPlayer player) { - String base = STUDIO_ID_SANITIZER.matcher(player.getScoreboardName().toLowerCase(Locale.ROOT)).replaceAll("_"); + private static String studioDimensionId(NativeProtocolPlayer player) { + String base = STUDIO_ID_SANITIZER.matcher(player.name().toLowerCase(Locale.ROOT)).replaceAll("_"); if (base.isBlank()) { - base = player.getUUID().toString().replace("-", ""); + base = player.id().toString().replace("-", ""); } return STUDIO_NAMESPACE + ":" + STUDIO_PREFIX + base; } @@ -397,15 +400,15 @@ public final class ModdedStudioCommands { return STUDIO_NAMESPACE + ":" + STUDIO_PREFIX + "console"; } - private static int open(CommandSourceStack source, String pack, long seed) { + private static int open(NativeCommandSource source, String pack, long seed) { if (pack == null || pack.isBlank()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PROVIDE_DIMENSION_PACK_IRIS_STUDIO_OPEN_PACK_SEED_2)); return 0; } - ServerPlayer player = source.getPlayer(); - UUID owner = player == null ? CONSOLE_OWNER : player.getUUID(); + NativeProtocolPlayer player = source.player(); + UUID owner = player == null ? CONSOLE_OWNER : player.id(); String dimensionId = player == null ? studioConsoleDimensionId() : studioDimensionId(player); - MinecraftServer server = source.getServer(); + NativeModdedServer server = source.server(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_OPENING_STUDIO_SEED, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("seed", seed))); CompletableFuture transition = TRANSITIONS.submit( owner, @@ -415,8 +418,8 @@ public final class ModdedStudioCommands { } private static CompletableFuture openTransition( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, UUID owner, String dimensionId, String pack, @@ -443,8 +446,8 @@ public final class ModdedStudioCommands { } private static void prepareStudioOpen( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, UUID owner, String dimensionId, String pack, @@ -478,8 +481,8 @@ public final class ModdedStudioCommands { } private static void executeStudioOpen( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, UUID owner, String dimensionId, String pack, @@ -496,23 +499,18 @@ public final class ModdedStudioCommands { transition.complete(null); return; } - ServerPlayer player = server.getPlayerList().getPlayer(owner); + NativeProtocolPlayer player = server.player(owner); if (player == null) { throw new IllegalStateException("Studio owner disconnected before teleport."); } - ServerLevel studio = handle.level(); + NativeWorld studio = handle.level(); Engine engine = IrisModdedCommands.engineFor(studio); if (engine == null) { throw new IllegalStateException("Studio engine is unavailable for " + dimensionId + "."); } int surfaceY = engine.getMinHeight() + engine.getHeight(8, 8, false) + 2; - CompletableFuture teleport = ModdedDimensionManager.teleportAsync( - player, - server, - studio, - 8.5D, - surfaceY, - 8.5D); + CompletableFuture teleport = NativeWorldTeleport.teleport(player, + new NativeWorldTeleport.Destination(server, studio, 8.5D, surfaceY, 8.5D, 0L)); teleport.whenComplete((success, failure) -> server.execute(() -> { if (transition.isDone()) { return; @@ -541,7 +539,7 @@ public final class ModdedStudioCommands { } } - private static void replaceExistingStudio(MinecraftServer server, UUID owner, String dimensionId) { + private static void replaceExistingStudio(NativeModdedServer server, UUID owner, String dimensionId) { if (ModdedDimensionManager.level(server, dimensionId) != null || ModdedDimensionManager.handle(dimensionId) != null) { ModdedDimensionManager.remove(server, dimensionId, true); @@ -550,7 +548,7 @@ public final class ModdedStudioCommands { } private static void cleanupFailedOpen( - MinecraftServer server, + NativeModdedServer server, UUID owner, String dimensionId, Throwable failure @@ -566,18 +564,18 @@ public final class ModdedStudioCommands { } private static void completeConsoleOpen( - CommandSourceStack source, + NativeCommandSource source, String dimensionId, String pack, long seed, ModdedDimensionManager.Handle handle ) { - ServerLevel studio = handle.level(); + NativeWorld studio = handle.level(); Engine engine = IrisModdedCommands.engineFor(studio); int surface = engine == null - ? studio.getMinY() + 1 + ? studio.minHeight() + 1 : engine.getMinHeight() + engine.getHeight(8, 8, false) + 2; - surface = Math.max(studio.getMinY() + 1, Math.min(studio.getMaxY() - 2, surface)); + surface = Math.max(studio.minHeight() + 1, Math.min(studio.maxHeight() - 2, surface)); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_CONSOLE_STUDIO_OPEN_NOW_RUNS_SEED_TRANSIENT_NOT_WRITTEN_IRIS, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("seed", seed))); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_ENTER_IT_WITH_EXECUTE_RUN_TP_S_8_5_8, MessageArgument.untrusted("dimensionId", dimensionId), MessageArgument.untrusted("surface", surface))); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PREGEN_IT_WITH_IRIS_PREGEN_START_RADIUS, MessageArgument.untrusted("dimensionId", dimensionId))); @@ -585,8 +583,8 @@ public final class ModdedStudioCommands { } private static void reportOpenFailure( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, String dimensionId, String pack, CompletableFuture transition @@ -604,17 +602,17 @@ public final class ModdedStudioCommands { }); } - private static int close(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); - MinecraftServer server = source.getServer(); - UUID owner = player == null ? CONSOLE_OWNER : player.getUUID(); + private static int close(NativeCommandSource source) { + NativeProtocolPlayer player = source.player(); + NativeModdedServer server = source.server(); + UUID owner = player == null ? CONSOLE_OWNER : player.id(); TRANSITIONS.submit(owner, () -> closeTransition(source, server, owner)); return 1; } private static CompletableFuture closeTransition( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, UUID owner ) { CompletableFuture transition = new CompletableFuture<>(); @@ -645,8 +643,8 @@ public final class ModdedStudioCommands { return transition; } - private static int status(CommandSourceStack source) { - MinecraftServer server = source.getServer(); + private static int status(NativeCommandSource source) { + NativeModdedServer server = source.server(); List handles = ModdedDimensionManager.handles(); List studios = new ArrayList<>(); for (ModdedDimensionManager.Handle handle : handles) { @@ -676,22 +674,22 @@ public final class ModdedStudioCommands { return null; } - private static String ownerName(MinecraftServer server, UUID owner) { + private static String ownerName(NativeModdedServer server, UUID owner) { if (owner.equals(CONSOLE_OWNER)) { return "console"; } - ServerPlayer player = server.getPlayerList().getPlayer(owner); - return player == null ? owner.toString() : player.getScoreboardName(); + NativeProtocolPlayer player = server.player(owner); + return player == null ? owner.toString() : player.name(); } - private static int tpStudio(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int tpStudio(NativeCommandSource source) { + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS)); return 0; } - MinecraftServer server = source.getServer(); - UUID owner = player.getUUID(); + NativeModdedServer server = source.server(); + UUID owner = player.id(); CompletableFuture transition = TRANSITIONS.submit( owner, () -> teleportToStudio(source, server, owner)); @@ -700,8 +698,8 @@ public final class ModdedStudioCommands { } private static CompletableFuture teleportToStudio( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, UUID owner ) { CompletableFuture transition = new CompletableFuture<>(); @@ -716,7 +714,7 @@ public final class ModdedStudioCommands { transition.complete(null); return; } - ServerLevel studio = ModdedDimensionManager.level(server, dimensionId); + NativeWorld studio = ModdedDimensionManager.level(server, dimensionId); if (studio == null) { STUDIOS.remove(owner, dimensionId); IrisModdedCommands.fail(source, IrisLanguage.plain( @@ -724,7 +722,7 @@ public final class ModdedStudioCommands { transition.complete(null); return; } - ServerPlayer activePlayer = server.getPlayerList().getPlayer(owner); + NativeProtocolPlayer activePlayer = server.player(owner); Engine engine = IrisModdedCommands.engineFor(studio); if (activePlayer == null || engine == null) { transition.completeExceptionally(new IllegalStateException( @@ -732,13 +730,8 @@ public final class ModdedStudioCommands { return; } int surfaceY = engine.getMinHeight() + engine.getHeight(8, 8, false) + 2; - CompletableFuture teleport = ModdedDimensionManager.teleportAsync( - activePlayer, - server, - studio, - 8.5D, - surfaceY, - 8.5D); + CompletableFuture teleport = NativeWorldTeleport.teleport(activePlayer, + new NativeWorldTeleport.Destination(server, studio, 8.5D, surfaceY, 8.5D, 0L)); teleport.whenComplete((success, failure) -> server.execute(() -> { if (transition.isDone()) { return; @@ -762,8 +755,8 @@ public final class ModdedStudioCommands { } private static void reportTeleportFailure( - CommandSourceStack source, - MinecraftServer server, + NativeCommandSource source, + NativeModdedServer server, CompletableFuture transition ) { transition.whenComplete((ignored, failure) -> { @@ -787,7 +780,7 @@ public final class ModdedStudioCommands { return cause; } - private static int version(CommandSourceStack source, String pack) { + private static int version(NativeCommandSource source, String pack) { File folder = resolvePack(source, pack); if (folder == null) { return 0; @@ -802,7 +795,7 @@ public final class ModdedStudioCommands { return 1; } - private static int create(CommandSourceStack source, String nameRaw, String template) { + private static int create(NativeCommandSource source, String nameRaw, String template) { String name = nameRaw.toLowerCase(Locale.ROOT); if (!PROJECT_NAME.matcher(name).matches()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_INVALID_PROJECT_NAME_ALLOWED_Z_0_9, MessageArgument.untrusted("nameRaw", nameRaw))); @@ -814,7 +807,7 @@ public final class ModdedStudioCommands { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACK_ALREADY_EXISTS_AT, MessageArgument.untrusted("name", name), MessageArgument.untrusted("value", target.getAbsolutePath()))); return 0; } - MinecraftServer server = source.getServer(); + NativeModdedServer server = source.server(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_CREATING_PROJECT_FROM_TEMPLATE, MessageArgument.untrusted("name", name), MessageArgument.untrusted("template", template))); Thread thread = new Thread(() -> { try { @@ -845,12 +838,12 @@ public final class ModdedStudioCommands { return 1; } - private static int pkg(CommandSourceStack source, String pack) { + private static int pkg(NativeCommandSource source, String pack) { File folder = resolvePack(source, pack); if (folder == null) { return 0; } - MinecraftServer server = source.getServer(); + NativeModdedServer server = source.server(); String dimKey = folder.getName(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_PACKAGING_DIMENSION, MessageArgument.untrusted("dimKey", dimKey))); Thread thread = new Thread(() -> { @@ -1058,20 +1051,20 @@ public final class ModdedStudioCommands { } } - private static int regions(CommandSourceStack source, int radius) { - ServerPlayer player = source.getPlayer(); + private static int regions(NativeCommandSource source, int radius) { + NativeProtocolPlayer player = source.player(); if (player == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_SAMPLING_IS)); return 0; } - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS)); return 0; } - MinecraftServer server = source.getServer(); - int blockX = player.blockPosition().getX(); - int blockZ = player.blockPosition().getZ(); + NativeModdedServer server = source.server(); + int blockX = player.blockX(); + int blockZ = player.blockZ(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_STUDIO_COMMANDS_SAMPLING_REGION_DISTRIBUTION_X_CHUNKS_AROUND_YOU, MessageArgument.untrusted("value", (radius * 2)), MessageArgument.untrusted("value2", (radius * 2)))); Thread thread = new Thread(() -> { try { @@ -1106,4 +1099,8 @@ public final class ModdedStudioCommands { thread.start(); return 1; } + private static WorldView worldView(NativeModdedServer server, NativeWorld level) { + return new NativeWorldView(server, level); + } + } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedUnregisteredStructures.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedUnregisteredStructures.java index 807f42443..21db92e46 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedUnregisteredStructures.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedUnregisteredStructures.java @@ -18,6 +18,8 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureQueries; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.IrisStructureLocator; @@ -25,13 +27,7 @@ import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; import art.arcane.iris.structure.placement.StructureReachability; import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.levelgen.structure.Structure; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; import java.util.Collection; import java.util.HashSet; @@ -47,8 +43,8 @@ final class ModdedUnregisteredStructures { private ModdedUnregisteredStructures() { } - static int print(CommandSourceStack source) { - ServerLevel level = source.getLevel(); + static int print(NativeCommandSource source) { + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, @@ -57,7 +53,7 @@ final class ModdedUnregisteredStructures { } try { List excluded = collect(source, level, engine); - String dimension = level.dimension().identifier().toString(); + String dimension = level.name(); long unregistered = excluded.stream() .filter((ExcludedStructure entry) -> entry.status() == ReportStatus.UNREGISTERED) .count(); @@ -81,26 +77,26 @@ final class ModdedUnregisteredStructures { return 1; } catch (Throwable error) { ModdedIrisLog.error("Iris failed to build the excluded structure report for {}", - level.dimension().identifier(), error); + level.name(), error); IrisModdedCommands.fail(source, "Iris could not build the excluded structure report; see the server console."); return 0; } } - static List collect(CommandSourceStack source, ServerLevel level, Engine engine) { + static List collect(NativeCommandSource source, NativeWorld level, Engine engine) { boolean nativeGenerationEnabled = - source.getServer().getWorldGenSettings().options().generateStructures(); + source.server().generateStructures(); Set reachableNativeKeys = StructureReachability.reachableKeys(engine); - Registry registry = source.getServer().registryAccess().lookupOrThrow(Registries.STRUCTURE); + NativeStructureQueries registry = new NativeStructureQueries(level); Map excluded = new TreeMap<>(); - Set registeredKeys = new HashSet<>(registry.keySet().size()); - Set eligibleRegisteredKeys = new HashSet<>(registry.keySet().size()); - for (Identifier identifier : registry.keySet()) { + Set registeredKeys = new HashSet<>(registry.keys().size()); + Set eligibleRegisteredKeys = new HashSet<>(registry.keys().size()); + for (String identifier : registry.keys()) { String key = identifier.toString(); String normalizedKey = ModdedCommandSuggestions.normalizeKey(key); registeredKeys.add(normalizedKey); - Optional> holder = registry.get(identifier); + Optional holder = registry.resolve(identifier); if (holder.isEmpty()) { excluded.put(normalizedKey, new ExcludedStructure(ReportStatus.EXCLUDED, key, "The live structure registry contains this key but has no bound structure holder.")); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedVisionOverlay.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedVisionOverlay.java index 078d90f00..8b8af635f 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedVisionOverlay.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedVisionOverlay.java @@ -19,101 +19,63 @@ package art.arcane.iris.modded.command; import art.arcane.iris.studio.view.GuiHost; -import art.arcane.iris.studio.view.GuiMarker; +import art.arcane.volmlib.nativelib.view.WorldMarker; import art.arcane.iris.studio.view.GuiOverlay; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.studio.render.RenderType; -import art.arcane.iris.modded.ModdedDimensionManager; import art.arcane.iris.modded.ModdedIrisLog; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.phys.Vec3; import java.awt.Desktop; import java.io.File; -import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import art.arcane.volmlib.nativelib.view.WorldView; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; public final class ModdedVisionOverlay implements GuiOverlay { - private final MinecraftServer server; - private final ServerLevel level; + private final WorldView world; private final Engine engine; private final UUID opener; - public ModdedVisionOverlay(MinecraftServer server, ServerLevel level, Engine engine, UUID opener) { - this.server = server; - this.level = level; - this.engine = engine; - this.opener = opener; + public ModdedVisionOverlay(Context context) { + world = context.world(); + engine = context.engine(); + opener = context.opener(); } @Override - public List players() { - List markers = new ArrayList<>(); - for (ServerPlayer player : level.players()) { - Vec3 position = player.position(); - markers.add(GuiMarker.player(player.getScoreboardName(), position.x(), position.z())); - } - return markers; + public List players() { + return world.players(); } @Override - public void requestEntities(Consumer> sink) { - server.execute(() -> { - List markers = new ArrayList<>(); - for (Entity entity : level.getAllEntities()) { - if (entity instanceof ServerPlayer || !(entity instanceof LivingEntity living)) { - continue; - } - Vec3 position = living.position(); - markers.add(GuiMarker.entity(living.getType().toShortString(), position.x(), position.y(), position.z(), - living.getHealth(), living.getMaxHealth())); - } - sink.accept(markers); - }); + public void requestEntities(Consumer> sink) { + world.requestEntities(sink); } @Override public void teleport(double worldX, double worldZ) { int blockX = (int) Math.floor(worldX); int blockZ = (int) Math.floor(worldZ); - server.execute(() -> { - ServerPlayer player = opener == null ? null : server.getPlayerList().getPlayer(opener); - if (player == null) { - if (opener != null) { - return; - } - List players = level.players(); - if (players.isEmpty()) { - return; - } - player = players.get(0); - } + world.execute(() -> { int surfaceY = engine.getMinHeight() + engine.getHeight(blockX, blockZ, false) + 2; - CompletableFuture teleport = ModdedDimensionManager.teleportAsync( - player, - server, - level, - blockX + 0.5D, - surfaceY, - blockZ + 0.5D, - System.nanoTime() + TimeUnit.SECONDS.toNanos(10L)); - UUID playerId = player.getUUID(); - teleport.whenComplete((success, failure) -> { + Optional operation = world.teleport(opener, + new WorldView.Destination(blockX + 0.5D, surfaceY, blockZ + 0.5D, + System.nanoTime() + TimeUnit.SECONDS.toNanos(10L))); + if (operation.isEmpty()) { + return; + } + WorldView.TeleportOperation teleport = operation.get(); + teleport.result().whenComplete((success, failure) -> { if (failure != null) { ModdedIrisLog.error("Iris Vision teleport failed for {} at {},{}", - playerId, blockX, blockZ, failure); + teleport.playerId(), blockX, blockZ, failure); } else if (!Boolean.TRUE.equals(success)) { ModdedIrisLog.warn("Iris Vision teleport did not complete for {} at {},{}", - playerId, blockX, blockZ); + teleport.playerId(), blockX, blockZ); } }); }); @@ -136,4 +98,7 @@ public final class ModdedVisionOverlay implements GuiOverlay { }; return file == null ? null : file.getName(); } + public record Context(WorldView world, Engine engine, UUID opener) { + } + } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWandService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWandService.java index 15f6b8823..99ef13591 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWandService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWandService.java @@ -18,31 +18,19 @@ package art.arcane.iris.modded.command; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditInteraction; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTaggedItems; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.volmlib.util.localization.MessageArgument; -import net.minecraft.ChatFormatting; -import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponents; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.util.Unit; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraft.world.item.component.CustomData; -import net.minecraft.world.item.component.ItemLore; -import net.minecraft.world.item.component.TooltipDisplay; -import net.minecraft.world.level.Level; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText.Format; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.awt.Color; import java.util.List; @@ -64,128 +52,87 @@ public final class ModdedWandService { private ModdedWandService() { } - public record Selection(ResourceKey dimension, BlockPos first, BlockPos second) { + public record Selection(String dimension, NativeBlockPoint first, NativeBlockPoint second) { public boolean complete() { return first != null && second != null; } - public BlockPos min() { - return new BlockPos(Math.min(first.getX(), second.getX()), Math.min(first.getY(), second.getY()), Math.min(first.getZ(), second.getZ())); + public NativeBlockPoint min() { + return new NativeBlockPoint(Math.min(first.x(), second.x()), Math.min(first.y(), second.y()), Math.min(first.z(), second.z())); } - public BlockPos max() { - return new BlockPos(Math.max(first.getX(), second.getX()), Math.max(first.getY(), second.getY()), Math.max(first.getZ(), second.getZ())); + public NativeBlockPoint max() { + return new NativeBlockPoint(Math.max(first.x(), second.x()), Math.max(first.y(), second.y()), Math.max(first.z(), second.z())); } } - public static ItemStack createWand() { - ItemStack stack = new ItemStack(Items.BLAZE_ROD); - stack.set(DataComponents.CUSTOM_NAME, Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_NAME)).withStyle(ChatFormatting.BOLD, ChatFormatting.GOLD)); - stack.set(DataComponents.LORE, new ItemLore(List.of( - Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_FIRST)), - Component.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_SECOND))))); - stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE); - stack.set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, Boolean.TRUE); - stack.set(DataComponents.CUSTOM_DATA, CustomData.of(flagTag(WAND_TAG))); - stack.set(DataComponents.TOOLTIP_DISPLAY, - TooltipDisplay.DEFAULT.withHidden(DataComponents.UNBREAKABLE, true)); - return stack; + public static NativeItemStack createWand() { + return NativeTaggedItems.create(new NativeTaggedItems.Options("minecraft:blaze_rod", + NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_NAME)).withStyle(NativeCommandText.Format.BOLD, NativeCommandText.Format.GOLD), + List.of(NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_FIRST)), + NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.WAND_LORE_SECOND))), + WAND_TAG, true, true)); } - public static ItemStack createDust() { - ItemStack stack = new ItemStack(Items.GLOWSTONE_DUST); - stack.set(DataComponents.CUSTOM_NAME, Component.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_NAME)).withStyle(ChatFormatting.BOLD, ChatFormatting.YELLOW)); - stack.set(DataComponents.LORE, new ItemLore(List.of( - Component.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_LORE))))); - stack.set(DataComponents.UNBREAKABLE, Unit.INSTANCE); - stack.set(DataComponents.ENCHANTMENT_GLINT_OVERRIDE, Boolean.TRUE); - stack.set(DataComponents.CUSTOM_DATA, CustomData.of(flagTag(DUST_TAG))); - stack.set(DataComponents.TOOLTIP_DISPLAY, - TooltipDisplay.DEFAULT.withHidden(DataComponents.UNBREAKABLE, true)); - return stack; + public static NativeItemStack createDust() { + return NativeTaggedItems.create(new NativeTaggedItems.Options("minecraft:glowstone_dust", + NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_NAME)).withStyle(NativeCommandText.Format.BOLD, NativeCommandText.Format.YELLOW), + List.of(NativeCommandText.literal(IrisLanguage.plain(RuntimeUiMessages.DUST_LORE))), + DUST_TAG, true, true)); } - private static CompoundTag flagTag(String key) { - CompoundTag tag = new CompoundTag(); - tag.putBoolean(key, true); - return tag; + public static boolean isHoldingWand(NativeEditPlayer player) { + return player.holdingFlaggedItem(WAND_TAG); } - public static boolean isWand(ItemStack stack) { - return hasFlag(stack, WAND_TAG); - } - - public static boolean isDust(ItemStack stack) { - return hasFlag(stack, DUST_TAG); - } - - private static boolean hasFlag(ItemStack stack, String key) { - if (stack == null || stack.isEmpty()) { - return false; - } - CustomData data = stack.get(DataComponents.CUSTOM_DATA); - return data != null && data.copyTag().getBooleanOr(key, false); - } - - public static boolean isHoldingWand(ServerPlayer player) { - return isWand(player.getMainHandItem()); - } - - public static boolean attackBlock(Player player, Level level, InteractionHand hand, BlockPos pos) { - if (hand != InteractionHand.MAIN_HAND || level.isClientSide() || !(player instanceof ServerPlayer serverPlayer) || !(level instanceof ServerLevel serverLevel)) { - return false; - } - if (isWand(serverPlayer.getMainHandItem())) { - setCorner(serverPlayer, serverLevel, pos, true); + public static boolean attackBlock(NativeEditInteraction interaction) { + if (isHoldingWand(interaction.player())) { + setCorner(interaction.player(), interaction.world(), interaction.position(), true); return true; } return false; } - public static boolean useBlock(Player player, Level level, InteractionHand hand, BlockPos pos) { - if (hand != InteractionHand.MAIN_HAND || level.isClientSide() || !(player instanceof ServerPlayer serverPlayer) || !(level instanceof ServerLevel serverLevel)) { - return false; - } - ItemStack held = serverPlayer.getMainHandItem(); - if (isWand(held)) { - setCorner(serverPlayer, serverLevel, pos, false); + public static boolean useBlock(NativeEditInteraction interaction) { + if (isHoldingWand(interaction.player())) { + setCorner(interaction.player(), interaction.world(), interaction.position(), false); return true; } - if (isDust(held)) { - serverLevel.playSound(null, pos, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 2.0F, 1.97F); - ModdedDustRevealer.reveal(serverPlayer, serverLevel, pos); + if (interaction.player().holdingFlaggedItem(DUST_TAG)) { + interaction.world().sound(interaction.position(), "minecraft:block.amethyst_block.chime", new NativeEditWorld.SoundOptions(2.0F, 1.97F)); + ModdedDustRevealer.reveal(interaction.player(), interaction.world(), interaction.position()); return true; } return false; } - private static void setCorner(ServerPlayer player, ServerLevel level, BlockPos pos, boolean first) { - ResourceKey dimension = level.dimension(); - BlockPos corner = pos.immutable(); - SELECTIONS.compute(player.getUUID(), (UUID uuid, Selection existing) -> { - BlockPos other = existing != null && existing.dimension().equals(dimension) ? (first ? existing.second() : existing.first()) : null; + private static void setCorner(NativeEditPlayer player, NativeEditWorld level, NativeBlockPoint pos, boolean first) { + String dimension = level.key(); + NativeBlockPoint corner = pos; + SELECTIONS.compute(player.id(), (UUID uuid, Selection existing) -> { + NativeBlockPoint other = existing != null && existing.dimension().equals(dimension) ? (first ? existing.second() : existing.first()) : null; return first ? new Selection(dimension, corner, other) : new Selection(dimension, other, corner); }); - level.playSound(null, pos, SoundEvents.END_PORTAL_FRAME_FILL, SoundSource.PLAYERS, 1.0F, first ? 0.67F : 1.17F); - player.sendOverlayMessage(Component.literal(IrisLanguage.plain( + level.sound(pos, "minecraft:block.end_portal_frame.fill", new NativeEditWorld.SoundOptions(1.0F, first ? 0.67F : 1.17F)); + player.sendOverlayMessage(NativeCommandText.literal(IrisLanguage.plain( RuntimeUiMessages.WAND_POSITION_SET, MessageArgument.trusted("position", first ? 1 : 2), - MessageArgument.trusted("x", corner.getX()), - MessageArgument.trusted("y", corner.getY()), - MessageArgument.trusted("z", corner.getZ()) + MessageArgument.trusted("x", corner.x()), + MessageArgument.trusted("y", corner.y()), + MessageArgument.trusted("z", corner.z()) ))); } - public static Selection selection(ServerPlayer player) { - Selection selection = SELECTIONS.get(player.getUUID()); - if (selection == null || !selection.complete() || !selection.dimension().equals(player.level().dimension())) { + public static Selection selection(NativeEditPlayer player) { + Selection selection = SELECTIONS.get(player.id()); + if (selection == null || !selection.complete() || !selection.dimension().equals(player.world().key())) { return null; } return selection; } - public static void setSelection(ServerPlayer player, BlockPos first, BlockPos second) { - SELECTIONS.put(player.getUUID(), new Selection(player.level().dimension(), first.immutable(), second.immutable())); + public static void setSelection(NativeEditPlayer player, NativeBlockPoint first, NativeBlockPoint second) { + SELECTIONS.put(player.id(), new Selection(player.world().key(), first, second)); } public static void clearAll() { @@ -194,36 +141,37 @@ public final class ModdedWandService { ModdedWhatCommands.clear(); } - public static void serverTick(MinecraftServer server) { + public static void serverTick(NativeModdedServer server) { tickCounter++; if (tickCounter % DRAW_INTERVAL_TICKS != 0) { return; } try { - for (ServerPlayer player : server.getPlayerList().getPlayers()) { - if (!isHoldingWand(player)) { - continue; - } - Selection selection = selection(player); - if (selection == null) { - continue; - } - draw(player.level(), player, selection); - } + server.forEachPlayer(context -> drawSelection(context.editing())); } catch (Throwable e) { ModdedIrisLog.error("Iris wand selection draw failed", e); } } - private static void draw(ServerLevel level, ServerPlayer player, Selection selection) { - BlockPos min = selection.min(); - BlockPos max = selection.max(); - double lowX = min.getX(); - double lowY = min.getY(); - double lowZ = min.getZ(); - double highX = max.getX() + 1; - double highY = max.getY() + 1; - double highZ = max.getZ() + 1; + private static void drawSelection(NativeEditPlayer player) { + if (!isHoldingWand(player)) { + return; + } + Selection selection = selection(player); + if (selection != null) { + draw(player, selection); + } + } + + private static void draw(NativeEditPlayer player, Selection selection) { + NativeBlockPoint min = selection.min(); + NativeBlockPoint max = selection.max(); + double lowX = min.x(); + double lowY = min.y(); + double lowZ = min.z(); + double highX = max.x() + 1; + double highY = max.y() + 1; + double highZ = max.z() + 1; double[][] edges = { {lowX, lowY, lowZ, highX, lowY, lowZ}, {lowX, lowY, lowZ, lowX, highY, lowZ}, @@ -240,9 +188,9 @@ public final class ModdedWandService { }; ThreadLocalRandom random = ThreadLocalRandom.current(); - double px = player.getX(); - double py = player.getY(); - double pz = player.getZ(); + double px = player.x(); + double py = player.y(); + double pz = player.z(); int sent = 0; for (double[] edge : edges) { double dx = edge[3] - edge[0]; @@ -268,10 +216,9 @@ public final class ModdedWandService { if (distX * distX + distY * distY + distZ * distZ > DRAW_DISTANCE_SQUARED) { continue; } - float hue = (float) (0.5F + (Math.sin((x + y + z + (player.tickCount / 2.0F)) / 20.0F) / 2.0D)); + float hue = (float) (0.5F + (Math.sin((x + y + z + (player.ticks() / 2.0F)) / 20.0F) / 2.0D)); Color color = Color.getHSBColor(hue, 1.0F, 1.0F); - DustParticleOptions options = new DustParticleOptions(color.getRGB() & 0xFFFFFF, 0.9F); - level.sendParticles(player, options, true, true, x, y, z, 1, 0.0D, 0.0D, 0.0D, 0.0D); + player.coloredDust(color.getRGB() & 0xFFFFFF, 0.9F, x, y, z); sent++; if (sent >= DRAW_POINT_CAP) { return; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWhatCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWhatCommands.java index 15ab7d0b7..598d2e465 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWhatCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWhatCommands.java @@ -30,10 +30,14 @@ import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldInspection; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedScheduler; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.IrisContext; import art.arcane.volmlib.util.localization.MessageArgument; import art.arcane.volmlib.util.localization.TextKey; @@ -43,31 +47,9 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.particles.DustParticleOptions; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceKey; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.RandomizableContainer; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.SpawnerBlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.storage.loot.LootTable; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.HitResult; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; import java.util.ArrayList; import java.util.List; @@ -77,13 +59,14 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Predicate; public final class ModdedWhatCommands { - private static final Predicate GATE = - Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); - private static final SuggestionProvider MARKER_TYPES = - (CommandContext context, SuggestionsBuilder builder) -> - SharedSuggestionProvider.suggest( + private static final Predicate GATE = + NativeCommandRegistration.GAMEMASTERS; + private static final SuggestionProvider MARKER_TYPES = + (CommandContext context, SuggestionsBuilder builder) -> + NativeCommandRegistration.suggest( List.of("cave_floor", "cave_ceiling", "object"), builder); - private static final DustParticleOptions MARKER_DUST = new DustParticleOptions(0x5A8CFF, 1.2F); + private static final NativeEditPlayer.Dust MARKER_DUST = new NativeEditPlayer.Dust(0x5A8CFF, 1.2F); + private static final NativeEditPlayer.ParticleSpread MARKER_SPREAD = new NativeEditPlayer.ParticleSpread(3, 0.2D, 0.2D, 0.2D, 0.0D); private static final int MAX_MARKERS = 8_192; private static final int MARKER_BATCH_SIZE = 128; private static final ConcurrentHashMap ACTIVE_MARKER_RUNS = new ConcurrentHashMap<>(); @@ -91,29 +74,29 @@ public final class ModdedWhatCommands { private ModdedWhatCommands() { } - public static LiteralArgumentBuilder tree() { - return Commands.literal("what").requires(GATE) - .executes(ModdedCommandTree.localized((CommandContext context) -> + public static LiteralArgumentBuilder tree() { + return NativeCommandRegistration.literal("what").requires(GATE) + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectHere(context.getSource()))) - .then(Commands.literal("here") - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.literal("here") + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectHere(context.getSource())))) - .then(Commands.literal("biome") - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.literal("biome") + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectBiome(context.getSource())))) - .then(Commands.literal("region") - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.literal("region") + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectRegion(context.getSource())))) - .then(Commands.literal("block") - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.literal("block") + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectBlock(context.getSource())))) - .then(Commands.literal("hand") - .executes(ModdedCommandTree.localized((CommandContext context) -> + .then(NativeCommandRegistration.literal("hand") + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectHand(context.getSource())))) - .then(Commands.literal("markers") - .then(Commands.argument("marker", StringArgumentType.greedyString()) + .then(NativeCommandRegistration.literal("markers") + .then(NativeCommandRegistration.argument("marker", StringArgumentType.greedyString()) .suggests(MARKER_TYPES) - .executes(ModdedCommandTree.localized((CommandContext context) -> + .executes(ModdedCommandTree.localized((CommandContext context) -> inspectMarkers( context.getSource(), StringArgumentType.getString(context, "marker")))))); @@ -126,24 +109,24 @@ public final class ModdedWhatCommands { ACTIVE_MARKER_RUNS.clear(); } - private static int inspectHere(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int inspectHere(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2); } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_6)); return 0; } - BlockPos pos = player.blockPosition(); + NativeBlockPoint pos = player.blockPosition(); int result = inspectBiome(source); result &= inspectRegion(source); - int relativeY = pos.getY() - engine.getMinHeight(); + int relativeY = pos.y() - engine.getMinHeight(); try { - IrisBiome cave = engine.getCaveOrMantleBiome(pos.getX(), relativeY, pos.getZ()); + IrisBiome cave = engine.getCaveOrMantleBiome(pos.x(), relativeY, pos.z()); IrisModdedCommands.ok(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_CAVE_BIOME, MessageArgument.untrusted("value", cave == null @@ -154,23 +137,23 @@ public final class ModdedWhatCommands { ModdedCommandMessages.IRIS_MODDED_COMMANDS_CAVE_BIOME_LOOKUP_FAILED); result = 0; } - int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, pos.getX(), pos.getZ()) - 1; - BlockState surface = level.getBlockState(new BlockPos(pos.getX(), surfaceY, pos.getZ())); + int surfaceY = new NativeWorldInspection(level).surfaceY(pos.x(), pos.z()); + NativeBlockState surface = level.getBlock(pos.x(), surfaceY, pos.z()); IrisModdedCommands.ok(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_SURFACE_BLOCK_Y, - MessageArgument.untrusted("value", BuiltInRegistries.BLOCK.getKey(surface.getBlock())), + MessageArgument.untrusted("value", surface.materialKey()), MessageArgument.trusted("value2", surfaceY))); sendPosition(source, pos); return result; } - private static int inspectBiome(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int inspectBiome(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2); } - ServerLevel level = source.getLevel(); - BlockPos pos = player.blockPosition(); + NativeWorld level = source.world(); + NativeBlockPoint pos = player.blockPosition(); NativeBiome nativeBiome = nativeBiome(level, pos); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { @@ -182,7 +165,7 @@ public final class ModdedWhatCommands { } try { IrisBiome biome = engine.getBiome( - pos.getX(), pos.getY() - engine.getMinHeight(), pos.getZ()); + pos.x(), pos.y() - engine.getMinHeight(), pos.z()); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_IRIS_BIOME, MessageArgument.untrusted("biome", biome.getLoadKey()), @@ -198,7 +181,7 @@ public final class ModdedWhatCommands { } catch (SavedBiomeUnavailableException error) { if (error.getCause() != null) { ModdedIrisLog.error("Iris saved biome lookup failed in {}", - source.getLevel().dimension().identifier(), error); + source.world().name(), error); } IrisModdedCommands.fail(source, error.getMessage()); return 0; @@ -209,23 +192,23 @@ public final class ModdedWhatCommands { } } - private static int inspectRegion(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int inspectRegion(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_2); } - Engine engine = IrisModdedCommands.engineFor(source.getLevel()); + Engine engine = IrisModdedCommands.engineFor(source.world()); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_DIMENSION_IS_NOT_GENERATED_BY_IRIS_6)); return 0; } - BlockPos pos = player.blockPosition(); - int centerX = (pos.getX() & ~15) + 8; - int centerZ = (pos.getZ() & ~15) + 8; + NativeBlockPoint pos = player.blockPosition(); + int centerX = (pos.x() & ~15) + 8; + int centerZ = (pos.z() & ~15) + 8; try { IrisRegion region = engine.getRegion( - centerX, pos.getY() - engine.getMinHeight(), centerZ); + centerX, pos.y() - engine.getMinHeight(), centerZ); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_IRIS_REGION, MessageArgument.untrusted("region", region.getLoadKey()), @@ -234,7 +217,7 @@ public final class ModdedWhatCommands { } catch (SavedBiomeUnavailableException error) { if (error.getCause() != null) { ModdedIrisLog.error("Iris saved biome lookup failed in {}", - source.getLevel().dimension().identifier(), error); + source.world().name(), error); } IrisModdedCommands.fail(source, error.getMessage()); return 0; @@ -245,53 +228,51 @@ public final class ModdedWhatCommands { } } - private static int inspectHand(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int inspectHand(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_IT_INSPECTS); } - ItemStack stack = player.getMainHandItem(); - if (stack.isEmpty()) { + NativeWorldInspection.HeldItem stack = NativeWorldInspection.heldItem(source.player()); + if (stack == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_YOUR_MAIN_HAND_IS_EMPTY)); return 0; } IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_MATERIAL, - MessageArgument.untrusted("material", BuiltInRegistries.ITEM.getKey(stack.getItem())))); - if (stack.getItem() instanceof BlockItem blockItem) { + MessageArgument.untrusted("material", stack.key()))); + if (stack.blockState() != null) { IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_FULL_STATE, MessageArgument.untrusted( - "state", ModdedBlockState.serialize(blockItem.getBlock().defaultBlockState())))); + "state", stack.blockState()))); } IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_ITEM_COUNT, - MessageArgument.trusted("count", stack.getCount()))); + MessageArgument.trusted("count", stack.count()))); return 1; } - private static int inspectBlock(CommandSourceStack source) { - ServerPlayer player = source.getPlayer(); + private static int inspectBlock(NativeCommandSource source) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_IT_INSPECTS_2); } - HitResult hit = player.pick(128.0D, 1.0F, false); - if (!(hit instanceof BlockHitResult blockHit) || hit.getType() != HitResult.Type.BLOCK) { + NativeBlockPoint pos = player.pickBlock(128.0D); + if (pos == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( ModdedCommandMessages.IRIS_MODDED_COMMANDS_LOOK_AT_BLOCK_NOT_SKY)); return 0; } - ServerLevel level = source.getLevel(); - BlockPos pos = blockHit.getBlockPos(); - BlockState state = level.getBlockState(pos); - PlatformBlockState platform = ModdedBlockState.of(state, null); + NativeWorld level = source.world(); + NativeBlockState platform = level.getBlock(pos.x(), pos.y(), pos.z()); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_MATERIAL, - MessageArgument.untrusted("material", BuiltInRegistries.BLOCK.getKey(state.getBlock())))); + MessageArgument.untrusted("material", platform.materialKey()))); IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_FULL_STATE, - MessageArgument.untrusted("state", ModdedBlockState.serialize(state)))); + MessageArgument.untrusted("state", platform.key()))); sendPosition(source, pos); sendProperties(source, platform); sendObject(source, level, pos); @@ -299,17 +280,17 @@ public final class ModdedWhatCommands { return 1; } - private static void sendPosition(CommandSourceStack source, BlockPos pos) { + private static void sendPosition(NativeCommandSource source, NativeBlockPoint pos) { IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_POSITION, - MessageArgument.trusted("x", pos.getX()), - MessageArgument.trusted("y", pos.getY()), - MessageArgument.trusted("z", pos.getZ()), - MessageArgument.trusted("chunkX", pos.getX() >> 4), - MessageArgument.trusted("chunkZ", pos.getZ() >> 4))); + MessageArgument.trusted("x", pos.x()), + MessageArgument.trusted("y", pos.y()), + MessageArgument.trusted("z", pos.z()), + MessageArgument.trusted("chunkX", pos.x() >> 4), + MessageArgument.trusted("chunkZ", pos.z() >> 4))); } - private static void sendProperties(CommandSourceStack source, PlatformBlockState platform) { + private static void sendProperties(NativeCommandSource source, NativeBlockState platform) { List flags = propertyNames(platform); if (flags.isEmpty()) { IrisModdedCommands.ok(source, IrisLanguage.plain(IrisMessages.MODDED_PROPERTIES_NONE)); @@ -320,7 +301,7 @@ public final class ModdedWhatCommands { MessageArgument.untrusted("properties", String.join(", ", flags)))); } - static List propertyNames(PlatformBlockState platform) { + static List propertyNames(NativeBlockState platform) { List flags = new ArrayList<>(); addFlag(flags, platform.isSolid(), RuntimeUiMessages.WHAT_FLAG_SOLID); addFlag(flags, platform.isFluid(), RuntimeUiMessages.WHAT_FLAG_FLUID); @@ -343,14 +324,14 @@ public final class ModdedWhatCommands { } } - private static void sendObject(CommandSourceStack source, ServerLevel level, BlockPos pos) { + private static void sendObject(NativeCommandSource source, NativeWorld level, NativeBlockPoint pos) { Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { return; } try { String object = engine.getObjectPlacementKey( - pos.getX(), pos.getY() - engine.getMinHeight(), pos.getZ()); + pos.x(), pos.y() - engine.getMinHeight(), pos.z()); if (object != null) { IrisModdedCommands.ok(source, IrisLanguage.plain( RuntimeUiMessages.WHAT_OBJECT, @@ -358,47 +339,34 @@ public final class ModdedWhatCommands { } } catch (Throwable error) { ModdedIrisLog.error("Iris object lookup failed for /iris what block at {}, {}, {}", - pos.getX(), pos.getY(), pos.getZ(), error); + pos.x(), pos.y(), pos.z(), error); } } - private static void sendBlockEntity(CommandSourceStack source, - ServerLevel level, BlockPos pos) { - BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity == null) { + private static void sendBlockEntity(NativeCommandSource source, + NativeWorld level, NativeBlockPoint pos) { + NativeWorldInspection.BlockEntityDetails details = new NativeWorldInspection(level).blockEntity(pos); + if (details == null) { return; } IrisModdedCommands.ok(source, IrisLanguage.plain( - RuntimeUiMessages.WHAT_BLOCK_ENTITY, - MessageArgument.untrusted( - "type", BuiltInRegistries.BLOCK_ENTITY_TYPE.getKey(blockEntity.getType())))); - if (blockEntity instanceof RandomizableContainer container) { - ResourceKey lootTable = container.getLootTable(); - if (lootTable != null) { - IrisModdedCommands.ok(source, IrisLanguage.plain( - RuntimeUiMessages.WHAT_LOOT_TABLE, - MessageArgument.untrusted("loot", lootTable.identifier()))); - } + RuntimeUiMessages.WHAT_BLOCK_ENTITY, MessageArgument.untrusted("type", details.type()))); + if (details.lootTable() != null) { + IrisModdedCommands.ok(source, IrisLanguage.plain( + RuntimeUiMessages.WHAT_LOOT_TABLE, MessageArgument.untrusted("loot", details.lootTable()))); } - if (blockEntity instanceof SpawnerBlockEntity) { - CompoundTag tag = blockEntity.saveWithoutMetadata(level.registryAccess()); - CompoundTag spawnData = tag.getCompound("SpawnData").orElse(null); - CompoundTag entity = spawnData == null ? null : spawnData.getCompound("entity").orElse(null); - String entityId = entity == null ? "" : entity.getStringOr("id", ""); - if (!entityId.isBlank()) { - IrisModdedCommands.ok(source, IrisLanguage.plain( - RuntimeUiMessages.WHAT_SPAWNER_ENTITY, - MessageArgument.untrusted("entity", entityId))); - } + if (details.spawnedEntity() != null) { + IrisModdedCommands.ok(source, IrisLanguage.plain( + RuntimeUiMessages.WHAT_SPAWNER_ENTITY, MessageArgument.untrusted("entity", details.spawnedEntity()))); } } - private static int inspectMarkers(CommandSourceStack source, String markerRaw) { - ServerPlayer player = source.getPlayer(); + private static int inspectMarkers(NativeCommandSource source, String markerRaw) { + NativeEditPlayer player = source.editingPlayer(); if (player == null) { return playerRequired(source, ModdedCommandMessages.IRIS_MODDED_COMMANDS_THIS_COMMAND_CAN_ONLY_BE_USED_BY_PLAYERS_MARKERS_RENDER); } - ServerLevel level = source.getLevel(); + NativeWorld level = source.world(); Engine engine = IrisModdedCommands.engineFor(level); if (engine == null) { IrisModdedCommands.fail(source, IrisLanguage.plain( @@ -413,11 +381,11 @@ public final class ModdedWhatCommands { return 0; } String marker = markerRaw.trim(); - BlockPos origin = player.blockPosition(); + NativeBlockPoint origin = player.blockPosition(); MarkerRun run = new MarkerRun( - player.getUUID(), player, level, engine, marker, - origin.getX() >> 4, origin.getZ() >> 4, new AtomicBoolean()); - MarkerRun previous = ACTIVE_MARKER_RUNS.put(player.getUUID(), run); + player.id(), player, level, engine, marker, + origin.x() >> 4, origin.z() >> 4, new AtomicBoolean()); + MarkerRun previous = ACTIVE_MARKER_RUNS.put(player.id(), run); if (previous != null) { previous.cancelled().set(true); } @@ -428,9 +396,9 @@ public final class ModdedWhatCommands { return 1; } - private static void scanMarkers(CommandSourceStack source, + private static void scanMarkers(NativeCommandSource source, ModdedScheduler scheduler, MarkerRun run) { - List hits = new ArrayList<>(); + List hits = new ArrayList<>(); MatterMarker marker = new MatterMarker(run.marker()); try (GenerationSessionLease lease = run.engine().acquireGenerationLease("modded_what_markers"); IrisContext.Scope ignored = IrisContext.open(run.engine(), lease.sessionId(), null)) { @@ -440,7 +408,7 @@ public final class ModdedWhatCommands { return; } for (IrisPosition position : run.engine().getMantle().findMarkers(chunkX, chunkZ, marker)) { - hits.add(new BlockPos(position.getX(), position.getY(), position.getZ())); + hits.add(new NativeBlockPoint(position.getX(), position.getY(), position.getZ())); if (hits.size() >= MAX_MARKERS) { break; } @@ -461,9 +429,9 @@ public final class ModdedWhatCommands { } } - private static void renderMarkerBatch(CommandSourceStack source, + private static void renderMarkerBatch(NativeCommandSource source, ModdedScheduler scheduler, MarkerRun run, - List hits, int from) { + List hits, int from) { if (!active(run)) { // Drop the registry entry on abort too, or the run record pins the player, level // and engine until server stop. No-op if a newer run already replaced it. @@ -472,10 +440,8 @@ public final class ModdedWhatCommands { } int to = Math.min(hits.size(), from + MARKER_BATCH_SIZE); for (int index = from; index < to; index++) { - BlockPos hit = hits.get(index); - run.level().sendParticles(run.player(), MARKER_DUST, true, true, - hit.getX() + 0.5D, hit.getY() + 1.0D, hit.getZ() + 0.5D, - 3, 0.2D, 0.2D, 0.2D, 0.0D); + NativeBlockPoint hit = hits.get(index); + run.player().dust(MARKER_DUST, hit.x() + 0.5D, hit.y() + 1.0D, hit.z() + 0.5D, MARKER_SPREAD); } if (to < hits.size()) { scheduler.laterGlobal(() -> renderMarkerBatch(source, scheduler, run, hits, to), 1); @@ -491,14 +457,12 @@ public final class ModdedWhatCommands { private static boolean active(MarkerRun run) { return !run.cancelled().get() && ACTIVE_MARKER_RUNS.get(run.playerId()) == run - && !run.player().hasDisconnected() - && !run.player().isRemoved() - && run.player().level() == run.level() + && run.player().activeIn(new NativeEditWorld(run.level())) && !run.engine().isClosing() && !run.engine().isClosed(); } - private static void markerFailure(CommandSourceStack source, + private static void markerFailure(NativeCommandSource source, ModdedScheduler scheduler, MarkerRun run, Throwable error) { ModdedIrisLog.error("Iris marker scan failed for {}", run.marker(), error); scheduler.global(() -> { @@ -510,26 +474,23 @@ public final class ModdedWhatCommands { }); } - private static NativeBiome nativeBiome(ServerLevel level, BlockPos pos) { - Holder holder = level.getBiome(pos); - String key = holder.unwrapKey() - .map((ResourceKey resourceKey) -> resourceKey.identifier().toString()) - .orElse(IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED)); - Registry registry = level.registryAccess().lookupOrThrow(Registries.BIOME); - return new NativeBiome(key, registry.getId(holder.value())); + private static NativeBiome nativeBiome(NativeWorld level, NativeBlockPoint pos) { + NativeEditWorld.BiomeIdentity identity = new NativeEditWorld(level).biome(pos); + String key = identity.key() == null ? IrisLanguage.plain(RuntimeUiMessages.STATUS_UNREGISTERED) : identity.key(); + return new NativeBiome(key, identity.id()); } - private static int playerRequired(CommandSourceStack source, + private static int playerRequired(NativeCommandSource source, TextKey message) { IrisModdedCommands.fail(source, IrisLanguage.plain(message)); return 0; } - private static void logLookupFailure(CommandSourceStack source, + private static void logLookupFailure(NativeCommandSource source, String operation, Throwable error, TextKey message) { ModdedIrisLog.error("Iris /what {} lookup failed in {}", operation, - source.getLevel().dimension().identifier(), error); + source.world().name(), error); IrisModdedCommands.fail(source, IrisLanguage.plain( message, MessageArgument.untrusted("value", error.getClass().getSimpleName()))); @@ -540,8 +501,8 @@ public final class ModdedWhatCommands { private record MarkerRun( UUID playerId, - ServerPlayer player, - ServerLevel level, + NativeEditPlayer player, + NativeWorld level, Engine engine, String marker, int chunkX, diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWorldCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWorldCommands.java index b8b6f9719..2c2f15d28 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWorldCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedWorldCommands.java @@ -24,12 +24,12 @@ import art.arcane.iris.pack.BrokenPackException; import art.arcane.iris.pack.PackValidationRegistry; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.modded.IrisModdedChunkGenerator; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; import art.arcane.iris.modded.MainWorldService; import art.arcane.iris.modded.ModdedDimensionManager; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedModConfig; import art.arcane.iris.modded.ModdedPrimaryWorldRouter; -import art.arcane.iris.modded.ModdedServerLevels; import art.arcane.iris.modded.ModdedStartup; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.builder.LiteralArgumentBuilder; @@ -37,12 +37,11 @@ import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.ParsedCommandNode; import com.mojang.brigadier.suggestion.SuggestionProvider; import com.mojang.brigadier.suggestion.SuggestionsBuilder; -import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; -import net.minecraft.commands.SharedSuggestionProvider; -import net.minecraft.commands.arguments.IdentifierArgument; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandSource; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandArguments; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.io.File; import java.util.ArrayList; @@ -56,25 +55,25 @@ import art.arcane.iris.modded.localization.ModdedCommandMessages; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.volmlib.util.localization.MessageArgument; public final class ModdedWorldCommands { - private static final Predicate GATE = Commands.hasPermission(Commands.LEVEL_GAMEMASTERS); + private static final Predicate GATE = NativeCommandRegistration.GAMEMASTERS; private static final String DEFAULT_NAMESPACE = "irisworldgen"; private static final long DEFAULT_SEED = 1337L; - private static final SuggestionProvider LOADED_DIMENSIONS = (CommandContext context, SuggestionsBuilder builder) -> SharedSuggestionProvider.suggest(loadedIrisDimensions(context.getSource().getServer()), builder); + private static final SuggestionProvider LOADED_DIMENSIONS = (CommandContext context, SuggestionsBuilder builder) -> NativeCommandRegistration.suggest(loadedIrisDimensions(context.getSource().server()), builder); private ModdedWorldCommands() { } - public static LiteralArgumentBuilder tree(String name) { - LiteralArgumentBuilder root = Commands.literal(name).requires(GATE); + public static LiteralArgumentBuilder tree(String name) { + LiteralArgumentBuilder root = NativeCommandRegistration.literal(name).requires(GATE); - root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); + root.executes(ModdedCommandTree.localized((CommandContext context) -> ModdedCommandHelp.send(context.getSource(), name))); - root.then(Commands.literal("status") - .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); - root.then(Commands.literal("list") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); - root.then(Commands.literal("ls") - .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("status") + .executes(ModdedCommandTree.localized((CommandContext context) -> status(context.getSource())))); + root.then(NativeCommandRegistration.literal("list") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); + root.then(NativeCommandRegistration.literal("ls") + .executes(ModdedCommandTree.localized((CommandContext context) -> list(context.getSource())))); root.then(enableTree("enable")); root.then(enableTree("create")); @@ -90,34 +89,34 @@ public final class ModdedWorldCommands { return root; } - private static LiteralArgumentBuilder enableTree(String name) { - return Commands.literal(name) - .then(Commands.argument("dimension", IdentifierArgument.id()) - .then(Commands.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> enable(context.getSource(), + private static LiteralArgumentBuilder enableTree(String name) { + return NativeCommandRegistration.literal(name) + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.identifier()) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> enable(context.getSource(), dimensionArgument(context), StringArgumentType.getString(context, "pack"), null))) - .then(Commands.argument("seed", StringArgumentType.word()) - .executes(ModdedCommandTree.localized((CommandContext context) -> enable(context.getSource(), + .then(NativeCommandRegistration.argument("seed", StringArgumentType.word()) + .executes(ModdedCommandTree.localized((CommandContext context) -> enable(context.getSource(), dimensionArgument(context), StringArgumentType.getString(context, "pack"), StringArgumentType.getString(context, "seed"))))))); } - private static LiteralArgumentBuilder disableTree() { - return Commands.literal("disable") - .then(Commands.argument("dimension", IdentifierArgument.id()).suggests(LOADED_DIMENSIONS) - .executes(ModdedCommandTree.localized((CommandContext context) -> disable(context.getSource(), dimensionArgument(context), false)))); + private static LiteralArgumentBuilder disableTree() { + return NativeCommandRegistration.literal("disable") + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.identifier()).suggests(LOADED_DIMENSIONS) + .executes(ModdedCommandTree.localized((CommandContext context) -> disable(context.getSource(), dimensionArgument(context), false)))); } - private static LiteralArgumentBuilder updateTree() { - return Commands.literal("update") - .then(Commands.argument("dimension", IdentifierArgument.id()).suggests(LOADED_DIMENSIONS) - .then(Commands.argument("pack", StringArgumentType.string()) + private static LiteralArgumentBuilder updateTree() { + return NativeCommandRegistration.literal("update") + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.identifier()).suggests(LOADED_DIMENSIONS) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.string()) .suggests(IrisModdedCommands.PACK_NAMES) .executes(ModdedCommandTree.localized( - (CommandContext context) -> update( + (CommandContext context) -> update( context.getSource(), dimensionArgument(context), StringArgumentType.getString(context, "pack") @@ -125,44 +124,44 @@ public final class ModdedWorldCommands { )))); } - private static LiteralArgumentBuilder deleteTree(String name) { - return Commands.literal(name) - .then(Commands.argument("dimension", IdentifierArgument.id()).suggests(LOADED_DIMENSIONS) - .executes(ModdedCommandTree.localized((CommandContext context) -> disable(context.getSource(), dimensionArgument(context), true)))); + private static LiteralArgumentBuilder deleteTree(String name) { + return NativeCommandRegistration.literal(name) + .then(NativeCommandRegistration.argument("dimension", NativeCommandArguments.identifier()).suggests(LOADED_DIMENSIONS) + .executes(ModdedCommandTree.localized((CommandContext context) -> disable(context.getSource(), dimensionArgument(context), true)))); } - private static LiteralArgumentBuilder replaceOverworldTree() { - return Commands.literal("replace-overworld") - .then(Commands.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> replaceOverworld(context.getSource(), + private static LiteralArgumentBuilder replaceOverworldTree() { + return NativeCommandRegistration.literal("replace-overworld") + .then(NativeCommandRegistration.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> replaceOverworld(context.getSource(), StringArgumentType.getString(context, "pack"), null))) - .then(Commands.argument("seed", StringArgumentType.word()) - .executes(ModdedCommandTree.localized((CommandContext context) -> replaceOverworld(context.getSource(), + .then(NativeCommandRegistration.argument("seed", StringArgumentType.word()) + .executes(ModdedCommandTree.localized((CommandContext context) -> replaceOverworld(context.getSource(), StringArgumentType.getString(context, "pack"), StringArgumentType.getString(context, "seed")))))); } - private static LiteralArgumentBuilder mainWorldTree() { - return Commands.literal("mainworld") - .then(Commands.literal("off") - .executes(ModdedCommandTree.localized((CommandContext context) -> clearMainWorld(context.getSource())))) - .then(Commands.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) - .executes(ModdedCommandTree.localized((CommandContext context) -> mainWorld(context.getSource(), + private static LiteralArgumentBuilder mainWorldTree() { + return NativeCommandRegistration.literal("mainworld") + .then(NativeCommandRegistration.literal("off") + .executes(ModdedCommandTree.localized((CommandContext context) -> clearMainWorld(context.getSource())))) + .then(NativeCommandRegistration.argument("pack", StringArgumentType.string()).suggests(IrisModdedCommands.PACK_NAMES) + .executes(ModdedCommandTree.localized((CommandContext context) -> mainWorld(context.getSource(), StringArgumentType.getString(context, "pack"), null))) - .then(Commands.argument("seed", StringArgumentType.word()) - .executes(ModdedCommandTree.localized((CommandContext context) -> mainWorld(context.getSource(), + .then(NativeCommandRegistration.argument("seed", StringArgumentType.word()) + .executes(ModdedCommandTree.localized((CommandContext context) -> mainWorld(context.getSource(), StringArgumentType.getString(context, "pack"), StringArgumentType.getString(context, "seed")))))); } - public static int createWorld(CommandSourceStack source, String name, String pack, long seed) { + public static int createWorld(NativeCommandSource source, String name, String pack, long seed) { String[] packRef = parsePackRef(pack); return enable(source, name, packRef[0], packRef[1], seed); } - private static int enable(CommandSourceStack source, String targetDimension, String packRaw, String seedRaw) { + private static int enable(NativeCommandSource source, String targetDimension, String packRaw, String seedRaw) { Long seed = parseSeed(source, seedRaw); if (seed == null) { return 0; @@ -171,8 +170,8 @@ public final class ModdedWorldCommands { return enable(source, targetDimension, packRef[0], packRef[1], seed); } - private static int enable(CommandSourceStack source, String targetDimension, String pack, String packDimension, long seed) { - MinecraftServer server = source.getServer(); + private static int enable(NativeCommandSource source, String targetDimension, String pack, String packDimension, long seed) { + NativeModdedServer server = source.server(); String dimensionId; try { dimensionId = normalizeDimensionId(targetDimension); @@ -193,7 +192,7 @@ public final class ModdedWorldCommands { return 0; } - private static int enableInstalled(CommandSourceStack source, MinecraftServer server, String dimensionId, String pack, String packDimension, long seed) { + private static int enableInstalled(NativeCommandSource source, NativeModdedServer server, String dimensionId, String pack, String packDimension, long seed) { if (blockIfPackBroken(source, dimensionId, pack)) { return 0; } @@ -212,7 +211,7 @@ public final class ModdedWorldCommands { return 1; } - private static int update(CommandSourceStack source, String targetDimension, String packRaw) { + private static int update(NativeCommandSource source, String targetDimension, String packRaw) { String dimensionId; try { dimensionId = normalizeDimensionId(targetDimension); @@ -230,7 +229,7 @@ public final class ModdedWorldCommands { } try { ModdedDimensionManager.UpdateResult result = ModdedDimensionManager.stagePersistentUpdate( - source.getServer(), + source.server(), dimensionId, pack, packDimension @@ -253,8 +252,8 @@ public final class ModdedWorldCommands { } } - private static int replaceOverworld(CommandSourceStack source, String packRaw, String seedRaw) { - MinecraftServer server = source.getServer(); + private static int replaceOverworld(NativeCommandSource source, String packRaw, String seedRaw) { + NativeModdedServer server = source.server(); Long seed = parseSeed(source, seedRaw); if (seed == null) { return 0; @@ -287,15 +286,15 @@ public final class ModdedWorldCommands { return 1; } - private static int clearMainWorld(CommandSourceStack source) { + private static int clearMainWorld(NativeCommandSource source) { ModdedModConfig.setMainWorld("", 0L); MainWorldService.clearOverride(); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_IRIS_MAIN_WORLD_OVERRIDE_CLEARED_OVERWORLD_KEEPS_ITS_CURRENT_GENERATOR)); return 1; } - private static int mainWorld(CommandSourceStack source, String packRaw, String seedRaw) { - MinecraftServer server = source.getServer(); + private static int mainWorld(NativeCommandSource source, String packRaw, String seedRaw) { + NativeModdedServer server = source.server(); long seed; if (seedRaw == null || seedRaw.isBlank()) { seed = 0L; @@ -326,7 +325,7 @@ public final class ModdedWorldCommands { return 0; } - private static int applyMainWorld(CommandSourceStack source, String pack, String packDimension, String packRef, long seed) { + private static int applyMainWorld(NativeCommandSource source, String pack, String packDimension, String packRef, long seed) { if (blockIfPackBroken(source, "the main world", pack)) { return 0; } @@ -357,14 +356,14 @@ public final class ModdedWorldCommands { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PLAYER_DATA_INVENTORIES_ADVANCEMENTS_STATS_IS_KEPT_EXISTING_TERRAIN_THOSE)); if (ModdedModConfig.get().mainWorldAutoRestart()) { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_MAINWORLDAUTORESTART_IS_ENABLED_STOPPING_SERVER_NOW_SO_YOUR_RESTART_WRAPPER)); - source.getServer().halt(false); + source.server().halt(false); } else { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_RESTART_SERVER_NOW_GENERATE_IT_SET_MAINWORLDAUTORESTART_TRUE_MODDED_JSON)); } return 1; } - private static boolean blockIfPackBroken(CommandSourceStack source, String dimensionId, String pack) { + private static boolean blockIfPackBroken(NativeCommandSource source, String dimensionId, String pack) { try { ModdedStartup.requirePackForWorldCreation(pack); return false; @@ -378,7 +377,7 @@ public final class ModdedWorldCommands { } } - private static boolean loadPackDimension(CommandSourceStack source, String pack, String packDimension) { + private static boolean loadPackDimension(NativeCommandSource source, String pack, String packDimension) { File packFolder = new File(ModdedPackCommands.packsRoot(), pack); if (!packFolder.isDirectory()) { IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_PACK_WAS_NOT_FOUND_UNDER, MessageArgument.untrusted("pack", pack), MessageArgument.untrusted("value", ModdedPackCommands.packsRoot().getAbsolutePath()))); @@ -402,7 +401,7 @@ public final class ModdedWorldCommands { return new String[]{value, value}; } - private static boolean validPackRef(CommandSourceStack source, String pack, String packDimension) { + private static boolean validPackRef(NativeCommandSource source, String pack, String packDimension) { if (pack.matches("[A-Za-z0-9_.-]+") && !pack.contains("..") && packDimension.matches("[A-Za-z0-9_/.-]+") && !packDimension.contains("..")) { return true; @@ -411,7 +410,7 @@ public final class ModdedWorldCommands { return false; } - private static Long parseSeed(CommandSourceStack source, String seedRaw) { + private static Long parseSeed(NativeCommandSource source, String seedRaw) { if (seedRaw == null || seedRaw.isBlank()) { return DEFAULT_SEED; } @@ -427,8 +426,8 @@ public final class ModdedWorldCommands { } } - private static int disable(CommandSourceStack source, String targetDimension, boolean wipeStorage) { - MinecraftServer server = source.getServer(); + private static int disable(NativeCommandSource source, String targetDimension, boolean wipeStorage) { + NativeModdedServer server = source.server(); String dimensionId; try { dimensionId = normalizeDimensionId(targetDimension); @@ -465,13 +464,14 @@ public final class ModdedWorldCommands { return 1; } - private static int status(CommandSourceStack source) { - MinecraftServer server = source.getServer(); + private static int status(NativeCommandSource source) { + NativeModdedServer server = source.server(); int loaded = 0; - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator) { + for (NativeWorld level : server.worlds()) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (generator != null) { loaded++; - IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_LOADED_IRIS_LEVEL_PACK_DIMENSION, MessageArgument.untrusted("value", level.dimension().identifier()), MessageArgument.untrusted("value2", generator.activePack()), MessageArgument.untrusted("value3", generator.activeDimensionKey()))); + IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_LOADED_IRIS_LEVEL_PACK_DIMENSION, MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("value2", generator.activePack()), MessageArgument.untrusted("value3", generator.activeDimensionKey()))); } } String primary = ModdedModConfig.get().primaryWorld(); @@ -484,8 +484,8 @@ public final class ModdedWorldCommands { return loaded > 0 ? 1 : 0; } - private static int list(CommandSourceStack source) { - List dimensions = loadedIrisDimensions(source.getServer()); + private static int list(NativeCommandSource source) { + List dimensions = loadedIrisDimensions(source.server()); IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_LOADED_IRIS_DIMENSIONS, MessageArgument.untrusted("value", dimensions.size()))); for (String dimension : dimensions) { IrisModdedCommands.ok(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_WORLD_COMMANDS_MESSAGE_2, MessageArgument.untrusted("dimension", dimension))); @@ -496,23 +496,23 @@ public final class ModdedWorldCommands { return 1; } - private static List loadedIrisDimensions(MinecraftServer server) { + private static List loadedIrisDimensions(NativeModdedServer server) { List dimensions = new ArrayList<>(); - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator) { - dimensions.add(level.dimension().identifier().toString()); + for (NativeWorld level : server.worlds()) { + if (NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class) != null) { + dimensions.add(level.name()); } } return dimensions; } - private static String dimensionArgument(CommandContext context) { - for (ParsedCommandNode node : context.getNodes()) { + private static String dimensionArgument(CommandContext context) { + for (ParsedCommandNode node : context.getNodes()) { if ("dimension".equals(node.getNode().getName())) { return node.getRange().get(context.getInput()); } } - return IdentifierArgument.getId(context, "dimension").toString(); + return NativeCommandArguments.getIdentifier(context, "dimension"); } private static String normalizeDimensionId(String value) { diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkMapTerrainReceiptAccess.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkMapTerrainReceiptAccess.java deleted file mode 100644 index 22616a0db..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkMapTerrainReceiptAccess.java +++ /dev/null @@ -1,18 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; -import net.minecraft.server.level.ChunkHolder; -import net.minecraft.server.level.ChunkMap; -import net.minecraft.world.level.chunk.ChunkAccess; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(ChunkMap.class) -public interface ChunkMapTerrainReceiptAccess { - @Invoker("save") - boolean iris$saveTerrainCheckpoint(ChunkAccess chunk); - - @Accessor("updatingChunkMap") - Long2ObjectLinkedOpenHashMap iris$updatingChunks(); -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkTerrainReceiptMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkTerrainReceiptMixin.java deleted file mode 100644 index 80388f43a..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/ChunkTerrainReceiptMixin.java +++ /dev/null @@ -1,37 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.NativeTerrainReceiptHolder; -import net.minecraft.world.level.chunk.ChunkAccess; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; - -@Mixin(ChunkAccess.class) -public abstract class ChunkTerrainReceiptMixin implements NativeTerrainReceiptHolder { - @Unique - private byte[] iris$naturalTerrain; - @Unique - private long iris$structureActivation; - - @Override - public long iris$getStructureActivation() { - return iris$structureActivation; - } - - @Override - public void iris$setStructureActivation(long activation) { - if (activation < 0) { - throw new IllegalArgumentException("Native structure activation cannot be negative"); - } - iris$structureActivation = activation; - } - - @Override - public byte[] iris$getNaturalTerrain() { - return iris$naturalTerrain == null ? null : iris$naturalTerrain.clone(); - } - - @Override - public void iris$setNaturalTerrain(byte[] receipt) { - iris$naturalTerrain = receipt == null ? null : receipt.clone(); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/EntityPersistenceMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/EntityPersistenceMixin.java deleted file mode 100644 index d3972a3fc..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/EntityPersistenceMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.ModdedEntityPersistence; -import art.arcane.iris.modded.ModdedMixinFlags; -import net.minecraft.world.entity.Entity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(Entity.class) -public abstract class EntityPersistenceMixin { - @Inject(method = "shouldBeSaved", at = @At("RETURN"), cancellable = true) - private void iris$applyGeneratedPersistence(CallbackInfoReturnable info) { - ModdedMixinFlags.markEntityPersistence(); - Entity entity = (Entity) (Object) this; - info.setReturnValue(ModdedEntityPersistence.shouldSave(entity, info.getReturnValue())); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LevelChunkTerrainReceiptMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LevelChunkTerrainReceiptMixin.java deleted file mode 100644 index 709ac0596..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LevelChunkTerrainReceiptMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.ModdedNativeTerrainReceipts; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.ProtoChunk; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(LevelChunk.class) -public abstract class LevelChunkTerrainReceiptMixin { - @Inject(method = "(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/level/chunk/ProtoChunk;Lnet/minecraft/world/level/chunk/LevelChunk$PostLoadProcessor;)V", at = @At("RETURN")) - private void iris$transferTerrain(ServerLevel level, ProtoChunk source, LevelChunk.PostLoadProcessor processor, - CallbackInfo callback) { - ModdedNativeTerrainReceipts.setStructureActivation((LevelChunk) (Object) this, - ModdedNativeTerrainReceipts.structureActivation(source)); - ModdedNativeTerrainReceipts.set((LevelChunk) (Object) this, ModdedNativeTerrainReceipts.get(source)); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LivingEntityLootMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LivingEntityLootMixin.java deleted file mode 100644 index ac6f63701..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/LivingEntityLootMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.ModdedDeathLoot; -import art.arcane.iris.modded.ModdedMixinFlags; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.damagesource.DamageSource; -import net.minecraft.world.entity.LivingEntity; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(LivingEntity.class) -public abstract class LivingEntityLootMixin { - @Inject(method = "dropFromLootTable(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/world/damagesource/DamageSource;Z)V", at = @At("HEAD"), cancellable = true) - private void iris$replaceBaseLoot(ServerLevel level, DamageSource damageSource, boolean playerKilled, CallbackInfo info) { - ModdedMixinFlags.markLivingEntityLoot(); - if (ModdedDeathLoot.replaceBaseLoot((LivingEntity) (Object) this)) { - info.cancel(); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/MobAwarenessMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/MobAwarenessMixin.java deleted file mode 100644 index e3e02f8d4..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/MobAwarenessMixin.java +++ /dev/null @@ -1,41 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.ModdedEntityAwareness; -import art.arcane.iris.modded.ModdedMixinFlags; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.ai.goal.FloatGoal; -import net.minecraft.world.entity.ai.goal.WrappedGoal; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Mixin(Mob.class) -public abstract class MobAwarenessMixin { - @Inject( - method = "serverAiStep", - at = @At( - value = "FIELD", - target = "Lnet/minecraft/world/entity/Mob;noActionTime:I", - opcode = Opcodes.PUTFIELD, - shift = At.Shift.AFTER), - cancellable = true) - private void iris$tickUnawareMob(CallbackInfo info) { - ModdedMixinFlags.markMobAwareness(); - Mob mob = (Mob) (Object) this; - if (ModdedEntityAwareness.isAware(mob)) { - return; - } - for (WrappedGoal wrappedGoal : mob.getGoalSelector().getAvailableGoals()) { - if (wrappedGoal.getGoal() instanceof FloatGoal floatGoal) { - if (floatGoal.canUse()) { - floatGoal.tick(); - } - mob.getJumpControl().tick(); - break; - } - } - info.cancel(); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/SerializableTerrainReceiptMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/SerializableTerrainReceiptMixin.java deleted file mode 100644 index 31f569bf5..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/SerializableTerrainReceiptMixin.java +++ /dev/null @@ -1,89 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.world.history.NativeTerrainReceipt; -import art.arcane.iris.modded.ModdedNativeTerrainReceipts; -import art.arcane.iris.modded.NativeTerrainReceiptHolder; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.entity.ai.village.poi.PoiManager; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.PalettedContainerFactory; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.storage.RegionStorageInfo; -import net.minecraft.world.level.chunk.storage.SerializableChunkData; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(SerializableChunkData.class) -public abstract class SerializableTerrainReceiptMixin implements NativeTerrainReceiptHolder { - @Unique - private byte[] iris$naturalTerrain; - @Unique - private long iris$structureActivation; - - @Override - public long iris$getStructureActivation() { - return iris$structureActivation; - } - - @Override - public void iris$setStructureActivation(long activation) { - if (activation < 0) { - throw new IllegalArgumentException("Native structure activation cannot be negative"); - } - iris$structureActivation = activation; - } - - @Override - public byte[] iris$getNaturalTerrain() { - return iris$naturalTerrain == null ? null : iris$naturalTerrain.clone(); - } - - @Override - public void iris$setNaturalTerrain(byte[] receipt) { - iris$naturalTerrain = receipt == null ? null : receipt.clone(); - } - - @Inject(method = "parse", at = @At("RETURN")) - private static void iris$parseTerrain(LevelHeightAccessor height, PalettedContainerFactory containers, - CompoundTag tag, CallbackInfoReturnable callback) { - SerializableChunkData parsed = callback.getReturnValue(); - if (parsed != null) { - ((NativeTerrainReceiptHolder) (Object) parsed).iris$setStructureActivation( - tag.getLongOr(NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY, 0)); - ((NativeTerrainReceiptHolder) (Object) parsed).iris$setNaturalTerrain( - tag.getByteArray(NativeTerrainReceipt.NBT_KEY).orElse(null)); - } - } - - @Inject(method = "copyOf", at = @At("RETURN")) - private static void iris$copyTerrain(ServerLevel level, ChunkAccess chunk, - CallbackInfoReturnable callback) { - ((NativeTerrainReceiptHolder) (Object) callback.getReturnValue()).iris$setStructureActivation( - ModdedNativeTerrainReceipts.structureActivation(chunk)); - ((NativeTerrainReceiptHolder) (Object) callback.getReturnValue()).iris$setNaturalTerrain( - ModdedNativeTerrainReceipts.get(chunk)); - } - - @Inject(method = "read", at = @At("RETURN")) - private void iris$restoreTerrain(ServerLevel level, PoiManager points, RegionStorageInfo storage, ChunkPos position, - CallbackInfoReturnable callback) { - ModdedNativeTerrainReceipts.set(callback.getReturnValue(), iris$naturalTerrain); - ModdedNativeTerrainReceipts.setStructureActivation(callback.getReturnValue(), iris$structureActivation); - } - - @Inject(method = "write", at = @At("RETURN")) - private void iris$writeTerrain(CallbackInfoReturnable callback) { - if (iris$structureActivation > 0) { - callback.getReturnValue().putLong(NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY, iris$structureActivation); - } - if (iris$naturalTerrain != null) { - callback.getReturnValue().putByteArray(NativeTerrainReceipt.NBT_KEY, iris$naturalTerrain.clone()); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java deleted file mode 100644 index 3c6213d7b..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java +++ /dev/null @@ -1,31 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.ModdedMixinFlags; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Mixin(StructureTemplate.Palette.class) -public abstract class StructureTemplatePaletteConcurrencyMixin { - @Shadow - @Final - @Mutable - private Map> cache; - - @Inject(method = "(Ljava/util/List;)V", at = @At("RETURN")) - private void iris$installConcurrentBlockCache(List blocks, - CallbackInfo info) { - cache = new ConcurrentHashMap<>(); - ModdedMixinFlags.markStructureTemplatePalette(); - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/WorldGenerationWriteGuardMixin.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/WorldGenerationWriteGuardMixin.java deleted file mode 100644 index 8aac2b380..000000000 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/mixin/WorldGenerationWriteGuardMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package art.arcane.iris.modded.mixin; - -import art.arcane.iris.modded.IrisModdedChunkGenerator; -import net.minecraft.core.BlockPos; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.world.level.chunk.status.ChunkStatus; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(WorldGenRegion.class) -public abstract class WorldGenerationWriteGuardMixin { - @Inject(method = "ensureCanWrite", at = @At("RETURN"), cancellable = true) - private void iris$guardCompletedTerrain(BlockPos position, CallbackInfoReturnable result) { - WorldGenRegion region = (WorldGenRegion) (Object) this; - if (result.getReturnValue() && region.getLevel().getChunkSource().getGenerator() - instanceof IrisModdedChunkGenerator generator - && region.getChunk(position.getX() >> 4, position.getZ() >> 4) - .getPersistedStatus().isOrAfter(ChunkStatus.FULL) - && !generator.allowsNativeChunkWrite(position.getX() >> 4, position.getZ() >> 4)) { - result.setReturnValue(false); - } - } -} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedChunkUpdateService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedChunkUpdateService.java index d8db9f4c7..b44a616fc 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedChunkUpdateService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedChunkUpdateService.java @@ -18,6 +18,13 @@ package art.arcane.iris.modded.service; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldMaintenance; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockProperties; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.studio.view.PregeneratorJob; import art.arcane.iris.integration.Identifier; @@ -27,9 +34,9 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.modded.IrisModdedChunkGenerator; import art.arcane.iris.modded.ModdedBlockResolution; import art.arcane.iris.modded.ModdedLootApplier; -import art.arcane.iris.modded.ModdedServerLevels; import art.arcane.iris.modded.ModdedTileData; import art.arcane.iris.modded.api.ModdedCustomContentRegistry; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockPlacement; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.world.storage.matter.TileWrapper; import art.arcane.volmlib.util.mantle.flag.MantleFlag; @@ -39,16 +46,7 @@ import art.arcane.volmlib.util.matter.Matter; import art.arcane.volmlib.util.matter.MatterCavern; import art.arcane.volmlib.util.matter.MatterUpdate; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -60,7 +58,6 @@ import java.util.concurrent.TimeUnit; public final class ModdedChunkUpdateService implements ModdedTickableService { private static final long PASS_PERIOD_MILLIS = 3_000L; private static final int PLAYER_CHUNK_RADIUS = 1; - private static final int SILENT_FLAGS = Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_SKIP_ON_PLACE; private final Set warmupQueue = ConcurrentHashMap.newKeySet(); private volatile ExecutorService warmupExecutor; @@ -104,7 +101,7 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { } @Override - public void onServerTick(MinecraftServer server) { + public void onServerTick(NativeModdedServer server) { long now = System.currentTimeMillis(); if (now - lastPassAt < PASS_PERIOD_MILLIS) { return; @@ -115,15 +112,17 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { return; } - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + for (NativeWorld level : server.worlds()) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (generator == null) { continue; } Engine engine = generator.engineIfBound(); if (engine == null || engine.isClosed() || engine.getMantle().getMantle().isClosed()) { continue; } - if (!hasUpdateTargets(!level.players().isEmpty(), !level.getForceLoadedChunks().isEmpty()) || isPregenActive(engine)) { + NativeWorldMaintenance nativeWorld = new NativeWorldMaintenance(level); + if (!hasUpdateTargets(nativeWorld.hasPlayers(), nativeWorld.hasForcedChunks()) || isPregenActive(engine)) { continue; } try { @@ -144,37 +143,36 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { return job != null && job.targetsWorldIdentity(engine.getWorld().identity()); } - private void updateNearPlayers(Engine engine, ServerLevel level) { - for (ServerPlayer player : level.players()) { - int centerX = player.blockPosition().getX() >> 4; - int centerZ = player.blockPosition().getZ() >> 4; + private void updateNearPlayers(Engine engine, NativeWorld level) { + new NativeWorldMaintenance(level).forEachPlayerPosition(player -> { + int centerX = player.x() >> 4; + int centerZ = player.z() >> 4; for (int dx = -PLAYER_CHUNK_RADIUS; dx <= PLAYER_CHUNK_RADIUS; dx++) { for (int dz = -PLAYER_CHUNK_RADIUS; dz <= PLAYER_CHUNK_RADIUS; dz++) { updateChunk(engine, level, centerX + dx, centerZ + dz); } } - } + }); } - private void updateForcedChunks(Engine engine, ServerLevel level) { - for (long chunkKey : level.getForceLoadedChunks()) { - updateChunk(engine, level, ChunkPos.getX(chunkKey), ChunkPos.getZ(chunkKey)); - } + private void updateForcedChunks(Engine engine, NativeWorld level) { + new NativeWorldMaintenance(level).forEachForcedChunk(chunkKey -> + updateChunk(engine, level, (int) chunkKey, (int) (chunkKey >> 32))); } - public void updateRegeneratedChunk(Engine engine, ServerLevel level, int chunkX, int chunkZ) { + public void updateRegeneratedChunk(Engine engine, NativeWorld level, int chunkX, int chunkZ) { updateChunk(engine, level, chunkX, chunkZ, false); } - private void updateChunk(Engine engine, ServerLevel level, int chunkX, int chunkZ) { + private void updateChunk(Engine engine, NativeWorld level, int chunkX, int chunkZ) { updateChunk(engine, level, chunkX, chunkZ, true); } - private void updateChunk(Engine engine, ServerLevel level, int chunkX, int chunkZ, boolean requireNeighbors) { + private void updateChunk(Engine engine, NativeWorld level, int chunkX, int chunkZ, boolean requireNeighbors) { if (requireNeighbors) { for (int x = -1; x <= 1; x++) { for (int z = -1; z <= 1; z++) { - if (level.getChunkSource().getChunkNow(chunkX + x, chunkZ + z) == null) { + if (!level.isChunkLoaded(chunkX + x, chunkZ + z)) { return; } } @@ -202,32 +200,33 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { } } - private void runTilePass(Engine engine, ServerLevel level, int chunkX, int chunkZ, MantleChunk chunk) { + private void runTilePass(Engine engine, NativeWorld level, int chunkX, int chunkZ, MantleChunk chunk) { int baseX = chunkX << 4; int baseZ = chunkZ << 4; int minHeight = engine.getWorld().minHeight(); materializeDeferredSlice(chunk, TileWrapper.class, () -> chunk.iterate(TileWrapper.class, (Integer x, Integer yf, Integer z, TileWrapper v) -> { int y = yf + minHeight; - if (y < level.getMinY() || y >= level.getMaxY()) { + if (y < level.minHeight() || y >= level.maxHeight() - 1) { return; } applyTile(level, baseX + (x & 15), y, baseZ + (z & 15), v.getData()); })); } - private void runCustomPass(Engine engine, ServerLevel level, int chunkX, int chunkZ, MantleChunk chunk) { + private void runCustomPass(Engine engine, NativeWorld level, int chunkX, int chunkZ, MantleChunk chunk) { int baseX = chunkX << 4; int baseZ = chunkZ << 4; int minHeight = engine.getWorld().minHeight(); materializeDeferredSlice(chunk, Identifier.class, () -> chunk.iterate(Identifier.class, (Integer x, Integer yf, Integer z, Identifier identifier) -> { int y = yf + minHeight; - if (y < level.getMinY() || y >= level.getMaxY()) { + if (y < level.minHeight() || y >= level.maxHeight() - 1) { return; } - BlockPos position = new BlockPos(baseX + (x & 15), y, baseZ + (z & 15)); - ModdedCustomContentRegistry.processBlockPlacement(engine, level, position, identifier.toString()); + NativeBlockPoint position = new NativeBlockPoint(baseX + (x & 15), y, baseZ + (z & 15)); + ModdedCustomContentRegistry.processBlockPlacement(engine, identifier.toString(), + () -> NativeWorldMaintenance.placement(level, position)); })); } @@ -236,35 +235,17 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { chunk.deleteSlices(sliceType); } - private void applyTile(ServerLevel level, int x, int y, int z, TileData tile) { + private void applyTile(NativeWorld level, int x, int y, int z, TileData tile) { if (!(tile instanceof ModdedTileData moddedTile)) { return; } - BlockPos pos = new BlockPos(x, y, z); - BlockState state = level.getBlockState(pos); - BlockState adjusted = moddedTile.adjustBlockState(state); - if (adjusted != state) { - level.setBlock(pos, adjusted, SILENT_FLAGS); - state = adjusted; - } - if (!state.hasBlockEntity() || !(state.getBlock() instanceof EntityBlock entityBlock)) { - return; - } + NativeBlockPoint position = new NativeBlockPoint(x, y, z); try { - BlockEntity blockEntity = level.getBlockEntity(pos); - if (blockEntity == null) { - blockEntity = entityBlock.newBlockEntity(pos, state); - } - if (blockEntity == null) { - IrisLogging.warn("Iris could not create block entity at " + pos + " for " + state); - return; - } - if (!moddedTile.isApplicable(state, blockEntity)) { - return; - } - level.setBlockEntity(blockEntity); - if (!moddedTile.apply(blockEntity, level)) { - IrisLogging.warn("Iris tile payload was empty at " + pos + " for " + state); + NativeWorldMaintenance.TileResult result = NativeWorldMaintenance.applyTile(level, position, moddedTile.nativeData()); + if (result == NativeWorldMaintenance.TileResult.MISSING_ENTITY) { + IrisLogging.warn("Iris could not create block entity at " + position + " for " + level.getBlock(x, y, z)); + } else if (result == NativeWorldMaintenance.TileResult.EMPTY_PAYLOAD) { + IrisLogging.warn("Iris tile payload was empty at " + position + " for " + level.getBlock(x, y, z)); } } catch (Throwable e) { IrisLogging.reportError(e); @@ -301,7 +282,7 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { } } - void runUpdatePass(Engine engine, ServerLevel level, int chunkX, int chunkZ, MantleChunk chunk) { + void runUpdatePass(Engine engine, NativeWorld level, int chunkX, int chunkZ, MantleChunk chunk) { PrecisionStopwatch stopwatch = PrecisionStopwatch.start(); int minHeight = engine.getWorld().minHeight(); int baseX = chunkX << 4; @@ -315,22 +296,12 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { chunk.iterate(MatterCavern.class, (Integer x, Integer yf, Integer z, MatterCavern v) -> { int y = yf + minHeight; - if (y < level.getMinY() || y >= level.getMaxY()) { + if (y < level.minHeight() || y >= level.maxHeight() - 1) { return; } int lx = x & 15; int lz = z & 15; - BlockPos pos = new BlockPos(baseX + lx, y, baseZ + lz); - if (!ModdedBlockResolution.isFluid(level.getBlockState(pos))) { - return; - } - boolean exposed = ModdedBlockResolution.isAir(level.getBlockState(pos.below())) - || ModdedBlockResolution.isAir(level.getBlockState(pos.west())) - || ModdedBlockResolution.isAir(level.getBlockState(pos.east())) - || ModdedBlockResolution.isAir(level.getBlockState(pos.south())) - || ModdedBlockResolution.isAir(level.getBlockState(pos.north())); - - if (exposed) { + if (NativeWorldMaintenance.exposedFluid(level, baseX + lx, y, baseZ + lz)) { grid[lx][lz] = Math.max(grid[lx][lz], y); } }); @@ -353,15 +324,15 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { engine.getMetrics().getUpdates().put(stopwatch.getMilliseconds()); } - private void update(Engine engine, ServerLevel level, int x, int y, int z, int baseX, int baseZ, MantleChunk chunk) { - if (y < level.getMinY() || y >= level.getMaxY()) { + private void update(Engine engine, NativeWorld level, int x, int y, int z, int baseX, int baseZ, MantleChunk chunk) { + if (y < level.minHeight() || y >= level.maxHeight() - 1) { return; } - BlockPos pos = new BlockPos(baseX + (x & 15), y, baseZ + (z & 15)); - BlockState state = level.getBlockState(pos); + NativeBlockPoint pos = new NativeBlockPoint(baseX + (x & 15), y, baseZ + (z & 15)); + NativeBlockState state = level.getBlock(pos.x(), pos.y(), pos.z()); engine.blockUpdatedMetric(); - if (ModdedBlockResolution.isStorage(state)) { - if (!ModdedBlockResolution.isStorageChest(state)) { + if (state.isStorage()) { + if (!state.isStorageChest()) { return; } try { @@ -370,8 +341,7 @@ public final class ModdedChunkUpdateService implements ModdedTickableService { IrisLogging.reportError(e); } } else { - level.setBlock(pos, Blocks.AIR.defaultBlockState(), SILENT_FLAGS); - level.setBlock(pos, state, Block.UPDATE_ALL); + NativeWorldMaintenance.refresh(level, pos); } } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java index dab5344f6..2be9e1e7a 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java @@ -27,7 +27,7 @@ import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.modded.ModdedWorldEngines; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.generation.context.IrisContext; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.util.Collection; import java.util.Collections; @@ -89,7 +89,7 @@ public final class ModdedEngineMaintenanceService implements ModdedTickableServi } @Override - public void onServerTick(MinecraftServer server) { + public void onServerTick(NativeModdedServer server) { ExecutorService active = service; if (active == null || active.isShutdown()) { return; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEntitySpawnService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEntitySpawnService.java index ee0d406d7..5658ea2da 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEntitySpawnService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedEntitySpawnService.java @@ -21,11 +21,12 @@ package art.arcane.iris.modded.service; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineWorldManager; import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedServerLevels; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; import art.arcane.iris.modded.ModdedWorldManager; import art.arcane.iris.spi.IrisLogging; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; public final class ModdedEntitySpawnService implements ModdedTickableService { @Override @@ -37,9 +38,10 @@ public final class ModdedEntitySpawnService implements ModdedTickableService { } @Override - public void onServerTick(MinecraftServer server) { - for (ServerLevel level : ModdedServerLevels.levels(server)) { - if (!(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + public void onServerTick(NativeModdedServer server) { + for (NativeWorld level : server.worlds()) { + IrisModdedChunkGenerator generator = NativeWorldGenerators.find(level, IrisModdedChunkGenerator.class); + if (generator == null) { continue; } Engine engine = generator.engineIfBound(); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedLogFilterService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedLogFilterService.java index 9158218f0..1f2dd64fe 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedLogFilterService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedLogFilterService.java @@ -26,14 +26,14 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.message.Message; -import java.util.List; +import java.util.Set; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeServerLogMessages; public final class ModdedLogFilterService implements ModdedService, Filter { - private static final String VANILLA_LOGGER_PREFIX = "net.minecraft"; - private static final List FILTERS = List.of( - "Ignoring heightmap data for chunk", - "Could not save data net.minecraft.world.entity.raid.PersistentRaid", - "UUID of added entity already exists"); + private static final Set FILTERS = Set.of( + NativeServerLogMessages.Kind.IGNORED_HEIGHTMAP, + NativeServerLogMessages.Kind.RAID_PERSISTENCE, + NativeServerLogMessages.Kind.DUPLICATE_ENTITY); private boolean installed = false; @@ -163,14 +163,7 @@ public final class ModdedLogFilterService implements ModdedService, Filter { } private Result check(String loggerName, String message) { - if (loggerName == null || !loggerName.startsWith(VANILLA_LOGGER_PREFIX) || message == null) { - return Result.NEUTRAL; - } - for (String filter : FILTERS) { - if (message.contains(filter)) { - return Result.DENY; - } - } - return Result.NEUTRAL; + return FILTERS.contains(NativeServerLogMessages.classify(loggerName, message)) + ? Result.DENY : Result.NEUTRAL; } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedSettingsHotloadService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedSettingsHotloadService.java index c0a18f3ae..79a5baf5d 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedSettingsHotloadService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedSettingsHotloadService.java @@ -20,7 +20,7 @@ package art.arcane.iris.modded.service; import art.arcane.iris.configuration.SettingsHotloadWatch; import art.arcane.iris.spi.IrisPlatforms; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.io.File; import java.util.concurrent.TimeUnit; @@ -45,7 +45,7 @@ public final class ModdedSettingsHotloadService implements ModdedTickableService } @Override - public void onServerTick(MinecraftServer server) { + public void onServerTick(NativeModdedServer server) { SettingsHotloadWatch active = hotloadWatch; if (active == null) { return; diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedStudioHotloadService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedStudioHotloadService.java index ada3e0142..db6916eee 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedStudioHotloadService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedStudioHotloadService.java @@ -28,7 +28,7 @@ import art.arcane.iris.modded.ModdedSavedTerrainCapture; import java.util.concurrent.CompletableFuture; import art.arcane.iris.generation.runtime.EnginePlatformHooks; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; import art.arcane.iris.world.IrisWorld; @@ -38,14 +38,15 @@ import art.arcane.iris.modded.ModdedDimensionManager; import art.arcane.iris.modded.ModdedForcedDatapack; import art.arcane.iris.modded.ModdedScheduler; import art.arcane.iris.modded.ModdedWorkspaceGenerator; -import art.arcane.iris.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; import art.arcane.iris.modded.command.ModdedPregenJob; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.io.ReactiveFolder; import art.arcane.volmlib.util.scheduling.ChronoLatch; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.border.WorldBorder; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldBoundary; import java.io.File; import java.util.HashSet; @@ -148,13 +149,13 @@ public final class ModdedStudioHotloadService implements ModdedTickableService, @Override public void prepareRuntimeHotload(Engine engine) { IrisWorld world = engine.getWorld(); - if (world == null || !world.hasPlatformWorld() - || !(world.platformWorld().nativeHandle() instanceof ServerLevel level) - || !(level.getChunkSource().getGenerator() instanceof IrisModdedChunkGenerator generator)) { + IrisModdedChunkGenerator generator = world == null || !world.hasPlatformWorld() ? null + : NativeWorldGenerators.find(world.platformWorld(), IrisModdedChunkGenerator.class); + if (generator == null) { NativeStructureVolumeIndex.invalidate(engine); return; } - generator.prepareRuntimeHotload(level, engine); + generator.prepareRuntimeHotload(world.platformWorld(), engine); } @Override @@ -165,21 +166,21 @@ public final class ModdedStudioHotloadService implements ModdedTickableService, return; } IrisWorld expectedIrisWorld = engine.getWorld(); - if (expectedIrisWorld == null || !expectedIrisWorld.hasPlatformWorld() - || !(expectedIrisWorld.platformWorld().nativeHandle() instanceof ServerLevel expectedLevel)) { + if (expectedIrisWorld == null || !expectedIrisWorld.hasPlatformWorld()) { return; } + NativeWorld expectedLevel = expectedIrisWorld.platformWorld(); IrisWorldBoundary boundary; try { boundary = IrisWorldBoundary.snapshot(configuredBoundary); } catch (Throwable error) { ModdedIrisLog.error("Invalid Iris world boundary for " - + expectedLevel.dimension().identifier() + ".", error); + + expectedLevel.name() + ".", error); throw propagateBoundaryFailure("Invalid Iris world boundary for '" - + expectedLevel.dimension().identifier() + "'.", error); + + expectedLevel.name() + "'.", error); } - MinecraftServer server = expectedLevel.getServer(); - if (server.isSameThread()) { + NativeModdedServer server = NativeModdedServer.forWorld(expectedLevel); + if (server.isServerThread()) { applyWorldBoundary(engine, expectedDimension, expectedIrisWorld, expectedLevel, boundary); return; } @@ -187,14 +188,14 @@ public final class ModdedStudioHotloadService implements ModdedTickableService, server.execute(() -> applyWorldBoundary(engine, expectedDimension, expectedIrisWorld, expectedLevel, boundary)); } catch (Throwable error) { ModdedIrisLog.error("Failed to schedule Iris world-boundary application for " - + expectedLevel.dimension().identifier() + ".", error); + + expectedLevel.name() + ".", error); throw propagateBoundaryFailure("Failed to schedule Iris world-boundary application for '" - + expectedLevel.dimension().identifier() + "'.", error); + + expectedLevel.name() + "'.", error); } } @Override - public void onServerTick(MinecraftServer server) { + public void onServerTick(NativeModdedServer server) { ExecutorService active = executor; if (active == null) { return; @@ -340,32 +341,30 @@ public final class ModdedStudioHotloadService implements ModdedTickableService, return false; } - static void applyWorldBoundary(WorldBorder worldBorder, IrisWorldBoundary boundary) { + static void applyWorldBoundary(NativeWorld world, IrisWorldBoundary boundary) { if (boundary == null) { return; } - worldBorder.setCenter(boundary.getCenter().getX(), boundary.getCenter().getZ()); - worldBorder.setSize(boundary.getSize()); - worldBorder.setWarningBlocks(boundary.getWarningDistance()); - worldBorder.setSafeZone(boundary.getDamageBuffer()); - worldBorder.setDamagePerBlock(boundary.getDamageAmount()); + NativeWorldBoundary.apply(world, new NativeWorldBoundary.Settings( + boundary.getCenter().getX(), boundary.getCenter().getZ(), boundary.getSize(), + boundary.getWarningDistance(), boundary.getDamageBuffer(), boundary.getDamageAmount())); } private static void applyWorldBoundary(Engine engine, IrisDimension expectedDimension, - IrisWorld expectedIrisWorld, ServerLevel expectedLevel, + IrisWorld expectedIrisWorld, NativeWorld expectedLevel, IrisWorldBoundary boundary) { if (engine.isClosed() || engine.isClosing() || engine.getDimension() != expectedDimension || engine.getWorld() != expectedIrisWorld - || expectedIrisWorld.platformWorld().nativeHandle() != expectedLevel) { + || !expectedIrisWorld.platformWorld().equals(expectedLevel)) { return; } try { - applyWorldBoundary(expectedLevel.getWorldBorder(), boundary); + applyWorldBoundary(expectedLevel, boundary); } catch (Throwable error) { ModdedIrisLog.error("Failed to apply Iris world boundary to " - + expectedLevel.dimension().identifier() + ".", error); + + expectedLevel.name() + ".", error); throw propagateBoundaryFailure("Failed to apply Iris world boundary to '" - + expectedLevel.dimension().identifier() + "'.", error); + + expectedLevel.name() + "'.", error); } } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTickableService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTickableService.java index b086e4fbd..7dd86e2f3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTickableService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTickableService.java @@ -18,8 +18,8 @@ package art.arcane.iris.modded.service; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; public interface ModdedTickableService extends ModdedService { - void onServerTick(MinecraftServer server); + void onServerTick(NativeModdedServer server); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentation.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentation.java index 5b13a2a45..3f1017452 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentation.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentation.java @@ -1,18 +1,15 @@ package art.arcane.iris.modded.service; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDropEffects; + import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedScheduler; -import net.minecraft.core.BlockPos; -import net.minecraft.core.particles.BlockParticleOption; -import net.minecraft.core.particles.ParticleTypes; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.sounds.SoundEvents; -import net.minecraft.sounds.SoundSource; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; import java.util.ArrayList; import java.util.Collection; @@ -25,9 +22,10 @@ final class ModdedTreeFellerPresentation { private static final int TARGET_EROSION_PULSES = 60; private static final int MAX_EFFECT_ORIGINS_PER_PULSE = 16; - private final ServerPlayer player; - private final ServerLevel sourceLevel; - private final List pendingDrops = new ArrayList<>(); + private final NativeProtocolPlayer player; + private final NativeWorld sourceLevel; + private final NativeDropEffects effects; + private final List pendingDrops = new ArrayList<>(); private final AtomicBoolean effectFailureReported = new AtomicBoolean(); private final AtomicBoolean deliveryFailureReported = new AtomicBoolean(); private boolean flushScheduled; @@ -35,12 +33,13 @@ final class ModdedTreeFellerPresentation { private double fallbackY; private double fallbackZ; - ModdedTreeFellerPresentation(ServerPlayer player, ServerLevel sourceLevel) { + ModdedTreeFellerPresentation(NativeProtocolPlayer player, NativeWorld sourceLevel) { this.player = player; this.sourceLevel = sourceLevel; - this.fallbackX = player.getX(); - this.fallbackY = player.getY() + 0.15D; - this.fallbackZ = player.getZ(); + this.effects = new NativeDropEffects(sourceLevel); + this.fallbackX = player.x(); + this.fallbackY = player.y() + 0.15D; + this.fallbackZ = player.z(); } static int blocksPerPulse(int blockCount) { @@ -55,67 +54,55 @@ final class ModdedTreeFellerPresentation { ); } - static List consolidateDrops(Collection drops) { - List consolidated = new ArrayList<>(); - for (ItemStack drop : drops) { + static List consolidateDrops(Collection drops) { + List consolidated = new ArrayList<>(); + for (NativeItemStack drop : drops) { mergeDrop(consolidated, drop); } return List.copyOf(consolidated); } - void activate(BlockPos position, BlockState state) { + void activate(NativeBlockPoint position, NativeBlockState state) { try { - double x = position.getX() + 0.5D; - double y = position.getY() + 0.5D; - double z = position.getZ() + 0.5D; - sourceLevel.sendParticles(ParticleTypes.ENCHANT, x, y, z, 24, 0.45D, 0.45D, 0.45D, 0.18D); - sourceLevel.sendParticles(ParticleTypes.END_ROD, x, y, z, 8, 0.25D, 0.25D, 0.25D, 0.035D); - sourceLevel.sendParticles( - new BlockParticleOption(ParticleTypes.BLOCK, state), - x, - y, - z, - 8, - 0.25D, - 0.25D, - 0.25D, - 0.04D - ); - sourceLevel.playSound(null, position, SoundEvents.ENCHANTMENT_TABLE_USE, SoundSource.PLAYERS, 0.55F, 1.35F); - sourceLevel.playSound(null, position, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 0.4F, 0.8F); + double x = position.x() + 0.5D; + double y = position.y() + 0.5D; + double z = position.z() + 0.5D; + effects.particle(NativeDropEffects.Particle.ENCHANT, + new NativeDropEffects.Emission(x, y, z, 24, 0.45D, 0.45D, 0.45D, 0.18D), null); + effects.particle(NativeDropEffects.Particle.END_ROD, + new NativeDropEffects.Emission(x, y, z, 8, 0.25D, 0.25D, 0.25D, 0.035D), null); + effects.particle(NativeDropEffects.Particle.BLOCK, + new NativeDropEffects.Emission(x, y, z, 8, 0.25D, 0.25D, 0.25D, 0.04D), state); + effects.sound("minecraft:block.enchantment_table.use", + new NativeDropEffects.SoundEmission(x, y, z, 0.55F, 1.35F)); + effects.sound("minecraft:block.amethyst_block.chime", + new NativeDropEffects.SoundEmission(x, y, z, 0.4F, 0.8F)); } catch (Throwable error) { reportEffectFailure(error); } } - void erode(BlockPos position, BlockState state, int processed, int effectStride, float pitch) { + void erode(NativeBlockPoint position, NativeBlockState state, int processed, int effectStride, float pitch) { if (processed % effectStride != 0) { return; } try { - double x = position.getX() + 0.5D; - double y = position.getY() + 0.5D; - double z = position.getZ() + 0.5D; - sourceLevel.sendParticles( - new BlockParticleOption(ParticleTypes.BLOCK, state), - x, - y, - z, - 5, - 0.3D, - 0.3D, - 0.3D, - 0.04D - ); - sourceLevel.sendParticles(ParticleTypes.ENCHANT, x, y, z, 3, 0.28D, 0.28D, 0.28D, 0.12D); - sourceLevel.playSound(null, position, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 0.22F, pitch); + double x = position.x() + 0.5D; + double y = position.y() + 0.5D; + double z = position.z() + 0.5D; + effects.particle(NativeDropEffects.Particle.BLOCK, + new NativeDropEffects.Emission(x, y, z, 5, 0.3D, 0.3D, 0.3D, 0.04D), state); + effects.particle(NativeDropEffects.Particle.ENCHANT, + new NativeDropEffects.Emission(x, y, z, 3, 0.28D, 0.28D, 0.28D, 0.12D), null); + effects.sound("minecraft:block.amethyst_block.chime", + new NativeDropEffects.SoundEmission(x, y, z, 0.22F, pitch)); } catch (Throwable error) { reportEffectFailure(error); } } - synchronized boolean route(Iterable drops) { - for (ItemStack drop : drops) { + synchronized boolean route(Iterable drops) { + for (NativeItemStack drop : drops) { if (drop != null && !drop.isEmpty()) { pendingDrops.add(drop.copy()); } @@ -129,23 +116,21 @@ final class ModdedTreeFellerPresentation { if (pendingDrops.isEmpty()) { return; } - List drops = consolidateDrops(pendingDrops); + List drops = consolidateDrops(pendingDrops); pendingDrops.clear(); - boolean atPlayer = !player.isRemoved() && player.level() == sourceLevel; - double x = atPlayer ? player.getX() : fallbackX; - double y = atPlayer ? player.getY() + 0.15D : fallbackY; - double z = atPlayer ? player.getZ() : fallbackZ; + boolean atPlayer = !player.removed() && player.inWorld(sourceLevel); + double x = atPlayer ? player.x() : fallbackX; + double y = atPlayer ? player.y() + 0.15D : fallbackY; + double z = atPlayer ? player.z() : fallbackZ; if (atPlayer) { fallbackX = x; fallbackY = y; fallbackZ = z; } int delivered = 0; - for (ItemStack drop : drops) { + for (NativeItemStack drop : drops) { try { - ItemEntity item = new ItemEntity(sourceLevel, x, y, z, drop, 0D, 0.08D, 0D); - item.setDefaultPickUpDelay(); - if (sourceLevel.addFreshEntity(item)) { + if (effects.drop(drop, new NativeDropEffects.DropPosition(x, y, z, 0D, 0.08D, 0D))) { delivered++; } else { pendingDrops.add(drop.copy()); @@ -157,8 +142,9 @@ final class ModdedTreeFellerPresentation { } try { int particles = Math.min(32, 6 + (delivered * 2)); - sourceLevel.sendParticles(ParticleTypes.ENCHANT, x, y + 0.35D, z, particles, 0.3D, 0.25D, 0.3D, 0.1D); - sourceLevel.playSound(null, x, y, z, SoundEvents.AMETHYST_BLOCK_CHIME, SoundSource.PLAYERS, 0.28F, 1.75F); + effects.particle(NativeDropEffects.Particle.ENCHANT, + new NativeDropEffects.Emission(x, y + 0.35D, z, particles, 0.3D, 0.25D, 0.3D, 0.1D), null); + effects.sound("minecraft:block.amethyst_block.chime", new NativeDropEffects.SoundEmission(x, y, z, 0.28F, 1.75F)); } catch (Throwable error) { reportEffectFailure(error); } @@ -184,20 +170,20 @@ final class ModdedTreeFellerPresentation { scheduler.laterGlobal(this::flush, 1); } - private static void mergeDrop(List consolidated, ItemStack drop) { + private static void mergeDrop(List consolidated, NativeItemStack drop) { if (drop == null || drop.isEmpty()) { return; } - ItemStack remaining = drop.copy(); - for (ItemStack existing : consolidated) { - if (!ItemStack.isSameItemSameComponents(existing, remaining)) { + NativeItemStack remaining = drop.copy(); + for (NativeItemStack existing : consolidated) { + if (!NativeItemStack.sameItemAndComponents(existing, remaining)) { continue; } - int capacity = existing.getMaxStackSize() - existing.getCount(); + int capacity = existing.maxStackSize() - existing.count(); if (capacity <= 0) { continue; } - int moved = Math.min(capacity, remaining.getCount()); + int moved = Math.min(capacity, remaining.count()); existing.grow(moved); remaining.shrink(moved); if (remaining.isEmpty()) { @@ -205,7 +191,7 @@ final class ModdedTreeFellerPresentation { } } while (!remaining.isEmpty()) { - int amount = Math.min(remaining.getCount(), remaining.getMaxStackSize()); + int amount = Math.min(remaining.count(), remaining.maxStackSize()); consolidated.add(remaining.copyWithCount(amount)); remaining.shrink(amount); } diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerService.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerService.java index 2393cefa0..d4fb602ce 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerService.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedTreeFellerService.java @@ -1,5 +1,12 @@ package art.arcane.iris.modded.service; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeHarvestSession; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; + import art.arcane.iris.modded.ModdedIrisLog; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.world.tree.TreeDefinitionIndex; @@ -8,22 +15,10 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.StructurePlacementMarker; import art.arcane.iris.generation.decoration.tree.TreeBlockMaterial; import art.arcane.iris.modded.ModdedBlockBreakHandler; -import art.arcane.iris.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.modded.ModdedEngineBootstrap; import art.arcane.iris.modded.ModdedScheduler; -import net.minecraft.core.BlockPos; -import net.minecraft.core.component.DataComponents; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.tags.BlockTags; -import net.minecraft.tags.ItemTags; -import net.minecraft.world.entity.EquipmentSlot; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; import java.util.Collections; import java.util.List; @@ -79,7 +74,7 @@ public final class ModdedTreeFellerService implements ModdedTickableService { } @Override - public void onServerTick(MinecraftServer server) { + public void onServerTick(NativeModdedServer server) { for (FellingRun run : List.copyOf(activeRuns)) { if (!isRunControlActive(run)) { finish(run); @@ -88,34 +83,34 @@ public final class ModdedTreeFellerService implements ModdedTickableService { } public PreparedOrigin prepare( - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState state + NativeWorld level, + NativeProtocolPlayer player, + NativeBlockPoint position, + NativeBlockState state ) { IrisSettings.IrisSettingsTreeFeller settings = IrisSettings.get().getTreeFeller(); if (!enabled.get() || settings == null || !settings.isEnabled() - || !player.gameMode().isSurvival() - || !player.isShiftKeyDown() - || !state.is(BlockTags.LOGS) - || !player.getInventory().getSelectedItem().is(ItemTags.AXES) - || !ModdedEngineBootstrap.loader().hasTreeFellerPermission(player)) { + || !NativeHarvestSession.survival(player) + || !NativeHarvestSession.sneaking(player) + || !NativeHarvestSession.log(state) + || !NativeHarvestSession.holdingAxe(player) + || !NativeHarvestSession.hasPermission(ModdedEngineBootstrap.loader(), player)) { return null; } Engine engine = ModdedBlockBreakHandler.engineFor(level); if (engine == null || engine.isClosed()) { return null; } - int minimumY = level.getMinY(); + int minimumY = level.minHeight(); String marker = markerAt(engine, minimumY, position); StructurePlacementMarker.Decoded decoded = StructurePlacementMarker.decode(marker); if (decoded == null || decoded.structureAware()) { return null; } TreeBlockMaterial expectedMaterial = materialAt(engine, minimumY, position); - if (expectedMaterial != null && !expectedMaterial.matches(ModdedBlockState.serialize(state))) { + if (expectedMaterial != null && !expectedMaterial.matches(state.key())) { return null; } if (expectedMaterial == null @@ -123,19 +118,18 @@ public final class ModdedTreeFellerService implements ModdedTickableService { && !definitionIndex(engine).isTreeMarker(marker)) { return null; } - ItemStack tool = player.getInventory().getSelectedItem(); + NativeHarvestSession harvest = new NativeHarvestSession(level, player); return new PreparedOrigin( this, level, player, - position.immutable(), + position, state, engine, marker, minimumY, - level.getMaxY(), - player.getInventory().getSelectedSlot(), - tool.copy(), + level.maxHeight() - 1, + harvest, settings.getDurabilityPreservationChance() ); } @@ -163,7 +157,7 @@ public final class ModdedTreeFellerService implements ModdedTickableService { activeRuns.add(run); presentation.activate(prepared.position(), prepared.state()); OriginDropRoute route = presentation::route; - if (run.toolBroken) { + if (prepared.harvest().toolBroken()) { ModdedBlockBreakHandler.completeManagedBreak(prepared.level(), prepared.position()); finish(run); return route; @@ -179,48 +173,8 @@ public final class ModdedTreeFellerService implements ModdedTickableService { } private boolean normalizeOriginTool(FellingRun run) { - PreparedOrigin prepared = run.prepared; - ItemStack before = prepared.toolBefore(); - ItemStack current = prepared.player().getInventory().getItem(prepared.heldSlot()); - boolean currentMatches = !current.isEmpty() - && ItemStack.matchesIgnoringComponents( - before, - current, - (componentType) -> componentType == DataComponents.DAMAGE - ); - if (!before.isDamageableItem()) { - if (!currentMatches || !ItemStack.isSameItemSameComponents(before, current)) { - return false; - } - run.expectedTool = current.copy(); - return true; - } - - boolean preserve = ThreadLocalRandom.current().nextInt(100) < prepared.preservationChance(); - int desiredDamage = before.getDamageValue() + (preserve ? 0 : 1); - if (desiredDamage >= before.getMaxDamage()) { - if (!current.isEmpty() && !currentMatches) { - return false; - } - Item brokenItem = before.getItem(); - prepared.player().getInventory().setItem(prepared.heldSlot(), ItemStack.EMPTY); - if (!current.isEmpty()) { - prepared.player().onEquippedItemBroken(brokenItem, EquipmentSlot.MAINHAND); - } - prepared.player().inventoryMenu.sendAllDataToRemote(); - run.expectedTool = ItemStack.EMPTY; - run.toolBroken = true; - return true; - } - if (!current.isEmpty() && !currentMatches) { - return false; - } - ItemStack normalized = before.copy(); - normalized.setDamageValue(desiredDamage); - prepared.player().getInventory().setItem(prepared.heldSlot(), normalized); - prepared.player().inventoryMenu.sendAllDataToRemote(); - run.expectedTool = normalized.copy(); - return true; + return run.prepared.harvest().normalizeOriginTool( + ThreadLocalRandom.current().nextInt(100) < run.prepared.preservationChance()); } private void discover(FellingRun run) { @@ -308,13 +262,13 @@ public final class ModdedTreeFellerService implements ModdedTickableService { private MemberResult processMember(FellingRun run, TreeMarkerTraversal.Position position) { PreparedOrigin prepared = run.prepared; - BlockPos blockPosition = blockPosition(position); - ServerLevel level = prepared.level(); - if (!level.isLoaded(blockPosition)) { + NativeBlockPoint blockPosition = blockPosition(position); + NativeWorld level = prepared.level(); + if (!prepared.harvest().loaded(blockPosition)) { return MemberResult.STOP; } - BlockState state = level.getBlockState(blockPosition); - if (state.isAir()) { + NativeBlockState state = level.getBlock(blockPosition.x(), blockPosition.y(), blockPosition.z()); + if (NativeHarvestSession.air(state)) { ModdedBlockBreakHandler.completeManagedBreak(level, blockPosition); return MemberResult.CONTINUE; } @@ -322,56 +276,38 @@ public final class ModdedTreeFellerService implements ModdedTickableService { return MemberResult.STOP; } TreeBlockMaterial expectedMaterial = materialAt(prepared.engine(), prepared.minimumY(), blockPosition); - if (expectedMaterial != null && !expectedMaterial.matches(ModdedBlockState.serialize(state))) { + if (expectedMaterial != null && !expectedMaterial.matches(state.key())) { ModdedBlockBreakHandler.completeManagedBreak(level, blockPosition); - return state.is(BlockTags.LOGS) ? MemberResult.STOP : MemberResult.CONTINUE; + return NativeHarvestSession.log(state) ? MemberResult.STOP : MemberResult.CONTINUE; } - boolean log = state.is(BlockTags.LOGS); - ServerPlayer player = prepared.player(); - if (!level.mayInteract(player, blockPosition) - || player.blockActionRestricted(level, blockPosition, player.gameMode()) - || !player.getInventory().getSelectedItem().canDestroyBlock( - state, - level, - blockPosition, - player - )) { + boolean log = NativeHarvestSession.log(state); + NativeProtocolPlayer player = prepared.player(); + if (!prepared.harvest().mayDestroy(blockPosition, state)) { return log ? MemberResult.STOP : MemberResult.CONTINUE; } - if (!ModdedEngineBootstrap.loader().canTreeFellerBreak( - level, - player, - blockPosition, - state - )) { + if (!prepared.harvest().canBreak(ModdedEngineBootstrap.loader(), blockPosition, state)) { return log ? MemberResult.STOP : MemberResult.CONTINUE; } - if (!state.equals(level.getBlockState(blockPosition)) + if (!state.equals(level.getBlock(blockPosition.x(), blockPosition.y(), blockPosition.z())) || !prepared.marker().equals(markerAt(prepared.engine(), prepared.minimumY(), blockPosition))) { return MemberResult.STOP; } - ItemStack toolForDrops = prepared.player().getInventory().getSelectedItem().copy(); - BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(blockPosition) : null; - List vanillaDrops = Block.getDrops( - state, - level, - blockPosition, - blockEntity, - player, - toolForDrops - ); + List vanillaDrops = prepared.harvest().drops(blockPosition, state); ModdedBlockBreakHandler.Result customDrops = ModdedBlockBreakHandler.evaluateManagedDrops( level, blockPosition, state ); - ToolReservation reservation = log ? reserveToolDamage(run) : ToolReservation.free(toolForDrops); - if (reservation == null) { + NativeHarvestSession.Reservation reservation = log ? prepared.harvest().reserveToolDamage( + ThreadLocalRandom.current().nextInt(100) < prepared.preservationChance()) : null; + if (log && reservation == null) { return MemberResult.STOP; } - if (!level.destroyBlock(blockPosition, false, player, 512)) { - refundToolDamage(run, reservation); + if (!prepared.harvest().destroy(blockPosition)) { + if (reservation != null) { + prepared.harvest().refundToolDamage(reservation); + } return MemberResult.STOP; } ModdedBlockBreakHandler.completeManagedBreak(level, blockPosition); @@ -380,71 +316,15 @@ public final class ModdedTreeFellerService implements ModdedTickableService { float pitch = Math.min(1.95F, 0.65F + (progress * 1.25F)); run.presentation.erode(blockPosition, state, processed, run.effectStride, pitch); run.presentation.route(customDrops.combinedDrops(vanillaDrops)); - return reservation.broke() ? MemberResult.STOP : MemberResult.CONTINUE; - } - - private ToolReservation reserveToolDamage(FellingRun run) { - PreparedOrigin prepared = run.prepared; - ItemStack current = prepared.player().getInventory().getSelectedItem(); - if (!ItemStack.isSameItemSameComponents(current, run.expectedTool) || !current.is(ItemTags.AXES)) { - return null; - } - ItemStack before = current.copy(); - if (!current.isDamageableItem() - || ThreadLocalRandom.current().nextInt(100) < prepared.preservationChance()) { - return ToolReservation.free(before); - } - int nextDamage = current.getDamageValue() + 1; - if (nextDamage >= current.getMaxDamage()) { - Item brokenItem = current.getItem(); - prepared.player().getInventory().setSelectedItem(ItemStack.EMPTY); - prepared.player().onEquippedItemBroken(brokenItem, EquipmentSlot.MAINHAND); - prepared.player().inventoryMenu.sendAllDataToRemote(); - run.expectedTool = ItemStack.EMPTY; - return new ToolReservation(before, true, true); - } - current.setDamageValue(nextDamage); - prepared.player().getInventory().setSelectedItem(current); - prepared.player().inventoryMenu.sendAllDataToRemote(); - run.expectedTool = current.copy(); - return new ToolReservation(before, true, false); - } - - private void refundToolDamage(FellingRun run, ToolReservation reservation) { - if (!reservation.charged()) { - return; - } - PreparedOrigin prepared = run.prepared; - ItemStack current = prepared.player().getInventory().getSelectedItem(); - boolean expectedEmpty = run.expectedTool.isEmpty(); - if ((expectedEmpty && current.isEmpty()) - || (!expectedEmpty && ItemStack.isSameItemSameComponents(current, run.expectedTool))) { - ItemStack restored = reservation.before().copy(); - prepared.player().getInventory().setSelectedItem(restored); - prepared.player().inventoryMenu.sendAllDataToRemote(); - run.expectedTool = restored.copy(); - } + return reservation != null && reservation.broke() ? MemberResult.STOP : MemberResult.CONTINUE; } private boolean isRunControlActive(FellingRun run) { PreparedOrigin prepared = run.prepared; - ServerPlayer player = prepared.player(); + NativeHarvestSession harvest = prepared.harvest(); IrisSettings.IrisSettingsTreeFeller settings = IrisSettings.get().getTreeFeller(); - if (!enabled.get() - || settings == null - || !settings.isEnabled() - || run.finished.get() - || player.isRemoved() - || player.hasDisconnected() - || player.level() != prepared.level() - || !player.gameMode().isSurvival() - || !player.isShiftKeyDown() - || player.getInventory().getSelectedSlot() != prepared.heldSlot() - || run.expectedTool.isEmpty()) { - return false; - } - ItemStack current = player.getInventory().getSelectedItem(); - return current.is(ItemTags.AXES) && ItemStack.isSameItemSameComponents(current, run.expectedTool); + return enabled.get() && settings != null && settings.isEnabled() && !run.finished.get() + && harvest.active() && harvest.survival() && harvest.sneaking() && harvest.holdingAxe(); } private void finish(FellingRun run) { @@ -456,43 +336,42 @@ public final class ModdedTreeFellerService implements ModdedTickableService { } } - private String markerAt(Engine engine, int minimumY, BlockPos position) { - return markerAt(engine, minimumY, position.getX(), position.getY(), position.getZ()); + private String markerAt(Engine engine, int minimumY, NativeBlockPoint position) { + return markerAt(engine, minimumY, position.x(), position.y(), position.z()); } private String markerAt(Engine engine, int minimumY, int x, int y, int z) { return engine.getMantle().getMantle().get(x, y - minimumY, z, String.class); } - private TreeBlockMaterial materialAt(Engine engine, int minimumY, BlockPos position) { + private TreeBlockMaterial materialAt(Engine engine, int minimumY, NativeBlockPoint position) { return engine.getMantle().getMantle().get( - position.getX(), - position.getY() - minimumY, - position.getZ(), + position.x(), + position.y() - minimumY, + position.z(), TreeBlockMaterial.class ); } - private TreeMarkerTraversal.Position positionOf(BlockPos position) { - return new TreeMarkerTraversal.Position(position.getX(), position.getY(), position.getZ()); + private TreeMarkerTraversal.Position positionOf(NativeBlockPoint position) { + return new TreeMarkerTraversal.Position(position.x(), position.y(), position.z()); } - private BlockPos blockPosition(TreeMarkerTraversal.Position position) { - return new BlockPos(position.x(), position.y(), position.z()); + private NativeBlockPoint blockPosition(TreeMarkerTraversal.Position position) { + return new NativeBlockPoint(position.x(), position.y(), position.z()); } public record PreparedOrigin( ModdedTreeFellerService owner, - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState state, + NativeWorld level, + NativeProtocolPlayer player, + NativeBlockPoint position, + NativeBlockState state, Engine engine, String marker, int minimumY, int maximumY, - int heldSlot, - ItemStack toolBefore, + NativeHarvestSession harvest, int preservationChance ) { public PreparedOrigin { @@ -503,13 +382,13 @@ public final class ModdedTreeFellerService implements ModdedTickableService { Objects.requireNonNull(state, "state"); Objects.requireNonNull(engine, "engine"); Objects.requireNonNull(marker, "marker"); - Objects.requireNonNull(toolBefore, "toolBefore"); + Objects.requireNonNull(harvest, "harvest"); } } @FunctionalInterface public interface OriginDropRoute { - boolean route(Iterable drops); + boolean route(Iterable drops); } private enum MemberResult { @@ -517,13 +396,7 @@ public final class ModdedTreeFellerService implements ModdedTickableService { STOP } - private record TreeClaim(ServerLevel level, String marker) { - } - - private record ToolReservation(ItemStack before, boolean charged, boolean broke) { - private static ToolReservation free(ItemStack before) { - return new ToolReservation(before, false, false); - } + private record TreeClaim(NativeWorld level, String marker) { } private static final class FellingRun { @@ -531,13 +404,11 @@ public final class ModdedTreeFellerService implements ModdedTickableService { private final PreparedOrigin prepared; private final ModdedTreeFellerPresentation presentation; private final AtomicBoolean finished = new AtomicBoolean(); - private ItemStack expectedTool = ItemStack.EMPTY; private List work = List.of(); private int cursor; private int processed; private int blocksPerPulse = 1; private int effectStride = 1; - private boolean toolBroken; private FellingRun( TreeClaim claim, diff --git a/adapters/modded-common/src/main/resources/irisworldgen.client.mixins.json b/adapters/modded-common/src/main/resources/irisworldgen.client.mixins.json deleted file mode 100644 index 5140fdc9e..000000000 --- a/adapters/modded-common/src/main/resources/irisworldgen.client.mixins.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "art.arcane.iris.client.mixin", - "compatibilityLevel": "JAVA_21", - "client": [ - "IrisWorldOpenFlowsMixin", - "IrisWorldTypeEntryMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} diff --git a/adapters/modded-common/src/main/resources/irisworldgen.entity.mixins.json b/adapters/modded-common/src/main/resources/irisworldgen.entity.mixins.json deleted file mode 100644 index 89973faef..000000000 --- a/adapters/modded-common/src/main/resources/irisworldgen.entity.mixins.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "art.arcane.iris.modded.mixin", - "compatibilityLevel": "JAVA_21", - "mixins": [ - "WorldGenerationWriteGuardMixin", - "ChunkMapTerrainReceiptAccess", - "ChunkTerrainReceiptMixin", - "LevelChunkTerrainReceiptMixin", - "SerializableTerrainReceiptMixin", - "EntityPersistenceMixin", - "LivingEntityLootMixin", - "MobAwarenessMixin", - "StructureTemplatePaletteConcurrencyMixin" - ], - "injectors": { - "defaultRequire": 1 - } -} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedBlockEntityParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedBlockEntityParityTest.java index 5292c0c50..da6924232 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedBlockEntityParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedBlockEntityParityTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeContainerLoot; + import art.arcane.volmlib.util.math.RNG; import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; @@ -66,13 +70,13 @@ public class IrisModdedBlockEntityParityTest { BlockPos position = new BlockPos(3, 70, 5); chunk.setBlockState(position, Blocks.CHEST.defaultBlockState(), 0); - IrisModdedChunkGenerator.createDefaultBlockEntity(chunk, position, Blocks.CHEST.defaultBlockState()); + NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, position, Blocks.CHEST.defaultBlockState()); BlockEntity blockEntity = chunk.getBlockEntity(position); assertNotNull(blockEntity); assertTrue(blockEntity instanceof Container); Container container = (Container) blockEntity; - ModdedLootApplier.fillContainer(container, List.of(new ItemStack(Items.DIAMOND)), new RNG(17L)); + NativeContainerLoot.fillContainer(container, List.of(new ItemStack(Items.DIAMOND)), new RNG(17L), message -> {}); assertEquals(1, countItem(container, Items.DIAMOND)); CompoundTag saved = blockEntity.saveWithoutMetadata(registries); ChestBlockEntity restored = new ChestBlockEntity(position, Blocks.CHEST.defaultBlockState()); @@ -94,9 +98,9 @@ public class IrisModdedBlockEntityParityTest { chunk.setBlockState(signPosition, signState, 0); chunk.setBlockState(spawnerPosition, spawnerState, 0); chunk.setBlockState(bannerPosition, bannerState, 0); - IrisModdedChunkGenerator.createDefaultBlockEntity(chunk, signPosition, signState); - IrisModdedChunkGenerator.createDefaultBlockEntity(chunk, spawnerPosition, spawnerState); - IrisModdedChunkGenerator.createDefaultBlockEntity(chunk, bannerPosition, bannerState); + NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, signPosition, signState); + NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, spawnerPosition, spawnerState); + NativeModdedChunkGenerator.createDefaultBlockEntity(chunk, bannerPosition, bannerState); assertTrue(chunk.getBlockEntity(signPosition) instanceof SignBlockEntity); assertTrue(chunk.getBlockEntity(spawnerPosition) instanceof SpawnerBlockEntity); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java index 9b2c34c5f..a908898f3 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnTableMerger; + import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraft.util.random.Weighted; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedHeightmapParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedHeightmapParityTest.java index cd6099ad1..65bd061e7 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedHeightmapParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedHeightmapParityTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedHeightmaps; + import net.minecraft.util.Mth; import net.minecraft.util.SimpleBitStorage; import org.junit.Test; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedLocateRoutingTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedLocateRoutingTest.java index 144bd44be..b49327e83 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedLocateRoutingTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedLocateRoutingTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.modded; -import art.arcane.iris.nativegen.NativeStructureLocateResults; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureLocateResults; import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedStructureParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedStructureParityTest.java index 0e6438ae3..6d88cb54f 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedStructureParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedStructureParityTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.mantle.flag.MantleFlag; import net.minecraft.SharedConstants; -import net.minecraft.core.BlockPos; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import net.minecraft.server.Bootstrap; import org.junit.BeforeClass; import org.junit.Test; @@ -40,31 +40,31 @@ public class IrisModdedStructureParityTest { @Test public void surfaceBiomeFastPathBeginsAboveTheCaveSwitch() { - assertFalse(IrisModdedBiomeSource.isGuaranteedSurfaceBiome(-2, -256)); - assertTrue(IrisModdedBiomeSource.isGuaranteedSurfaceBiome(-1, -256)); - assertFalse(IrisModdedBiomeSource.isGuaranteedSurfaceBiome(10, 0)); - assertTrue(IrisModdedBiomeSource.isGuaranteedSurfaceBiome(11, 0)); + assertFalse(ModdedBiomePolicy.isGuaranteedSurfaceBiome(-2, -256)); + assertTrue(ModdedBiomePolicy.isGuaranteedSurfaceBiome(-1, -256)); + assertFalse(ModdedBiomePolicy.isGuaranteedSurfaceBiome(10, 0)); + assertTrue(ModdedBiomePolicy.isGuaranteedSurfaceBiome(11, 0)); } @Test public void visibleCaveBiomeBeginsEightBlocksBelowTheSurface() { - assertFalse(IrisModdedBiomeSource.isUnderground(93, 100)); - assertTrue(IrisModdedBiomeSource.isUnderground(92, 100)); - assertTrue(IrisModdedBiomeSource.isUnderground(-20, 100)); + assertFalse(ModdedBiomePolicy.isUnderground(93, 100)); + assertTrue(ModdedBiomePolicy.isUnderground(92, 100)); + assertTrue(ModdedBiomePolicy.isUnderground(-20, 100)); } @Test public void monumentBiomeCubeUsesSurfaceBiomesAtShiftedSeaLevel() { - assertTrue(IrisModdedBiomeSource.isMonumentSurfaceBiomeQuery(50, 29, -256, 306)); - assertFalse(IrisModdedBiomeSource.isMonumentSurfaceBiomeQuery(51, 29, -256, 306)); - assertFalse(IrisModdedBiomeSource.isMonumentSurfaceBiomeQuery(50, 28, -256, 306)); + assertTrue(ModdedBiomePolicy.isMonumentSurfaceBiomeQuery(50, 29, -256, 306)); + assertFalse(ModdedBiomePolicy.isMonumentSurfaceBiomeQuery(51, 29, -256, 306)); + assertFalse(ModdedBiomePolicy.isMonumentSurfaceBiomeQuery(50, 28, -256, 306)); } @Test public void strongholdRingSearchSamplesOneQuartColumnPerChunk() { - assertEquals(4, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 112)); - assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(1, 112)); - assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 111)); + assertEquals(4, ModdedBiomePolicy.horizontalBiomeSearchQuartStep(0, 112)); + assertEquals(1, ModdedBiomePolicy.horizontalBiomeSearchQuartStep(1, 112)); + assertEquals(1, ModdedBiomePolicy.horizontalBiomeSearchQuartStep(0, 111)); } @Test @@ -86,9 +86,9 @@ public class IrisModdedStructureParityTest { @Test public void reconciledSpawnUsesOriginAndClampedSurfaceHeight() { - assertEquals(new BlockPos(0, 73, 0), ModdedEngineBootstrap.reconciledSpawnPosition(73, -64, 384)); - assertEquals(new BlockPos(0, -63, 0), ModdedEngineBootstrap.reconciledSpawnPosition(-100, -64, 384)); - assertEquals(new BlockPos(0, 318, 0), ModdedEngineBootstrap.reconciledSpawnPosition(400, -64, 384)); + assertEquals(new NativeBlockPoint(0, 73, 0), ModdedEngineBootstrap.reconciledSpawnPosition(73, -64, 384)); + assertEquals(new NativeBlockPoint(0, -63, 0), ModdedEngineBootstrap.reconciledSpawnPosition(-100, -64, 384)); + assertEquals(new NativeBlockPoint(0, 318, 0), ModdedEngineBootstrap.reconciledSpawnPosition(400, -64, 384)); } @Test @@ -102,7 +102,7 @@ public class IrisModdedStructureParityTest { ^ ((long) blockY * 132897987541L) ^ ((long) blockZ * 42317861L); - assertEquals(expected, IrisModdedBiomeSource.biomeResolutionSeed(worldSeed, blockX, blockY, blockZ)); + assertEquals(expected, ModdedBiomePolicy.biomeResolutionSeed(worldSeed, blockX, blockY, blockZ)); } @Test @@ -114,19 +114,19 @@ public class IrisModdedStructureParityTest { assertEquals( ModdedWorldgenIds.biomeRef("pack", "Upper", "Aurora"), - IrisModdedBiomeSource.customBiomeRef("pack", upper, "Aurora") + ModdedBiomePolicy.customBiomeRef("pack", upper, "Aurora") ); - assertFalse(IrisModdedBiomeSource.customBiomeRef("pack", host, "Aurora") - .equals(IrisModdedBiomeSource.customBiomeRef("pack", upper, "Aurora"))); + assertFalse(ModdedBiomePolicy.customBiomeRef("pack", host, "Aurora") + .equals(ModdedBiomePolicy.customBiomeRef("pack", upper, "Aurora"))); } @Test public void temporaryNaturalBiomeAnswersAreNotMemoized() { Engine engine = mock(Engine.class); when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(true); - assertFalse(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8)); + assertFalse(ModdedBiomePolicy.isBiomeCacheable(engine, 12, -8)); when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(false); - assertTrue(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8)); + assertTrue(ModdedBiomePolicy.isBiomeCacheable(engine, 12, -8)); } @Test @@ -162,16 +162,16 @@ public class IrisModdedStructureParityTest { public void structureStateRejectsBiomesOutsideThePackContract() { Set generated = Set.of("minecraft:deep_ocean", "minecraft:dark_forest"); - assertTrue(IrisModdedBiomeSource.isGeneratedBiomeKey("minecraft:deep_ocean", generated)); - assertTrue(IrisModdedBiomeSource.isGeneratedBiomeKey("MINECRAFT:DARK_FOREST", generated)); - assertFalse(IrisModdedBiomeSource.isGeneratedBiomeKey("minecraft:desert", generated)); - assertFalse(IrisModdedBiomeSource.isGeneratedBiomeKey(null, generated)); + assertTrue(ModdedBiomePolicy.isGeneratedBiomeKey("minecraft:deep_ocean", generated)); + assertTrue(ModdedBiomePolicy.isGeneratedBiomeKey("MINECRAFT:DARK_FOREST", generated)); + assertFalse(ModdedBiomePolicy.isGeneratedBiomeKey("minecraft:desert", generated)); + assertFalse(ModdedBiomePolicy.isGeneratedBiomeKey(null, generated)); } @Test public void structureBiomeContractRejectsAnEmptyConfiguredSet() { IllegalStateException error = assertThrows(IllegalStateException.class, - () -> IrisModdedBiomeSource.requireConfiguredStructureBiomeKeys(Set.of())); + () -> ModdedBiomePolicy.requireConfiguredStructureBiomeKeys(Set.of())); assertEquals("Iris has no configured structure biomes", error.getMessage()); } @@ -180,7 +180,7 @@ public class IrisModdedStructureParityTest { public void structureBiomeContractPreservesConfiguredKeysDuringBootstrap() { Set configured = Set.of("minecraft:deep_ocean", "minecraft:dark_forest"); - assertSame(configured, IrisModdedBiomeSource.requireConfiguredStructureBiomeKeys(configured)); + assertSame(configured, ModdedBiomePolicy.requireConfiguredStructureBiomeKeys(configured)); } @Test diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java new file mode 100644 index 000000000..d590d36ff --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java @@ -0,0 +1,103 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class ModdedBiomePolicyTest { + @Test + public void registryOrderAndHolderIdentitySurviveThePolicyBoundaryAndRefreshOnRepoint() { + BiomeToken ocean = new BiomeToken("minecraft:ocean"); + BiomeToken forest = new BiomeToken("minecraft:forest"); + List entries = new ArrayList<>(List.of(forest, ocean)); + AtomicReference> configured = new AtomicReference<>(Set.of(ocean.key(), forest.key())); + ModdedBiomePolicy policy = policy(entries, configured); + + List first = policy.orderedPossibleBiomes(); + assertSame(forest, first.getFirst()); + assertSame(ocean, first.getLast()); + assertEquals(first, new ArrayList<>(policy.possibleBiomes())); + + BiomeToken desert = new BiomeToken("minecraft:desert"); + entries.clear(); + entries.add(desert); + configured.set(Set.of(desert.key())); + assertSame(first, policy.orderedPossibleBiomes()); + + policy.clearCaches(); + + assertEquals(1L, policy.packGeneration()); + assertEquals(1, policy.orderedPossibleBiomes().size()); + assertSame(desert, policy.orderedPossibleBiomes().getFirst()); + } + + @Test + public void missingRequiredStructureBiomesFailBeforePublishingAPartialSet() { + AtomicReference> configured = new AtomicReference<>(Set.of("example:missing")); + ModdedBiomePolicy policy = policy( + new ArrayList<>(List.of(new BiomeToken("minecraft:plains"))), configured); + + IllegalStateException failure = assertThrows(IllegalStateException.class, policy::possibleBiomes); + + assertTrue(failure.getMessage().contains("example:missing")); + } + + private static ModdedBiomePolicy policy(List entries, + AtomicReference> configured) { + ModdedBiomePolicy policy = new ModdedBiomePolicy<>(new Access(entries)); + policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> null, () -> null, engine -> false, + configured::get, engine -> Set.of(), () -> 0L)); + return policy; + } + + private record BiomeToken(String key) { + } + + private record Access(List entries) implements NativeBiomeSourceAccess, + NativeBiomeSourceAccess.RegistryView { + @Override + public RegistryView registry() { + return this; + } + + @Override + public Set serializedBiomes() { + throw new AssertionError("A loaded registry must not use serialized biomes"); + } + + @Override + public BiomeToken serializedNoise(int x, int y, int z, Object sampler) { + throw new AssertionError("Listing biome holders must not sample terrain"); + } + + @Override + public String holderKey(BiomeToken holder) { + return holder.key(); + } + + @Override + public void forEach(Consumer consumer) { + entries.forEach(consumer); + } + + @Override + public BiomeToken lookup(String key) { + for (BiomeToken biome : entries) { + if (biome.key().equals(key)) { + return biome; + } + } + return null; + } + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomeWriterCacheTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomeWriterCacheTest.java index c4ea59f7e..a49699201 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomeWriterCacheTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomeWriterCacheTest.java @@ -1,6 +1,7 @@ package art.arcane.iris.modded; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBiomeRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import org.junit.Test; import java.util.List; @@ -12,7 +13,7 @@ import static org.junit.Assert.assertTrue; public class ModdedBiomeWriterCacheTest { @Test public void unavailableServerFallsBackToBiomeIdZero() { - ModdedBiomeWriter writer = new ModdedBiomeWriter(() -> null); + ModdedBiomeWriter writer = new ModdedBiomeWriter(new NativeBiomeRegistry(() -> null, "minecraft:plains")); assertEquals(0, writer.biomeIdFor("minecraft:plains")); assertEquals(0, writer.biomeIdFor("minecraft:plains")); @@ -20,10 +21,10 @@ public class ModdedBiomeWriterCacheTest { @Test public void unavailableServerYieldsAnEmptyMutableBiomeList() { - ModdedBiomeWriter writer = new ModdedBiomeWriter(() -> null); + ModdedBiomeWriter writer = new ModdedBiomeWriter(new NativeBiomeRegistry(() -> null, "minecraft:plains")); - List first = writer.allBiomes(); - List second = writer.allBiomes(); + List first = writer.allBiomes(); + List second = writer.allBiomes(); assertTrue(first.isEmpty()); assertNotSame("callers must never share the writer cache instance", first, second); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockBreakHandlerTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockBreakHandlerTest.java index 36569bced..4f618941c 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockBreakHandlerTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockBreakHandlerTest.java @@ -19,23 +19,33 @@ package art.arcane.iris.modded; import net.minecraft.core.Direction; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.StairBlock; import net.minecraft.world.level.block.state.BlockState; import org.junit.Test; +import org.junit.BeforeClass; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ModdedBlockBreakHandlerTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + @Test public void exactMatchingIncludesPropertiesWhileTypeMatchingDoesNot() { - BlockState north = Blocks.OAK_STAIRS.defaultBlockState().setValue(StairBlock.FACING, Direction.NORTH); - BlockState east = Blocks.OAK_STAIRS.defaultBlockState().setValue(StairBlock.FACING, Direction.EAST); + ModdedBlockState north = ModdedBlockState.of(Blocks.OAK_STAIRS.defaultBlockState().setValue(StairBlock.FACING, Direction.NORTH), null); + ModdedBlockState east = ModdedBlockState.of(Blocks.OAK_STAIRS.defaultBlockState().setValue(StairBlock.FACING, Direction.EAST), null); assertTrue(ModdedBlockBreakHandler.matchesState(north, north, true)); assertFalse(ModdedBlockBreakHandler.matchesState(north, east, true)); assertTrue(ModdedBlockBreakHandler.matchesState(north, east, false)); - assertFalse(ModdedBlockBreakHandler.matchesState(north, Blocks.COBBLESTONE.defaultBlockState(), false)); + assertFalse(ModdedBlockBreakHandler.matchesState(north, ModdedBlockState.of(Blocks.COBBLESTONE.defaultBlockState(), null), false)); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockResolutionContractTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockResolutionContractTest.java index da933df39..2ca05ff4a 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockResolutionContractTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBlockResolutionContractTest.java @@ -1,8 +1,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockProperties; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.generation.decoration.DecoratorPlatformHooks; import art.arcane.iris.generation.decoration.IrisSpeleothems; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import net.minecraft.SharedConstants; @@ -35,38 +37,38 @@ public class ModdedBlockResolutionContractTest { @Test public void getOrNullReturnsNullForUnknownKey() { - assertNull(ModdedBlockResolution.getOrNull(UNKNOWN)); - assertNull(ModdedBlockResolution.getOrNull(UNKNOWN, true)); + assertNull(ModdedBlockResolution.BLOCKS.getOrNull(UNKNOWN)); + assertNull(ModdedBlockResolution.BLOCKS.getOrNull(UNKNOWN, true)); } @Test public void getFallsBackToAirForUnknownKey() { - ModdedBlockState state = ModdedBlockResolution.get(UNKNOWN); + ModdedBlockState state = ModdedBlockResolution.BLOCKS.get(UNKNOWN); assertNotNull(state); assertEquals(Blocks.AIR, state.handle().getBlock()); } @Test public void getOrNullResolvesKnownKeyWithProperties() { - ModdedBlockState state = ModdedBlockResolution.getOrNull("minecraft:oak_log[axis=x]", true); + ModdedBlockState state = ModdedBlockResolution.BLOCKS.getOrNull("minecraft:oak_log[axis=x]", true); assertNotNull(state); assertEquals(Blocks.OAK_LOG, state.handle().getBlock()); } @Test public void unknownPropertyFallsBackToDefaultState() { - ModdedBlockState state = ModdedBlockResolution.getOrNull("minecraft:oak_log[not_a_property=x]", true); + ModdedBlockState state = ModdedBlockResolution.BLOCKS.getOrNull("minecraft:oak_log[not_a_property=x]", true); assertNotNull(state); assertEquals(Blocks.OAK_LOG, state.handle().getBlock()); } @Test public void cactusPlacementRequiresNativeCactusSupport() { - assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.CACTUS)); - assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.SAND)); - assertTrue(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.RED_SAND)); - assertFalse(ModdedBlockResolution.canPlaceOnto(Blocks.CACTUS, Blocks.STONE)); - assertTrue(ModdedBlockResolution.isDecorant(Blocks.CACTUS.defaultBlockState())); + assertTrue(NativeBlockProperties.canPlaceOnto(Blocks.CACTUS, Blocks.CACTUS)); + assertTrue(NativeBlockProperties.canPlaceOnto(Blocks.CACTUS, Blocks.SAND)); + assertTrue(NativeBlockProperties.canPlaceOnto(Blocks.CACTUS, Blocks.RED_SAND)); + assertFalse(NativeBlockProperties.canPlaceOnto(Blocks.CACTUS, Blocks.STONE)); + assertTrue(NativeBlockProperties.isDecorant(Blocks.CACTUS.defaultBlockState())); } @Test @@ -74,11 +76,11 @@ public class ModdedBlockResolutionContractTest { for (Block block : new Block[]{Blocks.SUGAR_CANE, Blocks.GRASS_BLOCK, Blocks.DIRT, Blocks.COARSE_DIRT, Blocks.PODZOL, Blocks.MYCELIUM, Blocks.ROOTED_DIRT, Blocks.MOSS_BLOCK, Blocks.PALE_MOSS_BLOCK, Blocks.MUD, Blocks.MUDDY_MANGROVE_ROOTS, Blocks.SAND, Blocks.RED_SAND, Blocks.SUSPICIOUS_SAND}) { - assertTrue(block.toString(), ModdedBlockResolution.canPlaceOnto(Blocks.SUGAR_CANE, block)); + assertTrue(block.toString(), NativeBlockProperties.canPlaceOnto(Blocks.SUGAR_CANE, block)); } for (Block block : new Block[]{Blocks.STONE, Blocks.GRAVEL, Blocks.CLAY, Blocks.DIRT_PATH, Blocks.FARMLAND, Blocks.AIR, Blocks.WATER}) { - assertFalse(block.toString(), ModdedBlockResolution.canPlaceOnto(Blocks.SUGAR_CANE, block)); + assertFalse(block.toString(), NativeBlockProperties.canPlaceOnto(Blocks.SUGAR_CANE, block)); } } @@ -87,11 +89,11 @@ public class ModdedBlockResolutionContractTest { for (Block block : new Block[]{Blocks.SULFUR_SPIKE, Blocks.POINTED_DRIPSTONE}) { for (SpeleothemThickness thickness : SpeleothemThickness.values()) { assertEquals(thickness == SpeleothemThickness.TIP, - ModdedBlockResolution.isUpdatable(block.defaultBlockState() + NativeBlockProperties.isUpdatable(block.defaultBlockState() .setValue(SpeleothemBlock.THICKNESS, thickness))); } } - assertFalse(ModdedBlockResolution.isUpdatable(Blocks.STONE.defaultBlockState())); + assertFalse(NativeBlockProperties.isUpdatable(Blocks.STONE.defaultBlockState())); } @Test public void nativeSpikesUseModdedSupportFaces() { @@ -100,14 +102,14 @@ public class ModdedBlockResolutionContractTest { try { for (String material : new String[]{"minecraft:sulfur_spike", "minecraft:pointed_dripstone"}) { for (boolean upward : new boolean[]{true, false}) { - Hunk output = Hunk.newArrayHunk(1, 3, 1); - PlatformBlockState spike = ModdedBlockResolution.get(material + Hunk output = Hunk.newArrayHunk(1, 3, 1); + NativeBlockState spike = ModdedBlockResolution.BLOCKS.get(material + "[vertical_direction=" + (upward ? "up" : "down") + ",thickness=tip]"); int supportY = upward ? 0 : 2; output.set(0, 1, 0, spike); - output.set(0, supportY, 0, ModdedBlockResolution.get("minecraft:stone_slab[type=top]")); + output.set(0, supportY, 0, ModdedBlockResolution.BLOCKS.get("minecraft:stone_slab[type=top]")); assertEquals(upward, IrisSpeleothems.isSupported(spike, output, 0, 0, 1)); - output.set(0, supportY, 0, ModdedBlockResolution.get("minecraft:stone_slab[type=bottom]")); + output.set(0, supportY, 0, ModdedBlockResolution.BLOCKS.get("minecraft:stone_slab[type=bottom]")); assertEquals(!upward, IrisSpeleothems.isSupported(spike, output, 0, 0, 1)); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedClientPackageIsolationTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedClientPackageIsolationTest.java index 8b4801420..c4f354f1f 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedClientPackageIsolationTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedClientPackageIsolationTest.java @@ -32,6 +32,7 @@ import static org.junit.Assert.assertTrue; */ public class ModdedClientPackageIsolationTest { private static final String CLIENT_MINECRAFT = "net/minecraft/client/"; + private static final String CLIENT_NATIVE = "art/arcane/volmlib/nativelib/minecraft26_2/client/"; private static final String CLIENT_IRIS = "art/arcane/iris/client/"; private static final List GUARDED_PACKAGES = List.of( "art/arcane/iris/modded", @@ -56,6 +57,9 @@ public class ModdedClientPackageIsolationTest { if (bytecode.contains(CLIENT_MINECRAFT)) { violations.add(name + " -> " + CLIENT_MINECRAFT); } + if (bytecode.contains(CLIENT_NATIVE)) { + violations.add(name + " -> " + CLIENT_NATIVE); + } if (bytecode.contains(CLIENT_IRIS)) { violations.add(name + " -> " + CLIENT_IRIS); } @@ -74,8 +78,8 @@ public class ModdedClientPackageIsolationTest { */ @Test public void scanDetectsClientReferencesInAKnownClientClass() throws IOException, URISyntaxException { - Path visionScreen = classesRoot().resolve("art/arcane/iris/client/IrisVisionScreen.class"); - assertTrue("IrisVisionScreen.class missing from " + visionScreen, Files.isRegularFile(visionScreen)); + Path visionScreen = classesRoot().resolve("art/arcane/volmlib/nativelib/minecraft26_2/client/NativeClientScreen.class"); + assertTrue("NativeClientScreen.class missing from " + visionScreen, Files.isRegularFile(visionScreen)); assertTrue("scan needle no longer matches a known client class", readAsLatin1(visionScreen).contains(CLIENT_MINECRAFT)); } @@ -89,7 +93,7 @@ public class ModdedClientPackageIsolationTest { } private static Path classesRoot() throws URISyntaxException { - String anchor = "/art/arcane/iris/modded/ModdedMixinFlags.class"; + String anchor = "/art/arcane/iris/modded/ModdedMixinAudit.class"; URL located = ModdedClientPackageIsolationTest.class.getResource(anchor); assertNotNull("compiled main classes are not on the test classpath as files", located); assertEquals("expected a directory classpath entry, got " + located, "file", located.getProtocol()); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeathLootTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeathLootTest.java new file mode 100644 index 000000000..737e24fa9 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeathLootTest.java @@ -0,0 +1,37 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityLoot; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import org.junit.Test; + +import java.util.Set; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ModdedDeathLootTest { + @Test + public void unboundEntityRetainsItsNativeBaseLoot() { + NativeEntityLoot entity = mock(NativeEntityLoot.class); + when(entity.world()).thenReturn(mock(NativeWorld.class)); + when(entity.tags()).thenReturn(Set.of("unrelated")); + assertFalse(ModdedDeathLoot.replaceBaseLoot(entity)); + } + + @Test + public void malformedBindingStillSuppressesNativeBaseLoot() { + NativeEntityLoot entity = mock(NativeEntityLoot.class); + when(entity.world()).thenReturn(mock(NativeWorld.class)); + when(entity.tags()).thenReturn(Set.of("iris_loot|invalid")); + assertTrue(ModdedDeathLoot.replaceBaseLoot(entity)); + } + + @Test + public void entityOutsideServerWorldRetainsNativeBaseLoot() { + NativeEntityLoot entity = mock(NativeEntityLoot.class); + assertFalse(ModdedDeathLoot.replaceBaseLoot(entity)); + assertFalse(ModdedDeathLoot.replaceBaseLoot(null)); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeferredBlockParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeferredBlockParityTest.java index 943706fe6..3e4fbb078 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeferredBlockParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDeferredBlockParityTest.java @@ -1,12 +1,12 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.modded.api.ModdedBlockData; import art.arcane.iris.modded.api.ModdedCustomContentRegistry; import art.arcane.iris.modded.api.ModdedDataProvider; import art.arcane.iris.modded.api.ModdedDataType; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import net.minecraft.SharedConstants; -import net.minecraft.resources.Identifier; import net.minecraft.server.Bootstrap; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.StairBlock; @@ -23,7 +23,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class ModdedDeferredBlockParityTest { - private static final Identifier BLOCK_ID = Identifier.parse("iris_deferred_test:oak_stairs"); + private static final String BLOCK_ID = "iris_deferred_test:oak_stairs"; @BeforeClass public static void bootstrapMinecraftRegistries() { @@ -34,7 +34,7 @@ public class ModdedDeferredBlockParityTest { @Test public void deferredProviderStateCarriesPlacementMetadataThroughMutation() { - ModdedBlockState resolved = ModdedBlockResolution.getOrNull( + ModdedBlockState resolved = ModdedBlockResolution.BLOCKS.getOrNull( "iris_deferred_test:oak_stairs[facing=north,half=bottom]"); assertNotNull(resolved); @@ -42,12 +42,12 @@ public class ModdedDeferredBlockParityTest { assertEquals("iris_deferred_test:oak_stairs[facing=north,half=bottom]", resolved.deferredPlacementKey()); assertEquals(Blocks.OAK_STAIRS, resolved.handle().getBlock()); - PlatformBlockState mutated = resolved.withProperty("facing", "west"); + NativeBlockState mutated = resolved.withProperty("facing", "west"); assertTrue(mutated.isCustom()); assertEquals(resolved.deferredPlacementKey(), mutated.deferredPlacementKey()); assertEquals("west", ((ModdedBlockState) mutated).handle().getValue(StairBlock.FACING).getName()); - PlatformBlockState base = mutated.placementBaseState(); + NativeBlockState base = mutated.placementBaseState(); assertFalse(base.isCustom()); assertEquals(Blocks.OAK_STAIRS, ((ModdedBlockState) base).handle().getBlock()); } @@ -59,18 +59,18 @@ public class ModdedDeferredBlockParityTest { } @Override - public Collection getTypes(ModdedDataType type) { + public Collection getTypes(ModdedDataType type) { return type == ModdedDataType.BLOCK ? List.of(BLOCK_ID) : List.of(); } @Override - public boolean isValidProvider(Identifier id, ModdedDataType type) { + public boolean isValidProvider(String id, ModdedDataType type) { return type == ModdedDataType.BLOCK && BLOCK_ID.equals(id); } @Override - public ModdedBlockData getBlockData(Identifier blockId, Map state) { - return ModdedBlockData.deferred(Blocks.OAK_STAIRS.defaultBlockState()); + public ModdedBlockData getBlockData(String blockId, Map state) { + return ModdedBlockData.deferred(ModdedBlockState.of(Blocks.OAK_STAIRS.defaultBlockState(), null)); } } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java index cd891a307..1adc60d53 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java @@ -18,8 +18,10 @@ package art.arcane.iris.modded; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldInspection.Dimension; + +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; @@ -152,8 +154,8 @@ public class ModdedDimensionTypeParityTest { .ceiling(TRUE); IrisDimension dimension = dimension("runtime_contract", IrisEnvironment.CUSTOM, -128, 384, 384, options); - WorldCheckDimensionContract.DimensionContract expected = WorldCheckDimensionContract.expectedDimensionContract(dimension); - WorldCheckDimensionContract.DimensionContract fallback = new WorldCheckDimensionContract.DimensionContract( + Dimension expected = WorldCheckDimensionContract.expectedDimensionContract(dimension); + Dimension fallback = new Dimension( -256, 768, 512, 1D, 0F, true, false, false, 0); assertTrue(WorldCheckDimensionContract.matchesDimensionContract(-128, 512, expected, expected)); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityAwarenessTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityAwarenessTest.java index 547c020d9..64a722892 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityAwarenessTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityAwarenessTest.java @@ -1,6 +1,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityBehavior; + import org.junit.Test; +import org.junit.Before; +import art.arcane.volmlib.nativelib.modded.EntityBehaviorTags; import java.util.HashSet; import java.util.Set; @@ -9,27 +13,32 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ModdedEntityAwarenessTest { + @Before + public void configureEntityTags() { + NativeEntityBehavior.bind(new EntityBehaviorTags("iris_non_persistent", "iris_unaware")); + } + @Test public void unawareConfigurationAddsThePartialAiTag() { Set tags = new HashSet<>(); - ModdedEntityAwareness.configureTags(tags, false); + NativeEntityBehavior.configureAwarenessTags(tags, false); - assertFalse(ModdedEntityAwareness.isAware(tags)); + assertFalse(NativeEntityBehavior.isAware(tags)); } @Test public void awareConfigurationRemovesThePartialAiTag() { Set tags = new HashSet<>(); - ModdedEntityAwareness.configureTags(tags, false); + NativeEntityBehavior.configureAwarenessTags(tags, false); - ModdedEntityAwareness.configureTags(tags, true); + NativeEntityBehavior.configureAwarenessTags(tags, true); - assertTrue(ModdedEntityAwareness.isAware(tags)); + assertTrue(NativeEntityBehavior.isAware(tags)); } @Test public void mobsAreAwareWithoutAnIrisTag() { - assertTrue(ModdedEntityAwareness.isAware(Set.of())); + assertTrue(NativeEntityBehavior.isAware(Set.of())); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityPersistenceTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityPersistenceTest.java index 94b0dea05..9dc246c4a 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityPersistenceTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntityPersistenceTest.java @@ -1,6 +1,10 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityBehavior; + import org.junit.Test; +import org.junit.Before; +import art.arcane.volmlib.nativelib.modded.EntityBehaviorTags; import java.util.HashSet; import java.util.Set; @@ -9,31 +13,36 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class ModdedEntityPersistenceTest { + @Before + public void configureEntityTags() { + NativeEntityBehavior.bind(new EntityBehaviorTags("iris_non_persistent", "iris_unaware")); + } + @Test public void generatedNonPersistentEntityIsExcludedFromVanillaSaves() { Set tags = new HashSet<>(); - ModdedEntityPersistence.configureTags(tags, false); + NativeEntityBehavior.configurePersistenceTags(tags, false); - assertFalse(ModdedEntityPersistence.shouldSave(tags, true)); + assertFalse(NativeEntityBehavior.shouldSave(tags, true)); } @Test public void positivePersistenceRemovesTheSaveExclusion() { Set tags = new HashSet<>(); - ModdedEntityPersistence.configureTags(tags, false); + NativeEntityBehavior.configurePersistenceTags(tags, false); - ModdedEntityPersistence.configureTags(tags, true); + NativeEntityBehavior.configurePersistenceTags(tags, true); - assertTrue(ModdedEntityPersistence.shouldSave(tags, true)); + assertTrue(NativeEntityBehavior.shouldSave(tags, true)); } @Test public void interceptionNeverOverridesVanillaSaveRejection() { Set tags = new HashSet<>(); - ModdedEntityPersistence.configureTags(tags, true); + NativeEntityBehavior.configurePersistenceTags(tags, true); - assertFalse(ModdedEntityPersistence.shouldSave(tags, false)); + assertFalse(NativeEntityBehavior.shouldSave(tags, false)); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnDifficultyTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnDifficultyTest.java index f643e4aac..f829ef17b 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnDifficultyTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnDifficultyTest.java @@ -1,5 +1,12 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntitySpawns; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.world.entity.IrisEntity; import art.arcane.volmlib.util.math.RNG; @@ -46,7 +53,7 @@ public class ModdedEntitySpawnDifficultyTest { public void sharedPlatformSpawnReturnsFalseForPeacefulHostiles() { ServerLevel level = mock(ServerLevel.class); when(level.getDifficulty()).thenReturn(Difficulty.PEACEFUL); - ModdedPlatform platform = new ModdedPlatform(mock(ModdedLoader.class)); + ModdedPlatform platform = new ModdedPlatform(mock(NativeModdedLoader.class)); assertFalse(platform.spawnEntity(new ModdedPlatformWorld(level), "minecraft:zombie", 0.5, 80, 0.5)); @@ -63,7 +70,7 @@ public class ModdedEntitySpawnDifficultyTest { when(chunks.getChunkNow(anyInt(), anyInt())).thenReturn(mock(LevelChunk.class)); IrisEntity configured = new IrisEntity().setType("zombie").setSpawnEffectRiseOutOfGround(false); - assertNull(ModdedEntitySpawner.spawn(mock(Engine.class), configured, level, 0, 80, 0, new RNG(1L))); + assertNull(ModdedEntitySpawner.spawn(mock(Engine.class), configured, new NativeEntityRuntime(level), 0, 80, 0, new RNG(1L))); verify(level, never()).addFreshEntity(any(Entity.class)); verify(level, never()).enabledFeatures(); @@ -80,7 +87,7 @@ public class ModdedEntitySpawnDifficultyTest { continue; } EntityType type = spy(registered); - assertNull(ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); + assertNull(NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); verify(type, never()).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); rejected++; } @@ -98,7 +105,7 @@ public class ModdedEntitySpawnDifficultyTest { EntityType type = spy(BuiltInRegistries.ENTITY_TYPE.getValue(Identifier.parse("minecraft:" + key))); doReturn(created).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); - assertSame(key, created, ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); + assertSame(key, created, NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); verify(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); } } @@ -113,7 +120,7 @@ public class ModdedEntitySpawnDifficultyTest { when(level.getDifficulty()).thenReturn(difficulty); doReturn(created).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); - assertSame(created, ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); + assertSame(created, NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL)); verify(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); } } @@ -127,6 +134,6 @@ public class ModdedEntitySpawnDifficultyTest { doThrow(failure).when(type).spawn(level, BlockPos.ZERO, EntitySpawnReason.NATURAL); assertSame(failure, assertThrows(IllegalStateException.class, - () -> ModdedEntitySpawner.spawnNative(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL))); + () -> NativeEntitySpawns.spawn(type, level, BlockPos.ZERO, EntitySpawnReason.NATURAL))); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnerParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnerParityTest.java index 90b6e90cd..515fd1e65 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnerParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedEntitySpawnerParityTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; + import art.arcane.iris.world.entity.IrisEffect; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Mob; @@ -22,9 +24,9 @@ public class ModdedEntitySpawnerParityTest { public void collisionCheckUsesPaperInclusiveIntegerDimensions() { Set checked = new HashSet<>(); - boolean clear = ModdedEntitySpawner.isAreaClearForSpawn( - 10, 64, 20, 0.6F, 1.95F, position -> { - checked.add(position); + boolean clear = NativeEntityRuntime.isAreaClearForSpawn( + 10, 64, 20, 0.6F, 1.95F, (x, y, z) -> { + checked.add(new BlockPos(x, y, z)); return true; }); @@ -35,8 +37,8 @@ public class ModdedEntitySpawnerParityTest { @Test public void collisionCheckCoversWideEntityVolumeAndRejectsOneBlockedCell() { AtomicInteger checked = new AtomicInteger(); - boolean fullyClear = ModdedEntitySpawner.isAreaClearForSpawn( - 0, 10, 0, 4F, 3.2F, position -> { + boolean fullyClear = NativeEntityRuntime.isAreaClearForSpawn( + 0, 10, 0, 4F, 3.2F, (x, y, z) -> { checked.incrementAndGet(); return true; }); @@ -46,8 +48,8 @@ public class ModdedEntitySpawnerParityTest { BlockPos obstruction = new BlockPos(2, 12, -2); - boolean clear = ModdedEntitySpawner.isAreaClearForSpawn( - 0, 10, 0, 4F, 3.2F, position -> !position.equals(obstruction)); + boolean clear = NativeEntityRuntime.isAreaClearForSpawn( + 0, 10, 0, 4F, 3.2F, (x, y, z) -> !new BlockPos(x, y, z).equals(obstruction)); assertFalse(clear); } @@ -61,19 +63,19 @@ public class ModdedEntitySpawnerParityTest { } } - assertTrue(ModdedEntitySpawner.allNeighborChunksLoaded(5, -2, + assertTrue(NativeEntityRuntime.allNeighborChunksLoaded(5, -2, (x, z) -> loaded.contains(pack(x, z)))); loaded.remove(pack(4, -3)); - assertFalse(ModdedEntitySpawner.allNeighborChunksLoaded(5, -2, + assertFalse(NativeEntityRuntime.allNeighborChunksLoaded(5, -2, (x, z) -> loaded.contains(pack(x, z)))); } @Test public void onlyAiFalseUsesVanillaNoAi() { - assertFalse(ModdedEntitySpawner.shouldDisableAi(true)); - assertTrue(ModdedEntitySpawner.shouldDisableAi(false)); + assertFalse(NativeEntityRuntime.shouldDisableAi(true)); + assertTrue(NativeEntityRuntime.shouldDisableAi(false)); } @Test @@ -137,16 +139,16 @@ public class ModdedEntitySpawnerParityTest { assertEquals(47, expected.size()); for (Map.Entry entry : expected.entrySet()) { - assertEquals(entry.getKey(), entry.getValue(), ModdedEntitySpawner.reasonFor(entry.getKey())); - assertEquals(entry.getKey(), entry.getValue(), ModdedEntitySpawner.reasonFor(entry.getKey().toLowerCase())); + assertEquals(entry.getKey(), entry.getValue(), NativeEntityRuntime.reasonFor(entry.getKey())); + assertEquals(entry.getKey(), entry.getValue(), NativeEntityRuntime.reasonFor(entry.getKey().toLowerCase())); } } @Test public void invalidConfiguredSpawnReasonIntentionallyDefaultsToNatural() { - assertEquals(EntitySpawnReason.NATURAL, ModdedEntitySpawner.reasonFor(null)); - assertEquals(EntitySpawnReason.NATURAL, ModdedEntitySpawner.reasonFor("")); - assertEquals(EntitySpawnReason.NATURAL, ModdedEntitySpawner.reasonFor("not_a_spawn_reason")); + assertEquals(EntitySpawnReason.NATURAL, NativeEntityRuntime.reasonFor(null)); + assertEquals(EntitySpawnReason.NATURAL, NativeEntityRuntime.reasonFor("")); + assertEquals(EntitySpawnReason.NATURAL, NativeEntityRuntime.reasonFor("not_a_spawn_reason")); } @Test diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java index 1d69c36a3..beccd3958 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java @@ -20,7 +20,7 @@ package art.arcane.iris.modded; import art.arcane.volmlib.util.collection.KSet; import art.arcane.volmlib.util.json.JSONObject; -import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217; +import art.arcane.iris.pack.datapack.v1217.DataFixerV1217; import art.arcane.iris.world.history.GenerationEpochContractFactory; import art.arcane.iris.world.history.GenerationRegistryContract; import art.arcane.iris.world.history.GenerationRegistryContractFactory; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedGenerationBootstrapBindingTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedGenerationBootstrapBindingTest.java index c45012ffe..906398ff3 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedGenerationBootstrapBindingTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedGenerationBootstrapBindingTest.java @@ -1,11 +1,17 @@ package art.arcane.iris.modded; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import java.lang.reflect.Field; import net.minecraft.server.level.ServerChunkCache; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.Level; + import net.minecraft.world.level.chunk.ChunkGenerator; import org.junit.Test; +import org.junit.BeforeClass; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; import java.util.List; @@ -20,67 +26,95 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class ModdedGenerationBootstrapBindingTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + @Test public void publishedLevelRequiresCurrentGeneratorIdentity() { - IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS); - MinecraftServer server = mock(MinecraftServer.class); - ServerLevel level = mock(ServerLevel.class); + IrisModdedChunkGenerator generator = generator(); + NativeModdedServer server = mock(NativeModdedServer.class); + NativeWorld level = world(); ServerChunkCache chunkSource = mock(ServerChunkCache.class); - when(server.getLevel(Level.OVERWORLD)).thenReturn(level); - when(level.dimension()).thenReturn(Level.OVERWORLD); - when(level.getChunkSource()).thenReturn(chunkSource); - when(chunkSource.getGenerator()).thenReturn(generator); + when(server.world("minecraft:overworld")).thenReturn(level); + when(level.name()).thenReturn("minecraft:overworld"); + when(((ServerLevel) level.nativeHandle()).getChunkSource()).thenReturn(chunkSource); + ChunkGenerator nativeGenerator = (ChunkGenerator) generator.nativeGenerator(); + when(chunkSource.getGenerator()).thenReturn(nativeGenerator); - assertSame(level, generator.requirePublishedLevel(server, Level.OVERWORLD)); + assertSame(level, generator.requirePublishedLevel(server, "minecraft:overworld")); ChunkGenerator otherGenerator = mock(ChunkGenerator.class); when(chunkSource.getGenerator()).thenReturn(otherGenerator); IllegalStateException mismatch = assertThrows(IllegalStateException.class, - () -> generator.requirePublishedLevel(server, Level.OVERWORLD)); + () -> generator.requirePublishedLevel(server, "minecraft:overworld")); assertTrue(mismatch.getMessage().contains("does not use Iris generator")); } @Test public void publishedLevelRejectsMissingServerAndWorld() { - IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS); + IrisModdedChunkGenerator generator = generator(); IllegalStateException missingServer = assertThrows(IllegalStateException.class, - () -> generator.requirePublishedLevel(null, Level.OVERWORLD)); + () -> generator.requirePublishedLevel(null, "minecraft:overworld")); assertTrue(missingServer.getMessage().contains("server is unavailable")); - MinecraftServer server = mock(MinecraftServer.class); + NativeModdedServer server = mock(NativeModdedServer.class); IllegalStateException missingWorld = assertThrows(IllegalStateException.class, - () -> generator.requirePublishedLevel(server, Level.OVERWORLD)); - assertTrue(missingWorld.getMessage().contains("no published ServerLevel")); + () -> generator.requirePublishedLevel(server, "minecraft:overworld")); + assertTrue(missingWorld.getMessage().contains("no published NativeWorld")); } @Test public void staleSnapshotResolvesOnlyCanonicalOverworldOwnedByGenerator() { - IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS); - MinecraftServer server = mock(MinecraftServer.class); - ServerLevel overworld = mock(ServerLevel.class); + IrisModdedChunkGenerator generator = generator(); + NativeModdedServer server = mock(NativeModdedServer.class); + NativeWorld overworld = world(); ServerChunkCache chunkSource = mock(ServerChunkCache.class); - when(server.getLevel(Level.OVERWORLD)).thenReturn(overworld); - when(overworld.getChunkSource()).thenReturn(chunkSource); - when(chunkSource.getGenerator()).thenReturn(generator); + when(server.overworld()).thenReturn(overworld); + when(((ServerLevel) overworld.nativeHandle()).getChunkSource()).thenReturn(chunkSource); + ChunkGenerator nativeGenerator = (ChunkGenerator) generator.nativeGenerator(); + when(chunkSource.getGenerator()).thenReturn(nativeGenerator); assertSame(overworld, generator.resolveBoundLevel(server, List.of())); ChunkGenerator otherGenerator = mock(ChunkGenerator.class); when(chunkSource.getGenerator()).thenReturn(otherGenerator); assertNull(generator.resolveBoundLevel(server, List.of())); - verify(server, never()).getLevel(Level.NETHER); + verify(server, never()).world("minecraft:the_nether"); } @Test public void dynamicLevelStillResolvesFromSnapshotWithoutCanonicalLookup() { - IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS); - MinecraftServer server = mock(MinecraftServer.class); - ServerLevel dynamicLevel = mock(ServerLevel.class); + IrisModdedChunkGenerator generator = generator(); + NativeModdedServer server = mock(NativeModdedServer.class); + NativeWorld dynamicLevel = world(); ServerChunkCache chunkSource = mock(ServerChunkCache.class); - when(dynamicLevel.getChunkSource()).thenReturn(chunkSource); - when(chunkSource.getGenerator()).thenReturn(generator); + when(((ServerLevel) dynamicLevel.nativeHandle()).getChunkSource()).thenReturn(chunkSource); + ChunkGenerator nativeGenerator = (ChunkGenerator) generator.nativeGenerator(); + when(chunkSource.getGenerator()).thenReturn(nativeGenerator); assertSame(dynamicLevel, generator.resolveBoundLevel(server, List.of(dynamicLevel))); - verify(server, never()).getLevel(Level.OVERWORLD); + verify(server, never()).overworld(); + } + + private static NativeWorld world() { + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(mock(ServerLevel.class)); + return world; + } + + private static IrisModdedChunkGenerator generator() { + IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS); + NativeModdedChunkGenerator nativeGenerator = mock(NativeModdedChunkGenerator.class, CALLS_REAL_METHODS); + try { + Field field = IrisModdedChunkGenerator.class.getDeclaredField("nativeGenerator"); + field.setAccessible(true); + field.set(generator, nativeGenerator); + return generator; + } catch (ReflectiveOperationException failure) { + throw new AssertionError(failure); + } } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedImportedFeatureStageTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedImportedFeatureStageTest.java new file mode 100644 index 000000000..5bb349ddf --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedImportedFeatureStageTest.java @@ -0,0 +1,72 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeFeatureBiomeSource; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.iris.structure.nativegen.IrisImportedFeatureControl; +import org.junit.Test; +import org.junit.BeforeClass; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; + +import static org.junit.Assert.assertFalse; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ModdedImportedFeatureStageTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void disabledFeaturesRecheckAfterPackRepointAndRuntimeEviction() { + NativeFeatureBiomeSource source = mock(NativeFeatureBiomeSource.class); + when(source.packGeneration()).thenReturn(1L); + IrisImportedFeatureControl control = spy(new IrisImportedFeatureControl()); + IrisDimension dimension = new IrisDimension().setImportedFeatures(control); + Engine engine = mock(Engine.class); + when(engine.getDimension()).thenReturn(dimension); + when(engine.getCacheID()).thenReturn(7); + ModdedImportedFeatureStage stage = new ModdedImportedFeatureStage(source); + + stage.prepare(engine); + stage.prepare(engine); + verify(control, times(1)).shouldGenerateFeatures(); + + when(source.packGeneration()).thenReturn(2L); + stage.prepare(engine); + verify(control, times(2)).shouldGenerateFeatures(); + + stage.evictRuntime(7); + stage.prepare(engine); + verify(control, times(3)).shouldGenerateFeatures(); + + stage.invalidate(); + stage.prepare(engine); + verify(control, times(4)).shouldGenerateFeatures(); + verify(source, never()).orderedPossibleBiomes(); + assertFalse(stage.active()); + } + + @Test + public void closingRuntimeDoesNotBuildFeatureTables() { + NativeFeatureBiomeSource source = mock(NativeFeatureBiomeSource.class); + Engine engine = mock(Engine.class); + when(engine.isClosing()).thenReturn(true); + ModdedImportedFeatureStage stage = new ModdedImportedFeatureStage(source); + + stage.prepare(engine); + stage.prepareWithoutWaiting(engine); + + verify(source, never()).packGeneration(); + verify(engine, never()).getDimension(); + assertFalse(stage.active()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLootApplierTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLootApplierTest.java index 2eaa73035..2ceb3d812 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLootApplierTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLootApplierTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeContainerLoot; + import art.arcane.iris.structure.placement.LootResolver; import art.arcane.iris.world.loot.IrisLootMode; import art.arcane.volmlib.util.math.RNG; @@ -112,7 +114,7 @@ public class ModdedLootApplierTest { @Test public void nativeKeyIsReturnedWhenTheLiveRegistryContainsIt() { - ResourceKey key = ModdedLootApplier.resolveNativeKey("minecraft:chests/simple_dungeon", candidate -> true); + ResourceKey key = NativeContainerLoot.resolveNativeKey("minecraft:chests/simple_dungeon", candidate -> true); assertNotNull(key); assertEquals(Identifier.parse("minecraft:chests/simple_dungeon"), key.identifier()); @@ -120,7 +122,7 @@ public class ModdedLootApplierTest { @Test public void nativeKeyIsRejectedWhenTheLiveRegistryDoesNotContainIt() { - ResourceKey key = ModdedLootApplier.resolveNativeKey("minecraft:chests/missing", candidate -> false); + ResourceKey key = NativeContainerLoot.resolveNativeKey("minecraft:chests/missing", candidate -> false); assertNull(key); } @@ -129,7 +131,7 @@ public class ModdedLootApplierTest { public void malformedNativeKeyIsRejectedBeforeRegistryLookup() { AtomicBoolean registryConsulted = new AtomicBoolean(false); - ResourceKey key = ModdedLootApplier.resolveNativeKey("not a valid identifier", candidate -> { + ResourceKey key = NativeContainerLoot.resolveNativeKey("not a valid identifier", candidate -> { registryConsulted.set(true); return true; }); @@ -142,7 +144,7 @@ public class ModdedLootApplierTest { public void nativeOnlyFillMarksTheContainerChanged() { TrackingContainer container = new TrackingContainer(); - ModdedLootApplier.fillContainer(container, List.of(), new RNG(17L)); + NativeContainerLoot.fillContainer(container, List.of(), new RNG(17L), message -> {}); assertTrue(container.changed); } @@ -152,8 +154,8 @@ public class ModdedLootApplierTest { BlockPos leftPos = new BlockPos(0, 64, 0); BlockPos rightPos = new BlockPos(1, 64, 0); - assertTrue(ModdedLootApplier.isCanonicalPair(leftPos, rightPos)); - assertFalse(ModdedLootApplier.isCanonicalPair(rightPos, leftPos)); + assertTrue(NativeContainerLoot.isCanonicalPair(leftPos, rightPos)); + assertFalse(NativeContainerLoot.isCanonicalPair(rightPos, leftPos)); } @Test @@ -165,8 +167,8 @@ public class ModdedLootApplierTest { RandomizableContainer empty = containerWithLootTable(null); RandomizableContainer nativeLoot = containerWithLootTable(key); - assertFalse(ModdedLootApplier.hasNativeLootTable(empty)); - assertTrue(ModdedLootApplier.hasNativeLootTable(nativeLoot)); + assertFalse(NativeContainerLoot.hasNativeLootTable(empty)); + assertTrue(NativeContainerLoot.hasNativeLootTable(nativeLoot)); } private RandomizableContainer containerWithLootTable(ResourceKey key) { diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructurePoiRegistrationTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructurePoiRegistrationTest.java index 6b371d9b3..6ef3b98e9 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructurePoiRegistrationTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructurePoiRegistrationTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedStructureStage; + import com.mojang.serialization.Codec; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; @@ -58,7 +60,7 @@ public class ModdedNativeStructurePoiRegistrationTest { chunk.setBlockState(ordinaryPosition, Blocks.STONE.defaultBlockState(), 0); Map visited = new LinkedHashMap<>(); - ModdedNativeStructureStage.visitExistingPois( + NativeModdedStructureStage.visitExistingPois( chunk, (position, state) -> visited.put(position.immutable(), state)); assertEquals(poiState, visited.get(poiPosition)); @@ -92,7 +94,7 @@ public class ModdedNativeStructurePoiRegistrationTest { Deque serverQueue = new ArrayDeque<>(); AtomicInteger missingRemovals = new AtomicInteger(); - ModdedNativeStructureStage.visitExistingPois(chunk, + NativeModdedStructureStage.visitExistingPois(chunk, (poiPosition, state) -> queuePoiTransition( serverQueue, section, poiPosition, Blocks.AIR.defaultBlockState(), state, missingRemovals)); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccessTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccessTest.java index 788bc5315..7b91d9df7 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccessTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedNativeStructureWorldgenAccessTest.java @@ -1,9 +1,11 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedNativeStructureWorldgenAccess; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; -import art.arcane.iris.nativegen.NativeStructurePostProcessor; -import art.arcane.iris.nativegen.NativeStructureTerrainIntegrator; +import art.arcane.volmlib.util.structure.StructureTerrainMode; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructurePostProcessor; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -84,7 +86,7 @@ public class ModdedNativeStructureWorldgenAccessTest { StructureStart start = new StructureStart(structure, generationCenter(), 0, new PiecesContainer(List.of(piece))); List targets = List.of( new NativeStructureTerrainIntegrator.TerrainTarget("minecraft:village_plains", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.FLATTEN))); + new IrisStructureTerrain().setMode(StructureTerrainMode.FLATTEN))); BoundingBox area = new BoundingBox(generationCenter().getMinBlockX(), -64, generationCenter().getMinBlockZ(), generationCenter().getMaxBlockX(), 319, generationCenter().getMaxBlockZ()); RecordingDelegate unbounded = new RecordingDelegate(); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java index e1adbc8dd..facc55cf2 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java @@ -1,7 +1,12 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerAccess; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.level.block.state.BlockState; @@ -20,7 +25,7 @@ public class ModdedPlatformPathsTest { private ModdedPlatform platform() { Path configDir = temporaryFolder.getRoot().toPath(); - return new ModdedPlatform(new ModdedLoader() { + return new ModdedPlatform(new NativeModdedLoader() { @Override public String platformName() { return "test"; @@ -37,20 +42,21 @@ public class ModdedPlatformPathsTest { } @Override - public MinecraftServer currentServer() { + public NativeModdedServer currentServer() { return null; } @Override - public void invalidateLevelCache(MinecraftServer server) { + public ModdedServerAccess serverAccess() { + return new ModdedServerLevels(server -> {}); } @Override - public void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level) { + public void fireDynamicLevelLoad(NativeWorld level) { } @Override - public void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level) { + public void fireDynamicLevelUnload(NativeWorld level) { } @Override @@ -69,12 +75,12 @@ public class ModdedPlatformPathsTest { } @Override - public boolean hasTreeFellerPermission(ServerPlayer player) { + public boolean hasBlockBreakPermission(ServerPlayer player) { return false; } @Override - public boolean canTreeFellerBreak(ServerLevel level, ServerPlayer player, BlockPos position, BlockState state) { + public boolean canBreakBlock(ServerLevel level, ServerPlayer player, BlockPos position, BlockState state) { return false; } }); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedSavedBiomeConsumersTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedSavedBiomeConsumersTest.java index 7d604df9f..c2a3526ab 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedSavedBiomeConsumersTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedSavedBiomeConsumersTest.java @@ -1,5 +1,11 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolPlayer; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; + import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.BiomeEnvironment; @@ -19,7 +25,7 @@ import net.minecraft.server.Bootstrap; import net.minecraft.server.level.ServerPlayer; import org.junit.Test; import org.junit.BeforeClass; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import org.mockito.MockedStatic; @@ -45,7 +51,7 @@ public class ModdedSavedBiomeConsumersTest { public static void initializeRuntimeTypes() throws Exception { SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState("AIR")).thenReturn(air); Class.forName(EngineMantle.class.getName()); @@ -78,10 +84,10 @@ public class ModdedSavedBiomeConsumersTest { try (MockedStatic configured = mockStatic(IrisSettings.class); MockedStatic spawns = mockStatic(ModdedEntitySpawner.class)) { configured.when(IrisSettings::get).thenReturn(settings); - spawns.when(() -> ModdedEntitySpawner.chunksSafe(level, 2, -1)).thenReturn(true); - assertThrows(SavedBiomeUnavailableException.class, () -> manager.initialSpawnChunk(level, 2, -1)); + spawns.when(() -> ModdedEntitySpawner.chunksSafe(any(NativeEntityRuntime.class), eq(2), eq(-1))).thenReturn(true); + assertThrows(SavedBiomeUnavailableException.class, () -> manager.initialSpawnChunk(new ModdedPlatformWorld(level), 2, -1)); verify(chunk, never()).raiseFlagUnchecked(eq(ModdedWorldManager.INITIAL_SPAWN_COMPLETION_FLAG), any()); - assertTrue(manager.initialSpawnChunk(level, 2, -1)); + assertTrue(manager.initialSpawnChunk(new ModdedPlatformWorld(level), 2, -1)); verify(chunk).raiseFlagUnchecked(eq(ModdedWorldManager.INITIAL_SPAWN_COMPLETION_FLAG), any()); } finally { manager.close(); @@ -105,10 +111,10 @@ public class ModdedSavedBiomeConsumersTest { IrisSettings settings = new IrisSettings(); settings.getWorld().setEffectSystem(false); Class stateType = Class.forName(ModdedEngineEffects.class.getName() + "$PlayerState"); - Constructor constructor = stateType.getDeclaredConstructor(ServerPlayer.class); + Constructor constructor = stateType.getDeclaredConstructor(NativeProtocolPlayer.class); constructor.setAccessible(true); - Object state = constructor.newInstance(player); - Method tick = ModdedEngineEffects.class.getDeclaredMethod("tickPlayer", ServerLevel.class, stateType); + Object state = constructor.newInstance(NativeProtocolPlayer.fromHandle(player)); + Method tick = ModdedEngineEffects.class.getDeclaredMethod("tickPlayer", NativeWorld.class, stateType); tick.setAccessible(true); Field selected = stateType.getDeclaredField("environment"); selected.setAccessible(true); @@ -116,9 +122,9 @@ public class ModdedSavedBiomeConsumersTest { try (MockedStatic configured = mockStatic(IrisSettings.class)) { configured.when(IrisSettings::get).thenReturn(settings); ModdedEngineEffects effects = new ModdedEngineEffects(engine); - tick.invoke(effects, level, state); + tick.invoke(effects, new ModdedPlatformWorld(level), state); assertNull(selected.get(state)); - tick.invoke(effects, level, state); + tick.invoke(effects, new ModdedPlatformWorld(level), state); assertSame(environment, selected.get(state)); verify(engine, times(2)).getBiomeEnvironment(0, 64, 0); } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java index 33cf7d02b..2b8d9a68e 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureSetFrequencyOverrides; + import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; import art.arcane.iris.structure.placement.IrisStructureSetFrequencyOverride; import art.arcane.volmlib.util.collection.KList; @@ -55,7 +57,7 @@ public class ModdedStructureSetFrequencyOverridesTest { .setFrequencyOverrides(overrides); ChunkGeneratorStructureState returned = - ModdedStructureSetFrequencyOverrides.apply(state, control); + NativeStructureSetFrequencyOverrides.apply(state, control); assertSame(state, returned); List> scaledSets = state.possibleStructureSets(); @@ -80,7 +82,7 @@ public class ModdedStructureSetFrequencyOverridesTest { .setFrequencyOverrides(overrides); List> scaled = - ModdedStructureSetFrequencyOverrides.scaleSets(List.of(custom), control); + NativeStructureSetFrequencyOverrides.scaleSets(List.of(custom), control); assertSame(custom, scaled.getFirst()); } @@ -98,7 +100,7 @@ public class ModdedStructureSetFrequencyOverridesTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - List> scaled = ModdedStructureSetFrequencyOverrides.scaleSets( + List> scaled = NativeStructureSetFrequencyOverrides.scaleSets( List.of(first, second), control); assertSame(first, scaled.get(0)); @@ -118,7 +120,7 @@ public class ModdedStructureSetFrequencyOverridesTest { IrisImportedStructureControl control = new IrisImportedStructureControl() .setFrequencyOverrides(overrides); - List> scaled = ModdedStructureSetFrequencyOverrides.scaleSets( + List> scaled = NativeStructureSetFrequencyOverrides.scaleSets( List.of(first, second), control); assertEquals(31, ((RandomSpreadStructurePlacement) @@ -140,7 +142,7 @@ public class ModdedStructureSetFrequencyOverridesTest { .setFrequencyOverrides(overrides); assertThrows(IllegalStateException.class, () -> - ModdedStructureSetFrequencyOverrides.scaleSets(List.of(custom), control)); + NativeStructureSetFrequencyOverrides.scaleSets(List.of(custom), control)); } private static Holder structureSet( diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureTemplateCacheTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureTemplateCacheTest.java index bf3f170da..dc856bb18 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureTemplateCacheTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureTemplateCacheTest.java @@ -55,7 +55,7 @@ public class ModdedStructureTemplateCacheTest { @Test public void requiredCommonMixinConfigRegistersPaletteConcurrencyFix() throws Exception { InputStream resource = ModdedStructureTemplateCacheTest.class.getClassLoader() - .getResourceAsStream("irisworldgen.entity.mixins.json"); + .getResourceAsStream("volmlib.entity.mixins.json"); assertNotNull(resource); String config; try (InputStream input = resource) { @@ -63,7 +63,7 @@ public class ModdedStructureTemplateCacheTest { } assertTrue(config.contains("\"StructureTemplatePaletteConcurrencyMixin\"")); assertNotNull(Class.forName( - "art.arcane.iris.modded.mixin.StructureTemplatePaletteConcurrencyMixin", + "art.arcane.volmlib.nativelib.minecraft26_2.modded.mixin.StructureTemplatePaletteConcurrencyMixin", false, ModdedStructureTemplateCacheTest.class.getClassLoader())); } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileDataIdentityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileDataIdentityTest.java index b1c682793..ee495b21d 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileDataIdentityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileDataIdentityTest.java @@ -1,5 +1,6 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileData; import art.arcane.iris.generation.block.TileData; import art.arcane.volmlib.util.collection.KMap; import com.google.gson.Gson; @@ -53,15 +54,15 @@ public class ModdedTileDataIdentityTest { ModdedTileData captured = ModdedTileData.capture("minecraft:player_head", snbt); - assertEquals(snbt, captured.snbt()); - assertEquals(snbt, captured.getProperties().get(ModdedTileData.NBT_PROPERTY)); + assertEquals(snbt, captured.nativeData().snbt()); + assertEquals(snbt, captured.getProperties().get(NativeTileData.NBT_PROPERTY)); // The Bukkit-readable map form is still written, and is still array-lossy - which is why the SNBT has to stay. assertTrue(captured.getProperties().get("profile") instanceof KMap); KMap profile = (KMap) captured.getProperties().get("profile"); assertTrue(profile.get("id") instanceof List); - CompoundTag payload = captured.payload(); + CompoundTag payload = captured.nativeData().payload(); assertNotNull(payload); Tag profileTag = payload.get("profile"); assertTrue(profileTag instanceof CompoundTag); @@ -101,7 +102,7 @@ public class ModdedTileDataIdentityTest { @Test public void legacyRecordHasNoBlockKey() { - ModdedTileData legacy = new ModdedTileData(new byte[]{0, 1}, new KMap<>(), null, 0); + ModdedTileData legacy = ModdedTileData.wrap(new NativeTileData(new byte[]{0, 1}, new KMap<>(), null, 0)); assertNull(legacy.getMaterialKey()); } @@ -131,7 +132,7 @@ public class ModdedTileDataIdentityTest { out.writeUTF(blockKey); out.writeUTF(GSON.toJson(properties)); } - return new ModdedTileData(bytes.toByteArray(), properties, blockKey, -1); + return ModdedTileData.wrap(new NativeTileData(bytes.toByteArray(), properties, blockKey, -1)); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java index 7fc5b6f80..3a948b43a 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileReader; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; import art.arcane.iris.generation.block.TileData; import art.arcane.volmlib.util.collection.KMap; import net.minecraft.SharedConstants; @@ -39,7 +41,8 @@ public class ModdedTileParityTest { SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); registries = RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY); - TileData.bindPlatformReader(new ModdedTileReader(() -> null)); + NativeTileReader nativeTileReader = new NativeTileReader(() -> null); + TileData.bindPlatformReader(in -> ModdedTileData.wrap(nativeTileReader.read(in))); TileData.bindPlatformFactory(ModdedTileData::fromProperties); } @@ -55,7 +58,7 @@ public class ModdedTileParityTest { }); SignBlockEntity sign = new SignBlockEntity(BlockPos.ZERO, Blocks.OAK_SIGN.defaultBlockState()); - sign.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.payload())); + sign.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); assertEquals("Iris", sign.getFrontText().getMessage(0, false).getString()); assertEquals("parity", sign.getBackText().getMessage(3, false).getString()); @@ -70,7 +73,7 @@ public class ModdedTileParityTest { }); SpawnerBlockEntity spawner = new SpawnerBlockEntity(BlockPos.ZERO, Blocks.SPAWNER.defaultBlockState()); - spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.payload())); + spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); CompoundTag saved = spawner.saveWithoutMetadata(registries); assertTrue(saved.toString().contains("minecraft:zombie")); @@ -84,7 +87,7 @@ public class ModdedTileParityTest { }); SpawnerBlockEntity spawner = new SpawnerBlockEntity(BlockPos.ZERO, Blocks.SPAWNER.defaultBlockState()); - spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.payload())); + spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); CompoundTag saved = spawner.saveWithoutMetadata(registries); assertTrue(saved.toString().contains("minecraft:command_block_minecart")); @@ -98,7 +101,7 @@ public class ModdedTileParityTest { }); SpawnerBlockEntity spawner = new SpawnerBlockEntity(BlockPos.ZERO, Blocks.SPAWNER.defaultBlockState()); - spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.payload())); + spawner.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); CompoundTag saved = spawner.saveWithoutMetadata(registries); assertTrue(saved.toString().contains("minecraft:pig")); @@ -114,7 +117,7 @@ public class ModdedTileParityTest { }); ChestBlockEntity chest = new ChestBlockEntity(BlockPos.ZERO, Blocks.CHEST.defaultBlockState()); - chest.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.payload())); + chest.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); RandomizableContainer lootable = chest; assertNotNull(lootable.getLootTable()); @@ -132,35 +135,35 @@ public class ModdedTileParityTest { assertEquals( BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:red_banner")), - tile.adjustBlockState(BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:white_banner")).defaultBlockState()).getBlock()); - assertEquals(Blocks.BARREL, tile.adjustBlockState(Blocks.BARREL.defaultBlockState()).getBlock()); + tile.nativeData().adjustBlockState(BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:white_banner")).defaultBlockState()).getBlock()); + assertEquals(Blocks.BARREL, tile.nativeData().adjustBlockState(Blocks.BARREL.defaultBlockState()).getBlock()); } @Test public void paper26_2LegacyBannerFirstOrdinalIsSmallStripes() { assertEquals( Identifier.parse("minecraft:small_stripes"), - ModdedTileReader.legacyBannerPatternKey(0)); + NativeTileReader.legacyBannerPatternKey(0)); } @Test public void paper26_2LegacyBannerMiddleOrdinalIsStripeLeft() { assertEquals( Identifier.parse("minecraft:stripe_left"), - ModdedTileReader.legacyBannerPatternKey(21)); + NativeTileReader.legacyBannerPatternKey(21)); } @Test public void paper26_2LegacyBannerLastOrdinalIsHalfVerticalRight() { assertEquals( Identifier.parse("minecraft:half_vertical_right"), - ModdedTileReader.legacyBannerPatternKey(42)); + NativeTileReader.legacyBannerPatternKey(42)); } @Test public void invalidLegacyBannerOrdinalFallsBackToBase() { - assertEquals(Identifier.parse("minecraft:base"), ModdedTileReader.legacyBannerPatternKey(-1)); - assertEquals(Identifier.parse("minecraft:base"), ModdedTileReader.legacyBannerPatternKey(43)); + assertEquals(Identifier.parse("minecraft:base"), NativeTileReader.legacyBannerPatternKey(-1)); + assertEquals(Identifier.parse("minecraft:base"), NativeTileReader.legacyBannerPatternKey(43)); } @Test @@ -173,13 +176,13 @@ public class ModdedTileParityTest { assertTrue(tile instanceof ModdedTileData); ChestBlockEntity chest = new ChestBlockEntity(BlockPos.ZERO, Blocks.CHEST.defaultBlockState()); chest.loadWithComponents(TagValueInput.create( - ProblemReporter.DISCARDING, registries, ((ModdedTileData) tile).payload())); + ProblemReporter.DISCARDING, registries, ((ModdedTileData) tile).nativeData().payload())); assertNotNull(chest.getLootTable()); assertEquals("minecraft:chests/abandoned_mineshaft", chest.getLootTable().identifier().toString()); assertEquals(9124L, chest.getLootTableSeed()); - assertTrue(((ModdedTileData) tile).isApplicable(Blocks.CHEST.defaultBlockState(), chest)); - assertFalse(((ModdedTileData) tile).isApplicable( + assertTrue(((ModdedTileData) tile).nativeData().isApplicable(Blocks.CHEST.defaultBlockState(), chest)); + assertFalse(((ModdedTileData) tile).nativeData().isApplicable( Blocks.BARREL.defaultBlockState(), new BarrelBlockEntity(BlockPos.ZERO, Blocks.BARREL.defaultBlockState()))); } @@ -197,8 +200,8 @@ public class ModdedTileParityTest { SignBlockEntity sign = new SignBlockEntity(BlockPos.ZERO, Blocks.OAK_SIGN.defaultBlockState()); ChestBlockEntity chest = new ChestBlockEntity(BlockPos.ZERO, Blocks.CHEST.defaultBlockState()); - assertTrue(tile.isApplicable(Blocks.OAK_SIGN.defaultBlockState(), sign)); - assertFalse(tile.isApplicable(Blocks.CHEST.defaultBlockState(), chest)); + assertTrue(tile.nativeData().isApplicable(Blocks.OAK_SIGN.defaultBlockState(), sign)); + assertFalse(tile.nativeData().isApplicable(Blocks.CHEST.defaultBlockState(), chest)); } private static ModdedTileData legacyTile(TileWriter writer) throws Exception { diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java index e7e7925bb..5f4b286de 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; + import art.arcane.iris.world.entity.IrisEntity; import art.arcane.iris.world.entity.IrisEntitySpawn; import art.arcane.iris.world.entity.IrisSpawnGroup; @@ -37,13 +41,13 @@ public class ModdedWaterEntitySpawnTest { ServerLevel level = mock(ServerLevel.class); IrisEntity entity = new IrisEntity().setType("tadpole").setSurface(IrisSurface.WATER); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.WATER.defaultBlockState()); - assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 78, 0)); + assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 78, 0)); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.SEAGRASS.defaultBlockState()); - assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 78, 0)); + assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 78, 0)); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.AIR.defaultBlockState()); - assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 79, 0)); + assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 79, 0)); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.LAVA.defaultBlockState()); - assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 78, 0)); + assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 78, 0)); } @Test @@ -51,38 +55,38 @@ public class ModdedWaterEntitySpawnTest { ServerLevel level = mock(ServerLevel.class); IrisEntity entity = new IrisEntity().setType("tadpole").setSurface(IrisSurface.LAVA); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.LAVA.defaultBlockState()); - assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 78, 0)); + assertTrue(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 78, 0)); when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.WATER.defaultBlockState()); - assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(level, entity, 0, 78, 0)); + assertFalse(ModdedEntitySpawner.isAreaClearForSpawn(new NativeEntityRuntime(level), entity, 0, 78, 0)); } @Test public void waterloggedSolidBlocksStillObstructWaterSpawns() { assertTrue(ModdedWorldManager.matchesSurface(IrisSurface.WATER, - Blocks.SEA_PICKLE.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, true))); + ModdedBlockState.of(Blocks.SEA_PICKLE.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, true), null))); assertFalse(ModdedWorldManager.matchesSurface(IrisSurface.WATER, - Blocks.OAK_FENCE.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, true))); + ModdedBlockState.of(Blocks.OAK_FENCE.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, true), null))); } @Test public void fractionalFluidVolumeIncludesHorizontalEdgesAndCenteredHeight() { Set checked = new HashSet<>(); - assertTrue(ModdedEntitySpawner.isFluidAreaClearForSpawn(-1, 78, -1, 1.3F, 0.8F, position -> { - checked.add(position); + assertTrue(NativeEntityRuntime.isFluidAreaClearForSpawn(-1, 78, -1, 1.3F, 0.8F, (x, y, z) -> { + checked.add(new BlockPos(x, y, z)); return true; })); assertEquals(18, checked.size()); assertTrue(checked.contains(new BlockPos(-2, 79, -2))); assertTrue(checked.contains(new BlockPos(0, 78, 0))); - assertFalse(ModdedEntitySpawner.isFluidAreaClearForSpawn(-1, 78, -1, 1.3F, 0.8F, - position -> !position.equals(new BlockPos(-2, 79, -2)))); + assertFalse(NativeEntityRuntime.isFluidAreaClearForSpawn(-1, 78, -1, 1.3F, 0.8F, + (x, y, z) -> !new BlockPos(x, y, z).equals(new BlockPos(-2, 79, -2)))); } @Test public void exactUpperBoundaryDoesNotRequireWaterOutsideTheBody() { Set checked = new HashSet<>(); - assertTrue(ModdedEntitySpawner.isFluidAreaClearForSpawn(0, 78, 0, 1F, 0.5F, position -> { - checked.add(position); + assertTrue(NativeEntityRuntime.isFluidAreaClearForSpawn(0, 78, 0, 1F, 0.5F, (x, y, z) -> { + checked.add(new BlockPos(x, y, z)); return true; })); assertEquals(Set.of(new BlockPos(0, 78, 0)), checked); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWorldCheckTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWorldCheckTest.java index 3945b87bc..58376f5f1 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWorldCheckTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWorldCheckTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStructureInspection; + import net.minecraft.resources.Identifier; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.levelgen.structure.BoundingBox; @@ -126,7 +128,7 @@ public class ModdedWorldCheckTest { public void smallStructureFootprintIncludesEveryChunk() { BoundingBox bounds = new BoundingBox(-16, -20, -16, 31, 120, 31); - List chunks = WorldCheckStructureAudit.boundedFootprintChunks(bounds, ChunkPos.ZERO, 96); + List chunks = NativeStructureInspection.boundedFootprintChunks(bounds, ChunkPos.ZERO, 96); assertEquals(9, chunks.size()); assertTrue(chunks.contains(new ChunkPos(-1, -1))); @@ -137,7 +139,7 @@ public class ModdedWorldCheckTest { public void largeStructureFootprintIsBoundedAndSamplesEdges() { BoundingBox bounds = new BoundingBox(-512, -64, -512, 511, 300, 511); - List chunks = WorldCheckStructureAudit.boundedFootprintChunks(bounds, ChunkPos.ZERO, 20); + List chunks = NativeStructureInspection.boundedFootprintChunks(bounds, ChunkPos.ZERO, 20); assertTrue(chunks.size() <= 20); assertTrue(chunks.size() >= 16); @@ -267,6 +269,6 @@ public class ModdedWorldCheckTest { private static boolean characteristic(String structureLabel, String structureKey, String blockKey) { return WorldCheckMaterials.isCharacteristicMaterial(structureLabel, - Identifier.parse(structureKey), Identifier.parse(blockKey)); + structureKey, blockKey); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeCommandExecutorTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeCommandExecutorTest.java new file mode 100644 index 000000000..18d006460 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeCommandExecutorTest.java @@ -0,0 +1,61 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandExecutor; +import net.minecraft.SharedConstants; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.MinecraftServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NativeCommandExecutorTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void dispatchesConsoleSourceWithLeadingSlashRemoved() { + MinecraftServer server = mock(MinecraftServer.class); + Commands commands = mock(Commands.class); + CommandSourceStack source = mock(CommandSourceStack.class); + when(server.getCommands()).thenReturn(commands); + when(server.createCommandSourceStack()).thenReturn(source); + List errors = new ArrayList<>(); + + assertTrue(NativeCommandExecutor.dispatch(NativeModdedServer.fromHandle(server), "/say terrain", errors::add)); + + verify(commands).performPrefixedCommand(source, "say terrain"); + assertTrue(errors.isEmpty()); + } + + @Test + public void failedDispatchPreservesFailureForTheCaller() { + MinecraftServer server = mock(MinecraftServer.class); + Commands commands = mock(Commands.class); + CommandSourceStack source = mock(CommandSourceStack.class); + when(server.getCommands()).thenReturn(commands); + when(server.createCommandSourceStack()).thenReturn(source); + IllegalStateException failure = new IllegalStateException("dispatch failed"); + doThrow(failure).when(commands).performPrefixedCommand(source, "say terrain"); + List errors = new ArrayList<>(); + + assertFalse(NativeCommandExecutor.dispatch(NativeModdedServer.fromHandle(server), "say terrain", errors::add)); + + assertEquals(List.of(failure), errors); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeItemTranslatorTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeItemTranslatorTest.java new file mode 100644 index 000000000..41c900b49 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeItemTranslatorTest.java @@ -0,0 +1,83 @@ +package art.arcane.iris.modded; + +import art.arcane.iris.world.loot.IrisLoot; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemContext; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemTranslator; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.collection.KMap; +import art.arcane.volmlib.util.math.RNG; +import net.minecraft.SharedConstants; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class NativeItemTranslatorTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + if (!Items.LEATHER_HELMET.builtInRegistryHolder().areComponentsBound()) { + Items.LEATHER_HELMET.builtInRegistryHolder().bindComponents(DataComponentMap.builder() + .set(DataComponents.MAX_STACK_SIZE, 1) + .set(DataComponents.MAX_DAMAGE, 55) + .set(DataComponents.DAMAGE, 0) + .build()); + } + } + + @Test + public void componentsPreserveAuthoredValuesAndDurability() { + IrisLoot loot = new IrisLoot().setType("leather_helmet").setMinAmount(2).setMaxAmount(2) + .setMinDurability(0.5).setMaxDurability(0.5).setUnbreakable(true) + .setCustomModel(73).setLeatherColor("0x12ab34").setDisplayName("&aExplorer") + .setLore(new KList<>("&bFirst line", "Second line")) + .setCustomNbt(new KMap().qput("marker", "terrain")); + List warnings = new ArrayList<>(); + NativeItemContext context = new NativeItemContext(null, + new NativeItemContext.Options("example", (key, message) -> warnings.add(message))); + + NativeItemStack result = NativeItemTranslator.stack(new IrisItemRecipe(loot, new RNG(41)), context); + + assertNotNull(result); + ItemStack stack = result.stack(); + assertTrue(stack.is(Items.LEATHER_HELMET)); + assertEquals(2, stack.getCount()); + assertEquals((int) Math.round(stack.getMaxDamage() * 0.5), stack.getDamageValue()); + assertNotNull(stack.get(DataComponents.UNBREAKABLE)); + assertEquals(73F, stack.get(DataComponents.CUSTOM_MODEL_DATA).floats().getFirst(), 0F); + assertEquals(0x12ab34, stack.get(DataComponents.DYED_COLOR).rgb()); + assertEquals("§aExplorer", stack.get(DataComponents.CUSTOM_NAME).getString()); + assertEquals("§bFirst line", stack.get(DataComponents.LORE).lines().getFirst().getString()); + assertEquals("terrain", stack.get(DataComponents.CUSTOM_DATA).copyTag().getString("marker").orElseThrow()); + assertTrue(warnings.isEmpty()); + } + + @Test + public void unknownItemDoesNotConsumeLootRandomness() { + RNG random = new RNG(83); + RNG expected = new RNG(83); + List warnings = new ArrayList<>(); + NativeItemContext context = new NativeItemContext(null, + new NativeItemContext.Options("example", (key, message) -> warnings.add(key))); + + NativeItemStack result = NativeItemTranslator.stack(new IrisItemRecipe( + new IrisLoot().setType("example:unknown_item").setMinAmount(1).setMaxAmount(64), random), context); + + assertNull(result); + assertEquals(expected.nextLong(), random.nextLong()); + assertEquals(List.of("item:example:unknown_item"), warnings); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativePayloadProtocolTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativePayloadProtocolTest.java new file mode 100644 index 000000000..d0ebdf052 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativePayloadProtocolTest.java @@ -0,0 +1,44 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativePayloadProtocol; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.DecoderException; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.RegistryFriendlyByteBuf; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; + +public class NativePayloadProtocolTest { + @Test + public void frameRoundTripKeepsItsChannelAndBytes() { + NativePayloadProtocol protocol = new NativePayloadProtocol("terrain:frame", 32); + byte[] frame = new byte[]{1, 4, 9, 16}; + RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), RegistryAccess.EMPTY); + try { + protocol.codec().encode(buffer, protocol.payload(frame)); + NativePayloadProtocol.Payload decoded = protocol.codec().decode(buffer); + + assertSame(protocol, decoded.protocol()); + assertArrayEquals(frame, decoded.data()); + assertEquals("terrain:frame", decoded.type().id().toString()); + } finally { + buffer.release(); + } + } + + @Test + public void decoderEnforcesTheConfiguredFrameLimit() { + NativePayloadProtocol protocol = new NativePayloadProtocol("terrain:frame", 3); + RegistryFriendlyByteBuf buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), RegistryAccess.EMPTY); + try { + buffer.writeByteArray(new byte[]{1, 2, 3, 4}); + assertThrows(DecoderException.class, () -> protocol.codec().decode(buffer)); + } finally { + buffer.release(); + } + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeRegistryAccessTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeRegistryAccessTest.java new file mode 100644 index 000000000..6beac113e --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeRegistryAccessTest.java @@ -0,0 +1,54 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeRegistryAccess; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class NativeRegistryAccessTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void statePropertiesPreserveTypesDefaultsAndSharedGroups() { + NativeRegistryAccess access = new NativeRegistryAccess(() -> null, message -> {}); + Map> properties = access.blockStateProperties(); + + assertSame(properties.get("minecraft:stone"), properties.get("minecraft:dirt")); + NativeBlockProperty power = properties.get("minecraft:redstone_wire").stream() + .filter(property -> property.name().equals("power")).findFirst().orElseThrow(); + assertEquals("integer", power.jsonType()); + assertEquals(0, power.defaultValue()); + assertEquals(16, power.allowedValues().size()); + assertTrue(power.allowedValues().contains(15)); + assertNull(power.numericRange()); + } + + @Test + public void staticRegistriesResolveWithoutAnActiveServerAndDynamicReadsStayEmpty() { + List warnings = new ArrayList<>(); + NativeRegistryAccess access = new NativeRegistryAccess(() -> null, warnings::add); + + assertEquals("minecraft:diamond_sword", access.item("DIAMOND SWORD").key()); + assertEquals("creature", access.entity("minecraft:cow").spawnCategory()); + assertNull(access.item("example:unknown")); + assertTrue(access.biomeKeys().isEmpty()); + assertTrue(access.structureKeys().isEmpty()); + assertTrue(access.enchantmentKeys().isEmpty()); + assertEquals(List.of("biome", "structure", "enchantment"), warnings); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStateOperationsTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStateOperationsTest.java new file mode 100644 index 000000000..0f6ee757a --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStateOperationsTest.java @@ -0,0 +1,51 @@ +package art.arcane.iris.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeBlockResolver; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStateMerger; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeStateRotator; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import net.minecraft.SharedConstants; +import net.minecraft.core.Direction; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.level.block.StairBlock; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class NativeStateOperationsTest { + @BeforeClass + public static void bootstrapMinecraftRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void rotationPreservesDeferredPlacementMetadata() { + ModdedBlockState parsed = NativeBlockResolver.strictParse("minecraft:oak_stairs[facing=north]"); + ModdedBlockState deferred = ModdedBlockState.deferred(parsed.handle(), parsed.parsedProperties(), "custom:stairs"); + + NativeBlockState result = NativeStateRotator.rotate(deferred, + (x, y, z) -> new NativeStateRotator.Vector(-z, y, x), failure -> { + throw new AssertionError(failure); + }); + + assertEquals(Direction.EAST, ((ModdedBlockState) result).handle().getValue(StairBlock.FACING)); + assertEquals("custom:stairs", result.deferredPlacementKey()); + } + + @Test + public void mergeAppliesOnlySpecifiedPropertiesAndRetainsDeferredPlacement() { + ModdedBlockState parsed = NativeBlockResolver.strictParse("minecraft:oak_stairs[facing=west]"); + ModdedBlockState deferred = ModdedBlockState.deferred(parsed.handle(), parsed.parsedProperties(), "custom:stairs"); + ModdedBlockState update = NativeBlockResolver.strictParse("minecraft:oak_stairs[waterlogged=true]"); + + NativeBlockState result = new NativeStateMerger(ModdedBlockResolution.BLOCKS).merge(deferred, update); + + assertEquals(Direction.WEST, ((ModdedBlockState) result).handle().getValue(StairBlock.FACING)); + assertTrue(((ModdedBlockState) result).handle().getValue(StairBlock.WATERLOGGED)); + assertEquals("custom:stairs", result.deferredPlacementKey()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStructureFailureContractTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStructureFailureContractTest.java index be6e017b2..7d51a63f0 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStructureFailureContractTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/NativeStructureFailureContractTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.modded; -import art.arcane.iris.nativegen.NativeStructureGenerationException; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureGenerationException; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import org.junit.BeforeClass; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/api/ModdedCustomContentRegistryTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/api/ModdedCustomContentRegistryTest.java index 694276da5..4afc75ccb 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/api/ModdedCustomContentRegistryTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/api/ModdedCustomContentRegistryTest.java @@ -1,6 +1,10 @@ package art.arcane.iris.modded.api; -import net.minecraft.resources.Identifier; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEntityRuntime; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeSpawnedEntity; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import org.junit.BeforeClass; import org.junit.Test; import java.util.Collection; @@ -11,8 +15,45 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class ModdedCustomContentRegistryTest { + @BeforeClass + public static void bootstrapRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void entityProviderReceivesCanonicalKeyAndOriginalWorldAndPosition() { + ModdedDataProvider provider = mock(ModdedDataProvider.class); + NativeEntityRuntime runtime = mock(NativeEntityRuntime.class); + NativeSpawnedEntity entity = mock(NativeSpawnedEntity.class); + NativeEntityRuntime.Position position = new NativeEntityRuntime.Position(2.5, 63.5, -4.5); + NativeEntityRuntime.CustomSpawn canonical = new NativeEntityRuntime.CustomSpawn(runtime, position, "provider_test:mob"); + when(provider.modId()).thenReturn("provider_entity_test"); + when(provider.isReady()).thenReturn(true); + when(provider.isValidProvider("provider_test:mob", ModdedDataType.ENTITY)).thenReturn(true); + when(provider.spawnMob(canonical)).thenReturn(entity); + ModdedCustomContentRegistry.Discovery discovery = ModdedCustomContentRegistry.discover(List.of(provider)); + try { + assertSame(entity, ModdedCustomContentRegistry.spawnMob( + new NativeEntityRuntime.CustomSpawn(runtime, position, " provider_test:mob[variant=a] "))); + verify(provider).spawnMob(canonical); + } finally { + discovery.rollback(); + } + } + + @Test + public void unclaimedDeferredBlockDoesNotReadTheWorld() { + ModdedCustomContentRegistry.processBlockPlacement(null, "provider_test:unclaimed", () -> { + throw new AssertionError("unclaimed placement read the world"); + }); + } + @Test public void publishesProvidersOnlyAfterCompleteDiscoveryAndCanRollBack() { String modId = "iris_discovery_success"; @@ -65,12 +106,12 @@ public class ModdedCustomContentRegistryTest { } @Override - public Collection getTypes(ModdedDataType type) { + public Collection getTypes(ModdedDataType type) { return List.of(); } @Override - public boolean isValidProvider(Identifier id, ModdedDataType type) { + public boolean isValidProvider(String id, ModdedDataType type) { return false; } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedCommandParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedCommandParityTest.java index c42ae4518..ab6061c93 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedCommandParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedCommandParityTest.java @@ -6,6 +6,7 @@ import art.arcane.iris.world.lifecycle.LifecycleOperationCoordinator; import art.arcane.iris.pack.PackDownloader; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.tree.CommandNode; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; import net.minecraft.SharedConstants; import net.minecraft.commands.CommandSourceStack; import net.minecraft.server.Bootstrap; @@ -27,7 +28,7 @@ public class IrisModdedCommandParityTest { Bootstrap.bootStrap(); CommandDispatcher dispatcher = new CommandDispatcher<>(); - IrisModdedCommands.register(dispatcher); + IrisModdedCommands.register(new NativeCommandRegistration(dispatcher)); CommandNode iris = child(dispatcher.getRoot(), "iris"); CommandNode language = child(iris, "language"); @@ -183,7 +184,7 @@ public class IrisModdedCommandParityTest { Bootstrap.bootStrap(); CommandDispatcher dispatcher = new CommandDispatcher<>(); - IrisModdedCommands.register(dispatcher); + IrisModdedCommands.register(new NativeCommandRegistration(dispatcher)); CommandNode iris = child(dispatcher.getRoot(), "iris"); assertNotNull("iris", iris.getCommand()); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedStructureCommandTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedStructureCommandTest.java index 5356d452f..dfe5b19eb 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedStructureCommandTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/IrisModdedStructureCommandTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.modded.command; import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; -import art.arcane.iris.nativegen.NativeStructureLocateResults; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureLocateResults; import com.mojang.datafixers.util.Pair; import net.minecraft.core.BlockPos; import org.junit.Test; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedDustRevealerTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedDustRevealerTest.java index 9ed81e8cc..c1e18ec8b 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedDustRevealerTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedDustRevealerTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.modded.command; -import net.minecraft.core.BlockPos; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; import org.junit.Test; import java.util.List; @@ -25,29 +25,29 @@ public class ModdedDustRevealerTest { @Test public void revealTraversalUsesDiagonalAdjacencyButNotDisconnectedBlocks() { - Set object = Set.of( - new BlockPos(0, 0, 0), - new BlockPos(1, 1, 1), - new BlockPos(3, 3, 3)); - List hits = ModdedDustRevealer.collect( - new BlockPos(0, 0, 0), + Set object = Set.of( + new NativeBlockPoint(0, 0, 0), + new NativeBlockPoint(1, 1, 1), + new NativeBlockPoint(3, 3, 3)); + List hits = ModdedDustRevealer.collect( + new NativeBlockPoint(0, 0, 0), "object", -64, -64, 320, new AtomicBoolean(), (int x, int relativeY, int z) -> - object.contains(new BlockPos(x, relativeY - 64, z)) + object.contains(new NativeBlockPoint(x, relativeY - 64, z)) ? "object" : null); - assertEquals(List.of(new BlockPos(0, 0, 0), new BlockPos(1, 1, 1)), hits); + assertEquals(List.of(new NativeBlockPoint(0, 0, 0), new NativeBlockPoint(1, 1, 1)), hits); } @Test public void cancelledRevealDoesNoTraversal() { - List hits = ModdedDustRevealer.collect( - new BlockPos(0, 0, 0), + List hits = ModdedDustRevealer.collect( + new NativeBlockPoint(0, 0, 0), "object", -64, -64, diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedLanguageCommandsTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedLanguageCommandsTest.java index 089ec44d6..ed6b95d38 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedLanguageCommandsTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedLanguageCommandsTest.java @@ -6,7 +6,7 @@ import art.arcane.volmlib.util.localization.PluginLanguageService; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.SharedConstants; import net.minecraft.commands.CommandSourceStack; -import net.minecraft.commands.Commands; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandRegistration; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.server.Bootstrap; @@ -67,13 +67,13 @@ public class ModdedLanguageCommandsTest { language.when(IrisLanguage::availableLocales).thenReturn(Set.of("de_DE")); language.when(() -> IrisLanguage.plain(DirectorHelpMessages.BACK)).thenReturn("Back"); CommandDispatcher dispatcher = new CommandDispatcher<>(); - dispatcher.register(Commands.literal("iris").then(ModdedLanguageCommands.tree())); + new NativeCommandRegistration(dispatcher).register(NativeCommandRegistration.literal("iris").then(ModdedLanguageCommands.tree())); assertEquals(1, dispatcher.execute("iris language", source)); } assertEquals(6, rendered.size()); - assertEquals(ModdedCommandFeedback.banner("/iris language"), rendered.getFirst()); + assertEquals(ModdedCommandFeedback.banner("/iris language").getString(), rendered.getFirst().getString()); assertEquals(new ClickEvent.RunCommand("/iris"), rendered.get(1).getStyle().getClickEvent()); assertEquals("Iris language: en_US", rendered.get(2).getString()); Component locale = rendered.get(3); @@ -82,7 +82,7 @@ public class ModdedLanguageCommandsTest { assertEquals(new ClickEvent.RunCommand("/iris language self de_DE"), locale.getSiblings().getFirst().getStyle().getClickEvent()); assertEquals(new ClickEvent.RunCommand("/iris language self reset"), rendered.get(4).getStyle().getClickEvent()); - assertEquals(ModdedCommandFeedback.footer(), rendered.getLast()); + assertEquals(ModdedCommandFeedback.footer().getString(), rendered.getLast().getString()); } private void assertPersonalCompletion(String requested, String selected, String expected) throws Exception { @@ -114,7 +114,7 @@ public class ModdedLanguageCommandsTest { try (MockedStatic language = mockStatic(IrisLanguage.class)) { language.when(IrisLanguage::selections).thenReturn(service); CommandDispatcher dispatcher = new CommandDispatcher<>(); - dispatcher.register(Commands.literal("iris").then(ModdedLanguageCommands.tree())); + new NativeCommandRegistration(dispatcher).register(NativeCommandRegistration.literal("iris").then(ModdedLanguageCommands.tree())); assertEquals(1, dispatcher.execute("iris language self " + requested, source)); assertTrue(rendered.isEmpty()); selection.complete(null); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedPregenMethodLifecycleTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedPregenMethodLifecycleTest.java new file mode 100644 index 000000000..fe0c8748f --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/ModdedPregenMethodLifecycleTest.java @@ -0,0 +1,104 @@ +package art.arcane.iris.modded.command; + +import art.arcane.iris.configuration.IrisSettings; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.modded.ModdedGenPool; +import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.MockedStatic; + +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ModdedPregenMethodLifecycleTest { + private MockedStatic settings; + private NativePregenRuntime runtime; + private ModdedPregenMethod method; + + @Before + public void prepare() { + settings = mockStatic(IrisSettings.class); + IrisSettings configured = new IrisSettings(); + settings.when(IrisSettings::get).thenReturn(configured); + runtime = mock(NativePregenRuntime.class); + when(runtime.worldIdentity()).thenReturn("example:terrain"); + Engine engine = mock(Engine.class); + when(engine.getHeight()).thenReturn(384); + method = new ModdedPregenMethod(new ModdedPregenMethod.Configuration(runtime, engine, true)); + } + + @After + public void cleanup() { + settings.close(); + } + + @Test + public void periodicSaveRunsOnlyAfterTheServerExecutesIt() { + method.save(); + verify(runtime, never()).save(); + ArgumentCaptor queued = ArgumentCaptor.forClass(Runnable.class); + verify(runtime).execute(queued.capture()); + when(runtime.isServerThread()).thenReturn(true); + queued.getValue().run(); + verify(runtime).save(); + } + + @Test + public void shutdownCanClaimTheFinalSaveWithoutQueuingWorkToItsOwnThread() { + when(runtime.isServerThread()).thenReturn(true); + assertTrue(method.deferFinalSaveToServerThread()); + method.close(); + assertTrue(method.hasPendingFinalSave()); + verify(runtime, never()).save(); + method.completeDeferredFinalSave(); + method.completeDeferredFinalSave(); + assertFalse(method.hasPendingFinalSave()); + verify(runtime, times(1)).save(); + verify(runtime, never()).execute(any()); + } + + @Test + public void asynchronousCallerCannotClaimTheShutdownSave() { + assertFalse(method.deferFinalSaveToServerThread()); + assertThrows(IllegalStateException.class, method::completeDeferredFinalSave); + verify(runtime, never()).save(); + } + + @Test + public void failedFinalSaveStillRestoresTheEmptyServerPauseSetting() { + AtomicInteger pause = new AtomicInteger(60); + when(runtime.supportsPauseWhenEmpty()).thenReturn(true); + when(runtime.pauseWhenEmptySeconds()).thenAnswer(call -> pause.get()); + doAnswer(call -> { + pause.set(call.getArgument(0, Integer.class)); + return null; + }).when(runtime).pauseWhenEmptySeconds(anyInt()); + when(runtime.isServerThread()).thenReturn(true); + IllegalStateException failure = new IllegalStateException("save failed"); + doThrow(failure).when(runtime).save(); + try (MockedStatic pool = mockStatic(ModdedGenPool.class)) { + method.init(); + assertEquals(0, pause.get()); + assertSame(failure, assertThrows(IllegalStateException.class, method::close)); + } + assertEquals(60, pause.get()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeEditWorldTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeEditWorldTest.java new file mode 100644 index 000000000..ea6a9437b --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeEditWorldTest.java @@ -0,0 +1,95 @@ +package art.arcane.iris.modded.command; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeEditWorld; +import net.minecraft.SharedConstants; +import net.minecraft.core.BlockPos; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NativeEditWorldTest { + @BeforeClass + public static void bootstrapRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void repeatedWritesKeepTheOriginalUndoState() { + ServerLevel level = mock(ServerLevel.class); + BlockPos point = new BlockPos(2, 64, 3); + when(level.getBlockState(point)).thenReturn(Blocks.STONE.defaultBlockState(), Blocks.DIRT.defaultBlockState()); + NativeEditWorld.EditSession session = new NativeEditWorld(new ModdedPlatformWorld(level)) + .editSession(new NativeEditWorld.EditOptions(1, 256, true)); + + assertEquals(NativeEditWorld.WriteResult.BLOCK, session.set(2, 64, 3, ModdedBlockState.of(Blocks.DIRT.defaultBlockState(), null))); + assertEquals(NativeEditWorld.WriteResult.AIR, session.set(2, 64, 3, ModdedBlockState.of(Blocks.AIR.defaultBlockState(), null))); + assertEquals(1, session.restore(failure -> { throw new AssertionError(failure.error()); })); + verify(level).setBlock(point, Blocks.STONE.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE); + } + + @Test + public void protectedBedrockAndOutOfRangeWritesNeverEnterUndo() { + ServerLevel level = mock(ServerLevel.class); + when(level.getBlockState(any())).thenReturn(Blocks.BEDROCK.defaultBlockState()); + NativeEditWorld.EditSession session = new NativeEditWorld(new ModdedPlatformWorld(level)) + .editSession(new NativeEditWorld.EditOptions(1, 256, true)); + ModdedBlockState stone = ModdedBlockState.of(Blocks.STONE.defaultBlockState(), null); + + assertEquals(NativeEditWorld.WriteResult.SKIPPED, session.set(0, 0, 0, stone)); + assertEquals(NativeEditWorld.WriteResult.SKIPPED, session.set(0, 256, 0, stone)); + assertEquals(NativeEditWorld.WriteResult.SKIPPED, session.set(0, 64, 0, stone)); + assertTrue(session.empty()); + verify(level, never()).setBlock(any(), any(), anyInt()); + } + + @Test + public void captureOmitsOnlyRegularAirAndPreservesCaveAir() { + ServerLevel level = mock(ServerLevel.class); + when(level.getBlockState(any())).thenAnswer(invocation -> { + BlockPos pos = invocation.getArgument(0); + return pos.getX() == 0 ? Blocks.AIR.defaultBlockState() : Blocks.CAVE_AIR.defaultBlockState(); + }); + NativeEditWorld.CaptureTarget target = mock(NativeEditWorld.CaptureTarget.class); + new NativeEditWorld(new ModdedPlatformWorld(level)).capture( + new NativeEditWorld.Bounds(new NativeBlockPoint(0, 0, 0), new NativeBlockPoint(1, 0, 0)), target); + + verify(target).block(1, 0, 0, ModdedBlockState.of(Blocks.CAVE_AIR.defaultBlockState(), null)); + verify(target, never()).block(eq(0), anyInt(), anyInt(), any()); + } + + @Test + public void reopenedDimensionDoesNotAcceptTheOldWorldIdentity() { + MinecraftServer server = mock(MinecraftServer.class); + ServerLevel original = mock(ServerLevel.class); + ServerLevel replacement = mock(ServerLevel.class); + when(original.getServer()).thenReturn(server); + when(original.dimension()).thenReturn(Level.OVERWORLD); + when(server.getLevel(Level.OVERWORLD)).thenReturn(original, replacement); + NativeEditWorld world = new NativeEditWorld(new ModdedPlatformWorld(original)); + + assertTrue(world.current()); + assertFalse(world.current()); + assertTrue(world.represents(new ModdedPlatformWorld(original))); + assertFalse(world.represents(new ModdedPlatformWorld(replacement))); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeTaggedItemsTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeTaggedItemsTest.java new file mode 100644 index 000000000..286f38890 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/command/NativeTaggedItemsTest.java @@ -0,0 +1,55 @@ +package art.arcane.iris.modded.command; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeCommandText; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTaggedItems; +import net.minecraft.SharedConstants; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.server.Bootstrap; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.item.Items; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +public class NativeTaggedItemsTest { + @BeforeClass + public static void bootstrapRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + if (!Items.BLAZE_ROD.builtInRegistryHolder().areComponentsBound()) { + Items.BLAZE_ROD.builtInRegistryHolder().bindComponents(DataComponentMap.builder() + .set(DataComponents.MAX_STACK_SIZE, 64).build()); + } + } + + @Test + public void toolRetainsNameLoreFlagsAndHiddenUnbreakableMarker() { + NativeItemStack item = NativeTaggedItems.create(new NativeTaggedItems.Options("minecraft:blaze_rod", + NativeCommandText.literal("Selection"), List.of(NativeCommandText.literal("First corner")), + "selection_tool", true, true)); + + assertSame(Items.BLAZE_ROD, item.stack().getItem()); + assertEquals("Selection", item.stack().get(DataComponents.CUSTOM_NAME).getString()); + assertEquals("First corner", item.stack().get(DataComponents.LORE).lines().getFirst().getString()); + assertTrue(item.stack().get(DataComponents.CUSTOM_DATA).copyTag().getBooleanOr("selection_tool", false)); + assertTrue(item.stack().get(DataComponents.ENCHANTMENT_GLINT_OVERRIDE)); + assertTrue(item.stack().has(DataComponents.UNBREAKABLE)); + assertTrue(item.stack().get(DataComponents.TOOLTIP_DISPLAY).hiddenComponents().contains(DataComponents.UNBREAKABLE)); + } + + @Test + public void editingSoundKeysResolveToTheOriginalEvents() { + assertSame(SoundEvents.END_PORTAL_FRAME_FILL, BuiltInRegistries.SOUND_EVENT.getValue(Identifier.parse("minecraft:block.end_portal_frame.fill"))); + assertSame(SoundEvents.LODESTONE_COMPASS_LOCK, BuiltInRegistries.SOUND_EVENT.getValue(Identifier.parse("minecraft:item.lodestone_compass.lock"))); + assertSame(SoundEvents.AMETHYST_BLOCK_CHIME, BuiltInRegistries.SOUND_EVENT.getValue(Identifier.parse("minecraft:block.amethyst_block.chime"))); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedChunkUpdateServiceTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedChunkUpdateServiceTest.java index ae516ecb1..0e0279392 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedChunkUpdateServiceTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedChunkUpdateServiceTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.modded.service; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineMetrics; import art.arcane.iris.world.IrisWorld; @@ -49,13 +51,14 @@ public class ModdedChunkUpdateServiceTest { when(engine.getMetrics()).thenReturn(new EngineMetrics(8)); ServerLevel level = mock(ServerLevel.class); when(level.getMinY()).thenReturn(-64); - when(level.getMaxY()).thenReturn(320); + when(level.getMaxY()).thenReturn(319); + when(level.getHeight()).thenReturn(384); BlockPos position = new BlockPos(15, -59, 14); BlockState falling = Blocks.WATER.defaultBlockState().setValue(LiquidBlock.LEVEL, 8); when(level.getBlockState(position)).thenReturn(falling); UpdateRecordingMantleChunk chunk = new UpdateRecordingMantleChunk(); - new ModdedChunkUpdateService().runUpdatePass(engine, level, 0, 0, chunk); + new ModdedChunkUpdateService().runUpdatePass(engine, new ModdedPlatformWorld(level), 0, 0, chunk); assertEquals(8, falling.getValue(LiquidBlock.LEVEL).intValue()); if (falling.getFluidState().hasProperty(FlowingFluid.FALLING)) { diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentationTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentationTest.java index ecd4cf37a..01df6089d 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentationTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedTreeFellerPresentationTest.java @@ -5,6 +5,7 @@ import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; import net.minecraft.server.Bootstrap; import net.minecraft.world.item.ItemStack; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeItemStack; import net.minecraft.world.item.Items; import org.junit.BeforeClass; import org.junit.Test; @@ -40,15 +41,15 @@ public class ModdedTreeFellerPresentationTest { @Test public void compatibleDropsConsolidateWithoutExceedingStackLimits() { - List drops = ModdedTreeFellerPresentation.consolidateDrops(List.of( - new ItemStack(Items.OAK_LOG, 48), - new ItemStack(Items.OAK_LOG, 48), - new ItemStack(Items.BIRCH_LOG, 3) + List drops = ModdedTreeFellerPresentation.consolidateDrops(List.of( + NativeItemStack.of(new ItemStack(Items.OAK_LOG, 48)), + NativeItemStack.of(new ItemStack(Items.OAK_LOG, 48)), + NativeItemStack.of(new ItemStack(Items.BIRCH_LOG, 3)) )); assertEquals(3, drops.size()); - assertEquals(64, drops.get(0).getCount()); - assertEquals(32, drops.get(1).getCount()); - assertEquals(3, drops.get(2).getCount()); + assertEquals(64, drops.get(0).count()); + assertEquals(32, drops.get(1).count()); + assertEquals(3, drops.get(2).count()); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedWorldBoundaryTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedWorldBoundaryTest.java index ea094179e..926200797 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedWorldBoundaryTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/service/ModdedWorldBoundaryTest.java @@ -3,6 +3,10 @@ package art.arcane.iris.modded.service; import art.arcane.iris.world.IrisWorldBoundary; import art.arcane.iris.world.IrisWorldBoundaryCenter; import net.minecraft.world.level.border.WorldBorder; +import net.minecraft.server.level.ServerLevel; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -17,7 +21,7 @@ public class ModdedWorldBoundaryTest { worldBorder.setSafeZone(2.5D); worldBorder.setDamagePerBlock(0.75D); - ModdedStudioHotloadService.applyWorldBoundary(worldBorder, null); + ModdedStudioHotloadService.applyWorldBoundary(world(worldBorder), null); assertEquals(3.5D, worldBorder.getCenterX(), 0D); assertEquals(-4.5D, worldBorder.getCenterZ(), 0D); @@ -37,7 +41,7 @@ public class ModdedWorldBoundaryTest { .setDamageBuffer(6.5D) .setDamageAmount(0.4D); - ModdedStudioHotloadService.applyWorldBoundary(worldBorder, boundary); + ModdedStudioHotloadService.applyWorldBoundary(world(worldBorder), boundary); assertEquals(12.5D, worldBorder.getCenterX(), 0D); assertEquals(-7.25D, worldBorder.getCenterZ(), 0D); @@ -46,4 +50,9 @@ public class ModdedWorldBoundaryTest { assertEquals(6.5D, worldBorder.getSafeZone(), 0D); assertEquals(0.4D, worldBorder.getDamagePerBlock(), 0D); } + private static ModdedPlatformWorld world(WorldBorder border) { + ServerLevel level = mock(ServerLevel.class); + when(level.getWorldBorder()).thenReturn(border); + return new ModdedPlatformWorld(level); + } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureReferenceRepairTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureReferenceRepairTest.java index e87f2c093..4ab32dca8 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureReferenceRepairTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureReferenceRepairTest.java @@ -1,5 +1,17 @@ package art.arcane.iris.nativegen; +import art.arcane.iris.structure.nativegen.IrisStructurePolicy; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceRepair; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureOwnershipRecordView; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureOwnershipFingerprint; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureVerticalPlacer; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureReferenceEnvelope; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.Engine; @@ -14,7 +26,7 @@ import art.arcane.iris.structure.nativegen.IrisNativeStructure; import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.nativegen.NativeStructureGenerationStatus; import art.arcane.volmlib.util.collection.KList; import com.github.benmanes.caffeine.cache.Cache; @@ -75,18 +87,16 @@ public class NativeStructureReferenceRepairTest { NativeStructureVerticalPlacer.alignOceanMonumentToSeaLevel( generated, 0, 80, -64, 320); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart wrapped = NativeStructureReferenceEnvelope.wrap( generated, structure, 0, terrain); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - "minecraft:monument", - wrapped, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY()), - NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + "minecraft:monument", wrapped, NativeStructureReferenceEnvelope.referenceBounds(wrapped, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(wrapped).minY())); PiecesContainer regeneratedPieces = OceanMonumentStructure.regeneratePiecesAfterLoad( origin, seed, new PiecesContainer(wrapped.getPieces())); StructureStart reloaded = new StructureStart(structure, origin, 0, regeneratedPieces); @@ -128,15 +138,13 @@ public class NativeStructureReferenceRepairTest { NativeStructureVerticalPlacer.alignOceanMonumentToSeaLevel( generated, 0, 80, -64, 320); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); StructureStart start = NativeStructureReferenceEnvelope.wrap( generated, structure, 0, terrain); - NativeStructureOwnershipRecord ownership = NativeStructureOwnershipFingerprint.capture( - structureKey, - start, - plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY()), - NativeStructureReferenceEnvelope.referenceBounds(start, structure, terrain)); + NativeStructureOwnershipRecord ownership = NativeStructureOwnershipRecord.capture( + NativeStructureOwnershipFingerprint.capture( + structureKey, start, NativeStructureReferenceEnvelope.referenceBounds(start, structure, terrain)), plan(origin, NativeStructureReferenceEnvelope.contentBounds(start).minY())); IrisDimension dimension = new IrisDimension(); dimension.getImportedStructures().getDisabled().add(structureKey); IrisData data = allocateWithoutConstructor(IrisData.class); @@ -165,7 +173,7 @@ public class NativeStructureReferenceRepairTest { registry, serverLevel, originChunk, emptyScannedChunk); ProtoChunk coveredChunk = emptyChunk(coveredTarget); NativeStructureReferenceRepair.createReferences( - engine, level, structureManager, coveredChunk); + new IrisStructurePolicy(engine), level, structureManager, coveredChunk); assertSame(start, originChunk.getStartForStructure(structure)); assertTrue(originChunk.getStartForStructure(structure).isValid()); @@ -173,7 +181,7 @@ public class NativeStructureReferenceRepairTest { ProtoChunk uncoveredChunk = emptyChunk(uncoveredTarget); NativeStructureReferenceRepair.createReferences( - engine, level, structureManager, uncoveredChunk); + new IrisStructurePolicy(engine), level, structureManager, uncoveredChunk); assertSame(start, originChunk.getStartForStructure(structure)); assertTrue(originChunk.getStartForStructure(structure).isValid()); @@ -248,9 +256,9 @@ public class NativeStructureReferenceRepairTest { for (ChunkPos candidate : candidates) { if (!ownership.covers(candidate.x(), candidate.z()) && Math.abs(candidate.x() - originX) - <= NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS + <= StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS && Math.abs(candidate.z() - originZ) - <= NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS) { + <= StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS) { return candidate; } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureVacuumParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureVacuumParityTest.java index 0895299cc..66c9c714b 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureVacuumParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/nativegen/NativeStructureVacuumParityTest.java @@ -1,7 +1,11 @@ package art.arcane.iris.nativegen; +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureTerrainIntegrator; + +import art.arcane.volmlib.nativelib.minecraft26_2.terrain.NativeStructureSurfaceFitter; + import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import net.minecraft.SharedConstants; import net.minecraft.core.HolderSet; import net.minecraft.server.Bootstrap; @@ -42,7 +46,7 @@ public class NativeStructureVacuumParityTest { NativeStructureTerrainIntegrator.TerrainTarget target = new NativeStructureTerrainIntegrator.TerrainTarget( "test:vacuum", start, - new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); assertTrue(NativeStructureSurfaceFitter.requiresSurfaceTerrain(target)); assertFalse(NativeStructureTerrainIntegrator.clearsLegacyTemplateAir( diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/BrigadierSourceMapperTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/BrigadierSourceMapperTest.java new file mode 100644 index 000000000..4a3055b39 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/BrigadierSourceMapperTest.java @@ -0,0 +1,75 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.tree.LiteralCommandNode; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +public class BrigadierSourceMapperTest { + @Test + public void preservesArgumentsSourceIdentityPermissionsAndAliasRedirects() throws Exception { + CommandDispatcher dispatcher = new CommandDispatcher<>(); + Object allowed = new Object(); + List observed = new ArrayList<>(); + BrigadierSourceMapper mapper = mapper(dispatcher); + LiteralCommandNode root = mapper.register(LiteralArgumentBuilder.literal("terrain") + .requires(source -> source.handle() == allowed) + .then(RequiredArgumentBuilder.argument("count", IntegerArgumentType.integer()) + .executes(context -> { + observed.add(context.getSource().handle()); + assertEquals("count", context.getNodes().getLast().getNode().getName()); + return IntegerArgumentType.getInteger(context, "count"); + }))); + mapper.register(LiteralArgumentBuilder.literal("ter").redirect(root)); + + assertEquals(7, dispatcher.execute("terrain 7", allowed)); + assertEquals(13, dispatcher.execute("ter 13", allowed)); + assertEquals(2, observed.size()); + assertSame(allowed, observed.getFirst()); + assertSame(allowed, observed.getLast()); + assertEquals(0, dispatcher.parse("terrain 7", new Object()).getContext().getNodes().size()); + } + + @Test + public void mapsSuggestionsAndForkedSources() throws Exception { + CommandDispatcher dispatcher = new CommandDispatcher<>(); + Object original = new Object(); + Object redirected = new Object(); + BrigadierSourceMapper mapper = mapper(dispatcher); + LiteralCommandNode root = mapper.register(LiteralArgumentBuilder.literal("terrain") + .then(RequiredArgumentBuilder.argument("pack", StringArgumentType.word()) + .suggests((context, builder) -> { + assertSame(original, context.getSource().handle()); + return builder.suggest("forest").buildFuture(); + }) + .executes(context -> { + assertSame(redirected, context.getSource().handle()); + assertEquals("forest", StringArgumentType.getString(context, "pack")); + return 3; + }))); + mapper.register(LiteralArgumentBuilder.literal("forked") + .fork(root, context -> List.of(new Source(redirected)))); + + Suggestions suggestions = dispatcher.getCompletionSuggestions(dispatcher.parse("terrain f", original)).get(); + assertEquals("forest", suggestions.getList().getFirst().getText()); + assertEquals(1, dispatcher.execute("forked forest", original)); + } + + private static BrigadierSourceMapper mapper(CommandDispatcher dispatcher) { + return new BrigadierSourceMapper<>(dispatcher, + new BrigadierSourceMapper.SourceMapping<>(Source::new, Source::handle)); + } + + private record Source(Object handle) { + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLevelDataAttachmentTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedLevelDataAttachmentTest.java similarity index 94% rename from adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLevelDataAttachmentTest.java rename to adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedLevelDataAttachmentTest.java index dc6ed6bde..ce0606edc 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedLevelDataAttachmentTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedLevelDataAttachmentTest.java @@ -1,4 +1,6 @@ -package art.arcane.iris.modded; +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; import org.junit.Test; diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedServerLevelsTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedServerLevelsTest.java similarity index 94% rename from adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedServerLevelsTest.java rename to adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedServerLevelsTest.java index 0b11aafd0..2840ce76d 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedServerLevelsTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/ModdedServerLevelsTest.java @@ -1,4 +1,6 @@ -package art.arcane.iris.modded; +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; import org.junit.Test; diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeChunkRegenerationTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeChunkRegenerationTest.java new file mode 100644 index 000000000..952a67033 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeChunkRegenerationTest.java @@ -0,0 +1,97 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import net.minecraft.SharedConstants; +import net.minecraft.core.BlockPos; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; +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.chunk.LevelChunk; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.levelgen.Heightmap; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.MockedStatic; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.lang.reflect.Field; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NativeChunkRegenerationTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void replacesBlocksAndEntitiesBeforePolicyThenRebuildsBiomeAndLighting() throws Exception { + ServerLevel level = mock(ServerLevel.class, RETURNS_DEEP_STUBS); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + LevelChunk chunk = mock(LevelChunk.class); + LevelChunkSection section = mock(LevelChunkSection.class); + ThreadedLevelLightEngine lights = mock(ThreadedLevelLightEngine.class); + when(level.getChunk(0, 0)).thenReturn(chunk); + when(chunk.getPos()).thenReturn(new ChunkPos(0, 0)); + when(chunk.getSectionsCount()).thenReturn(1); + when(chunk.getSection(0)).thenReturn(section); + when(level.getChunkSource().getLightEngine()).thenReturn(lights); + ChunkMap tracking = mock(ChunkMap.class); + Field chunkMap = ServerChunkCache.class.getDeclaredField("chunkMap"); + chunkMap.setAccessible(true); + chunkMap.set(level.getChunkSource(), tracking); + when(tracking.getPlayers(any(), eq(false))).thenReturn(List.of()); + when(section.setBlockState(anyInt(), anyInt(), anyInt(), any(), anyBoolean())) + .thenReturn(Blocks.AIR.defaultBlockState()); + BlockPos oldTile = new BlockPos(3, 4, 5); + when(chunk.getBlockEntities()).thenReturn(Map.of(oldTile, mock(BlockEntity.class))); + Entity entity = mock(Entity.class); + when(level.getEntities(isNull(Entity.class), any(), any())).thenReturn(List.of(entity)); + NativeBiomeResolver biomes = mock(NativeBiomeResolver.class); + List order = new ArrayList<>(); + doAnswer(invocation -> { + order.add("biomes"); + return null; + }).when(biomes).fill(chunk, level); + doAnswer(invocation -> { + order.add("unsaved"); + return null; + }).when(chunk).markUnsaved(); + BlockState glowing = Blocks.GLOWSTONE.defaultBlockState(); + NativeChunkRegeneration.Replacement replacement = new NativeChunkRegeneration.Replacement( + 0, 0, 0, 16, (x, y, z) -> x == 1 && y == 2 && z == 3 + ? ModdedBlockState.of(glowing, null) : null, biomes); + try (MockedStatic ignored = mockStatic(Heightmap.class)) { + new NativeChunkRegeneration(world).apply(replacement, () -> order.add("policy")); + } + assertEquals(List.of("policy", "biomes", "unsaved"), order); + verify(entity).discard(); + verify(chunk).removeBlockEntity(oldTile); + verify(section).acquire(); + verify(section).release(); + verify(section).setBlockState(1, 2, 3, glowing, false); + verify(lights).checkBlock(new BlockPos(1, 2, 3)); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeCommandTextTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeCommandTextTest.java new file mode 100644 index 000000000..5d6f92b3a --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeCommandTextTest.java @@ -0,0 +1,54 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.ClickEvent; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.HoverEvent; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class NativeCommandTextTest { + @Test + public void preservesMutableCompositionAndInteractiveStyle() { + NativeCommandText text = NativeCommandText.literal("World") + .withStyle(style -> style.withColor(0x123456).withBold(true) + .runCommand("/terrain list").hover(NativeCommandText.literal("Open list"))); + assertSame(text, text.append(" ready")); + assertEquals("World ready", text.getString()); + Component component = text.component(); + assertEquals(0x123456, component.getStyle().getColor().getValue()); + assertTrue(component.getStyle().isBold()); + assertEquals("/terrain list", ((ClickEvent.RunCommand) component.getStyle().getClickEvent()).command()); + assertEquals("Open list", ((HoverEvent.ShowText) component.getStyle().getHoverEvent()).value().getString()); + } + + @Test + @SuppressWarnings("unchecked") + public void preservesLazySuccessEvaluationAndFailureText() { + CommandSourceStack handle = mock(CommandSourceStack.class); + NativeCommandSource source = new NativeCommandSource(handle); + AtomicInteger evaluations = new AtomicInteger(); + source.sendSuccess(() -> { + evaluations.incrementAndGet(); + return NativeCommandText.literal("Completed"); + }, true); + assertEquals(0, evaluations.get()); + ArgumentCaptor> success = ArgumentCaptor.forClass(Supplier.class); + verify(handle).sendSuccess(success.capture(), eq(true)); + assertEquals("Completed", success.getValue().get().getString()); + assertEquals(1, evaluations.get()); + NativeCommandText failure = NativeCommandText.literal("Failed"); + source.sendFailure(failure); + verify(handle).sendFailure(failure.component()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeGeneratorOwnershipTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeGeneratorOwnershipTest.java new file mode 100644 index 000000000..a6d8861b5 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeGeneratorOwnershipTest.java @@ -0,0 +1,69 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.iris.modded.IrisModdedChunkGenerator; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorContext; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeWorldGenerators; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import com.mojang.serialization.JsonOps; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.chunk.ChunkGenerator; +import org.junit.Test; +import org.junit.BeforeClass; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; + +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeGeneratorOwnershipTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void unboundGeneratorKeepsMetadataAndSerializedFieldNames() { + IrisModdedChunkGenerator owner = new IrisModdedChunkGenerator( + new NativeGeneratorContext(mock(BiomeSource.class), "example:surface")); + NativeModdedChunkGenerator nativeGenerator = (NativeModdedChunkGenerator) owner.nativeGenerator(); + assertSame(owner, nativeGenerator.owner()); + assertEquals(-64, nativeGenerator.getMinY()); + assertEquals(384, nativeGenerator.getGenDepth()); + assertEquals(63, nativeGenerator.getSeaLevel()); + assertEquals("example:surface", nativeGenerator.context().dimensionKey()); + Set fields = IrisModdedChunkGenerator.DEFINITION.codec().keys(JsonOps.INSTANCE) + .map(value -> value.getAsString()).collect(Collectors.toSet()); + assertEquals(Set.of("biome_source", "dimension"), fields); + } + + @Test + public void worldLookupReturnsPolicyOwnerOnlyWhileNativeGeneratorIsInstalled() { + IrisModdedChunkGenerator owner = new IrisModdedChunkGenerator( + new NativeGeneratorContext(mock(BiomeSource.class), "example:surface")); + NativeModdedChunkGenerator nativeGenerator = (NativeModdedChunkGenerator) owner.nativeGenerator(); + ServerLevel level = mock(ServerLevel.class); + ServerChunkCache chunks = mock(ServerChunkCache.class); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + when(level.getChunkSource()).thenReturn(chunks); + when(chunks.getGenerator()).thenReturn(nativeGenerator); + assertSame(owner, NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class)); + assertTrue(nativeGenerator.represents(world)); + + when(chunks.getGenerator()).thenReturn(mock(ChunkGenerator.class)); + assertNull(NativeWorldGenerators.find(world, IrisModdedChunkGenerator.class)); + assertFalse(nativeGenerator.represents(world)); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeHarvestSessionTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeHarvestSessionTest.java new file mode 100644 index 000000000..6dde6d988 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeHarvestSessionTest.java @@ -0,0 +1,113 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import net.minecraft.SharedConstants; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.ItemTags; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.InventoryMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collection; +import net.minecraft.core.Holder; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeHarvestSessionTest { + @BeforeClass + public static void bootstrap() throws ReflectiveOperationException { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + if (!Items.DIAMOND_AXE.builtInRegistryHolder().areComponentsBound()) { + Items.DIAMOND_AXE.builtInRegistryHolder().bindComponents(DataComponentMap.builder() + .set(DataComponents.MAX_STACK_SIZE, 1).set(DataComponents.MAX_DAMAGE, 1561) + .set(DataComponents.DAMAGE, 0).build()); + } + Method bindTags = Holder.Reference.class.getDeclaredMethod("bindTags", Collection.class); + bindTags.setAccessible(true); + bindTags.invoke(Items.DIAMOND_AXE.builtInRegistryHolder(), List.of(ItemTags.AXES)); + } + + @Test + public void normalizesVanillaOriginDamageAndHonorsPreservation() throws Exception { + Fixture fixture = Fixture.create(8); + fixture.item().get().setDamageValue(9); + assertTrue(fixture.session().normalizeOriginTool(true)); + assertEquals(8, fixture.item().get().getDamageValue()); + } + + @Test + public void failedDestructionRefundsOnlyTheReservedTool() throws Exception { + Fixture fixture = Fixture.create(8); + assertTrue(fixture.session().normalizeOriginTool(false)); + NativeHarvestSession.Reservation reservation = fixture.session().reserveToolDamage(false); + assertNotNull(reservation); + assertEquals(10, fixture.item().get().getDamageValue()); + fixture.session().refundToolDamage(reservation); + assertEquals(9, fixture.item().get().getDamageValue()); + + NativeHarvestSession.Reservation second = fixture.session().reserveToolDamage(false); + ItemStack replacement = new ItemStack(Items.DIAMOND_AXE); + replacement.setDamageValue(20); + fixture.item().set(replacement); + fixture.session().refundToolDamage(second); + assertEquals(20, fixture.item().get().getDamageValue()); + assertFalse(fixture.session().active()); + } + + @Test + public void breakingReservationCanBeRefundedAfterFailedDestruction() throws Exception { + Fixture fixture = Fixture.create(1559); + assertTrue(fixture.session().normalizeOriginTool(false)); + NativeHarvestSession.Reservation reservation = fixture.session().reserveToolDamage(false); + assertNotNull(reservation); + assertTrue(reservation.broke()); + assertTrue(fixture.item().get().isEmpty()); + fixture.session().refundToolDamage(reservation); + assertEquals(1560, fixture.item().get().getDamageValue()); + } + + private record Fixture(NativeHarvestSession session, AtomicReference item) { + private static Fixture create(int damage) throws Exception { + ServerLevel level = mock(ServerLevel.class); + ServerPlayer player = mock(ServerPlayer.class); + Inventory inventory = mock(Inventory.class); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + when(player.level()).thenReturn(level); + when(player.getInventory()).thenReturn(inventory); + ItemStack tool = new ItemStack(Items.DIAMOND_AXE); + tool.setDamageValue(damage); + AtomicReference item = new AtomicReference<>(tool); + when(inventory.getSelectedItem()).thenAnswer(invocation -> item.get()); + when(inventory.getItem(0)).thenAnswer(invocation -> item.get()); + doAnswer(invocation -> { item.set(invocation.getArgument(0)); return null; }) + .when(inventory).setSelectedItem(any(ItemStack.class)); + doAnswer(invocation -> { item.set(invocation.getArgument(1)); return null; }) + .when(inventory).setItem(eq(0), any(ItemStack.class)); + Field menu = player.getClass().getField("inventoryMenu"); + menu.setAccessible(true); + menu.set(player, mock(InventoryMenu.class)); + return new Fixture(new NativeHarvestSession(world, NativeProtocolPlayer.fromHandle(player)), item); + } + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeMixinRegistrationTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeMixinRegistrationTest.java new file mode 100644 index 000000000..9d07a1283 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeMixinRegistrationTest.java @@ -0,0 +1,101 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.junit.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class NativeMixinRegistrationTest { + @Test + public void sharedMixinConfigurationsNameBundledClasses() throws Exception { + ClassLoader loader = getClass().getClassLoader(); + for (String config : List.of("volmlib.entity.mixins.json", "volmlib.client.mixins.json")) { + assertConfiguration(loader, config); + } + } + + @Test + public void fabricMixinConfigurationAccompaniesItsLoader() throws Exception { + ClassLoader loader = getClass().getClassLoader(); + if (loader.getResource("art/arcane/volmlib/nativelib/minecraft26_2/fabric/NativeFabricBootstrap.class") != null) { + assertConfiguration(loader, "volmlib.fabric.mixins.json"); + } + } + + @Test + public void declaredMixinsAvoidArrayOwnedVirtualCalls() throws Exception { + ClassLoader loader = getClass().getClassLoader(); + List configurations = new ArrayList<>(List.of("volmlib.entity.mixins.json", "volmlib.client.mixins.json")); + if (loader.getResource("volmlib.fabric.mixins.json") != null) { + configurations.add("volmlib.fabric.mixins.json"); + } + List violations = new ArrayList<>(); + for (String configuration : configurations) { + for (String resource : configurationClasses(loader, configuration)) { + try (InputStream input = loader.getResourceAsStream(resource)) { + assertNotNull(resource, input); + new ClassReader(input).accept(new ClassVisitor(Opcodes.ASM9) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM9) { + @Override + public void visitMethodInsn(int opcode, String owner, String method, + String methodDescriptor, boolean isInterface) { + if (opcode == Opcodes.INVOKEVIRTUAL && owner.startsWith("[")) { + violations.add(resource + ": " + name + " -> " + owner + "." + method); + } + } + }; + } + }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + } + } + } + assertTrue("Array-owned calls cannot be transformed by Mixin: " + violations, violations.isEmpty()); + } + + private static void assertConfiguration(ClassLoader loader, String config) throws Exception { + for (String resource : configurationClasses(loader, config)) { + assertNotNull(config + ": " + resource, loader.getResource(resource)); + } + } + + private static List configurationClasses(ClassLoader loader, String config) throws Exception { + List classes = new ArrayList<>(); + try (InputStream stream = loader.getResourceAsStream(config)) { + assertNotNull(config, stream); + JsonObject document = JsonParser.parseReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).getAsJsonObject(); + String base = document.get("package").getAsString().replace('.', '/') + "/"; + for (String side : List.of("mixins", "client", "server")) { + JsonArray mixins = document.getAsJsonArray(side); + if (mixins == null) { + continue; + } + for (JsonElement mixin : mixins) { + String resource = base + mixin.getAsString().replace('.', '/') + ".class"; + classes.add(resource); + } + } + if (document.has("plugin")) { + String plugin = document.get("plugin").getAsString().replace('.', '/') + ".class"; + classes.add(plugin); + } + } + return classes; + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativePregenRuntimeTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativePregenRuntimeTest.java new file mode 100644 index 000000000..e0de9338d --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativePregenRuntimeTest.java @@ -0,0 +1,66 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativePregenRuntime; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.TicketType; +import net.minecraft.world.level.ChunkPos; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.CompletableFuture; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + +public class NativePregenRuntimeTest { + @BeforeClass + public static void bootstrapRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void chunkTicketAdmissionAndReleaseRunThroughTheServerExecutor() { + MinecraftServer server = mock(MinecraftServer.class); + ServerLevel level = mock(ServerLevel.class); + ServerChunkCache chunks = mock(ServerChunkCache.class); + when(level.getServer()).thenReturn(server); + when(level.getChunkSource()).thenReturn(chunks); + CompletableFuture loaded = new CompletableFuture<>(); + doReturn(loaded).when(chunks).addTicketAndLoadWithRadius(any(), eq(new ChunkPos(-2, 7)), eq(0)); + NativePregenRuntime runtime = NativeModdedPregenRuntime.from(new ModdedPlatformWorld(level)); + + CompletableFuture result = runtime.loadChunk(-2, 7); + verify(chunks, never()).addTicketAndLoadWithRadius(any(), any(), eq(0)); + ArgumentCaptor requests = ArgumentCaptor.forClass(Runnable.class); + verify(server).execute(requests.capture()); + requests.getValue().run(); + assertFalse(result.isDone()); + loaded.complete(new Object()); + assertTrue(result.join().successful()); + ArgumentCaptor ticket = ArgumentCaptor.forClass(TicketType.class); + verify(chunks).addTicketAndLoadWithRadius(ticket.capture(), eq(new ChunkPos(-2, 7)), eq(0)); + + runtime.releaseChunk(-2, 7); + verify(chunks, never()).removeTicketWithRadius(any(), any(), eq(0)); + verify(server, times(2)).execute(requests.capture()); + requests.getAllValues().getLast().run(); + ArgumentCaptor released = ArgumentCaptor.forClass(TicketType.class); + verify(chunks).removeTicketWithRadius(released.capture(), eq(new ChunkPos(-2, 7)), eq(0)); + assertSame(ticket.getValue(), released.getValue()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerLogMessagesTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerLogMessagesTest.java new file mode 100644 index 000000000..afe994936 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerLogMessagesTest.java @@ -0,0 +1,27 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class NativeServerLogMessagesTest { + @Test + public void recognizesNativeCategoriesWithoutChoosingSuppressionPolicy() { + assertEquals(NativeServerLogMessages.Kind.IGNORED_HEIGHTMAP, + NativeServerLogMessages.classify("net.minecraft.chunk", "Ignoring heightmap data for chunk [2, -3]")); + assertEquals(NativeServerLogMessages.Kind.RAID_PERSISTENCE, + NativeServerLogMessages.classify("net.minecraft.storage", "Could not save data net.minecraft.world.entity.raid.PersistentRaid")); + assertEquals(NativeServerLogMessages.Kind.DUPLICATE_ENTITY, + NativeServerLogMessages.classify("net.minecraft.world", "UUID of added entity already exists: example")); + } + + @Test + public void leavesOtherLoggersAndMessagesUnclassified() { + assertEquals(NativeServerLogMessages.Kind.OTHER, + NativeServerLogMessages.classify("example.plugin", "UUID of added entity already exists")); + assertEquals(NativeServerLogMessages.Kind.OTHER, + NativeServerLogMessages.classify("net.minecraft.world", "Could not load chunk")); + assertEquals(NativeServerLogMessages.Kind.OTHER, NativeServerLogMessages.classify(null, "message")); + assertEquals(NativeServerLogMessages.Kind.OTHER, NativeServerLogMessages.classify("net.minecraft.world", null)); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/MainWorldServiceInstanceRootTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerWorldLayoutTest.java similarity index 56% rename from adapters/modded-common/src/test/java/art/arcane/iris/modded/MainWorldServiceInstanceRootTest.java rename to adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerWorldLayoutTest.java index 4c9c2d746..f0a7301bb 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/MainWorldServiceInstanceRootTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeServerWorldLayoutTest.java @@ -1,4 +1,4 @@ -package art.arcane.iris.modded; +package art.arcane.volmlib.nativelib.minecraft26_2.modded; import org.junit.Test; @@ -13,22 +13,22 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -public class MainWorldServiceInstanceRootTest { +public class NativeServerWorldLayoutTest { @Test public void universeOptionIsReadInBothArgumentForms() { - assertEquals("worlds", MainWorldService.commandLineOption( + assertEquals("worlds", NativeServerWorldLayout.commandLineOption( List.of("nogui", "--universe", "worlds"), "universe")); - assertEquals("worlds", MainWorldService.commandLineOption( + assertEquals("worlds", NativeServerWorldLayout.commandLineOption( List.of("--universe=worlds", "nogui"), "universe")); - assertEquals("survival", MainWorldService.commandLineOption( + assertEquals("survival", NativeServerWorldLayout.commandLineOption( List.of("--universe", "worlds", "--world", "survival"), "world")); } @Test public void missingOrValuelessOptionsResolveToNull() { - assertNull(MainWorldService.commandLineOption(List.of("nogui"), "universe")); - assertNull(MainWorldService.commandLineOption(List.of(), "world")); - assertNull(MainWorldService.commandLineOption(List.of("nogui", "--world"), "world")); + assertNull(NativeServerWorldLayout.commandLineOption(List.of("nogui"), "universe")); + assertNull(NativeServerWorldLayout.commandLineOption(List.of(), "world")); + assertNull(NativeServerWorldLayout.commandLineOption(List.of("nogui", "--world"), "world")); } @Test @@ -36,10 +36,10 @@ public class MainWorldServiceInstanceRootTest { Path instanceRoot = Files.createTempDirectory("iris-instance"); Path elsewhere = Files.createTempDirectory("iris-elsewhere"); - assertEquals(instanceRoot, MainWorldService.universeRoot(instanceRoot, null)); - assertEquals(instanceRoot, MainWorldService.universeRoot(instanceRoot, "")); - assertEquals(instanceRoot.resolve("worlds"), MainWorldService.universeRoot(instanceRoot, "worlds")); - assertEquals(elsewhere, MainWorldService.universeRoot(instanceRoot, elsewhere.toString())); + assertEquals(instanceRoot, NativeServerWorldLayout.universeRoot(instanceRoot, null)); + assertEquals(instanceRoot, NativeServerWorldLayout.universeRoot(instanceRoot, "")); + assertEquals(instanceRoot.resolve("worlds"), NativeServerWorldLayout.universeRoot(instanceRoot, "worlds")); + assertEquals(elsewhere, NativeServerWorldLayout.universeRoot(instanceRoot, elsewhere.toString())); } @Test @@ -48,7 +48,7 @@ public class MainWorldServiceInstanceRootTest { Path world = Files.createDirectory(universe.resolve("survival")); assertEquals(world.toAbsolutePath().normalize(), - MainWorldService.resolveWorldRoot(universe, "survival")); + NativeServerWorldLayout.resolveWorldRoot(universe, "survival")); } @Test @@ -57,9 +57,9 @@ public class MainWorldServiceInstanceRootTest { Files.createDirectory(universe.resolve("survival")); IOException escaped = assertThrows(IOException.class, - () -> MainWorldService.resolveWorldRoot(universe, "../survival")); + () -> NativeServerWorldLayout.resolveWorldRoot(universe, "../survival")); IOException empty = assertThrows(IOException.class, - () -> MainWorldService.resolveWorldRoot(universe, ".")); + () -> NativeServerWorldLayout.resolveWorldRoot(universe, ".")); assertTrue(escaped.getMessage().contains("Unsafe world name")); assertTrue(empty.getMessage().contains("Unsafe world name")); @@ -69,12 +69,12 @@ public class MainWorldServiceInstanceRootTest { public void worldRootReportsMissingDirectoriesAsTheRecoverableCase() throws IOException { Path universe = Files.createTempDirectory("iris-universe"); - MainWorldService.MissingWorldRootException missingWorld = assertThrows( - MainWorldService.MissingWorldRootException.class, - () -> MainWorldService.resolveWorldRoot(universe, "survival")); - MainWorldService.MissingWorldRootException missingUniverse = assertThrows( - MainWorldService.MissingWorldRootException.class, - () -> MainWorldService.resolveWorldRoot(universe.resolve("absent"), "survival")); + NativeServerWorldLayout.MissingWorldRootException missingWorld = assertThrows( + NativeServerWorldLayout.MissingWorldRootException.class, + () -> NativeServerWorldLayout.resolveWorldRoot(universe, "survival")); + NativeServerWorldLayout.MissingWorldRootException missingUniverse = assertThrows( + NativeServerWorldLayout.MissingWorldRootException.class, + () -> NativeServerWorldLayout.resolveWorldRoot(universe.resolve("absent"), "survival")); assertTrue(missingWorld.getMessage().contains("world directory does not exist")); assertEquals(universe.resolve("survival").toAbsolutePath().normalize(), missingWorld.path()); @@ -87,8 +87,8 @@ public class MainWorldServiceInstanceRootTest { Path universe = Files.createTempDirectory("iris-universe"); IOException escaped = assertThrows(IOException.class, - () -> MainWorldService.resolveWorldRoot(universe, "../survival")); + () -> NativeServerWorldLayout.resolveWorldRoot(universe, "../survival")); - assertFalse(escaped instanceof MainWorldService.MissingWorldRootException); + assertFalse(escaped instanceof NativeServerWorldLayout.MissingWorldRootException); } } diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnBiomeTablesTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnBiomeTablesTest.java new file mode 100644 index 000000000..98a0a3e90 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeSpawnBiomeTablesTest.java @@ -0,0 +1,88 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationLease; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; +import art.arcane.volmlib.nativelib.terrain.NativeSpawnBiomePolicy; +import net.minecraft.SharedConstants; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.level.biome.Biome; +import org.junit.BeforeClass; +import org.junit.Test; +import java.util.Optional; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class NativeSpawnBiomeTablesTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void runtimeRetirementDropsMappingsAndRebuildsUnderAdmission() { + Fixture fixture = new Fixture(); + fixture.tables.initializeVanillaSpawnBiomes(fixture.registry); + fixture.tables.initializeVanillaSpawnBiomes(fixture.registry); + assertSame(fixture.vanilla, fixture.tables.vanillaSpawnBiome(fixture.custom.value())); + verify(fixture.policy, times(1)).populate(eq(1), any()); + verify(fixture.lease, times(1)).close(); + verify(fixture.scope, times(1)).close(); + fixture.tables.evictRuntime(11); + assertNull(fixture.tables.vanillaSpawnBiome(fixture.custom.value())); + fixture.tables.initializeVanillaSpawnBiomes(fixture.registry); + verify(fixture.policy, times(2)).populate(eq(1), any()); + assertSame(fixture.vanilla, fixture.tables.vanillaSpawnBiome(fixture.custom.value())); + } + + @Test + public void absentRuntimeDoesNotReadTheRegistryOrAcquireAdmission() { + Fixture fixture = new Fixture(); + when(fixture.policy.current()).thenReturn(null); + fixture.tables.initializeVanillaSpawnBiomes(null); + assertNull(fixture.tables.vanillaSpawnBiome(fixture.custom.value())); + verify(fixture.policy, times(0)).lease(any()); + } + + private static final class Fixture { + private final NativeSpawnBiomePolicy policy; + private final Registry registry; + private final Holder.Reference vanilla; + private final Holder.Reference custom; + private final NativeGenerationLease lease = mock(NativeGenerationLease.class); + private final NativeGenerationScope scope = mock(NativeGenerationScope.class); + private final NativeSpawnBiomeTables tables; + + @SuppressWarnings("unchecked") + private Fixture() { + policy = mock(NativeSpawnBiomePolicy.class); + registry = mock(Registry.class); + vanilla = mock(Holder.Reference.class); + custom = mock(Holder.Reference.class); + when(vanilla.value()).thenReturn(mock(Biome.class)); + when(custom.value()).thenReturn(mock(Biome.class)); + when(registry.get(Identifier.parse("minecraft:plains"))).thenReturn(Optional.of(vanilla)); + when(registry.get(Identifier.parse("test:custom"))).thenReturn(Optional.of(custom)); + when(policy.current()).thenReturn(1); + when(policy.runtimeId(1)).thenReturn(11); + when(policy.lease(1)).thenReturn(lease); + when(policy.context(1, lease)).thenReturn(scope); + doAnswer(invocation -> { + NativeSpawnBiomePolicy.MappingTarget target = invocation.getArgument(1); + target.vanilla("minecraft:plains").accept("test:custom"); + return null; + }).when(policy).populate(eq(1), any()); + tables = new NativeSpawnBiomeTables<>(policy, new Object()); + } + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureHooksTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java similarity index 63% rename from adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureHooksTest.java rename to adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java index a50527929..00c4436c6 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureHooksTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java @@ -1,4 +1,8 @@ -package art.arcane.iris.modded; +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import net.minecraft.server.level.ServerLevel; + +import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; @@ -6,6 +10,7 @@ import net.minecraft.world.level.levelgen.feature.Feature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.junit.BeforeClass; import org.junit.Test; +import java.util.function.Supplier; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -14,7 +19,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -public class ModdedStructureHooksTest { +import static org.mockito.Mockito.mock; + +import static org.mockito.Mockito.when; + +public class NativeStructureOperationsTest { @BeforeClass public static void bootstrapMinecraftRegistries() { SharedConstants.tryDetectVersion(); @@ -23,28 +32,28 @@ public class ModdedStructureHooksTest { @Test public void objectFeaturesMatchPaperGroups() { - assertEquals("trees", ModdedStructureHooks.classifyFeature(Feature.TREE)); - assertEquals("fallen_trees", ModdedStructureHooks.classifyFeature(Feature.FALLEN_TREE)); - assertEquals("mushrooms", ModdedStructureHooks.classifyFeature(Feature.HUGE_BROWN_MUSHROOM)); - assertEquals("mushrooms", ModdedStructureHooks.classifyFeature(Feature.HUGE_RED_MUSHROOM)); - assertNull(ModdedStructureHooks.classifyFeature(Feature.ORE)); + assertEquals("trees", NativeStructureOperations.classifyFeature(Feature.TREE)); + assertEquals("fallen_trees", NativeStructureOperations.classifyFeature(Feature.FALLEN_TREE)); + assertEquals("mushrooms", NativeStructureOperations.classifyFeature(Feature.HUGE_BROWN_MUSHROOM)); + assertEquals("mushrooms", NativeStructureOperations.classifyFeature(Feature.HUGE_RED_MUSHROOM)); + assertNull(NativeStructureOperations.classifyFeature(Feature.ORE)); } @Test public void structureSpanGuardUsesInclusiveBoundingBoxDimensions() { BoundingBox box = new BoundingBox(-16, -64, 32, 15, 63, 47); - assertTrue(ModdedStructureHooks.isWithinSpan(box, 128)); - assertFalse(ModdedStructureHooks.isWithinSpan(box, 127)); - assertTrue(ModdedStructureHooks.isWithinSpan(box, 0)); - assertTrue(ModdedStructureHooks.isWithinSpan(box, -1)); + assertTrue(NativeStructureOperations.isWithinSpan(box, 128)); + assertFalse(NativeStructureOperations.isWithinSpan(box, 127)); + assertTrue(NativeStructureOperations.isWithinSpan(box, 0)); + assertTrue(NativeStructureOperations.isWithinSpan(box, -1)); } @Test public void chunkGridSizeCountsEveryChunkPlacementWouldLoad() { - assertEquals(1, ModdedStructureHooks.chunkGridSize(new BoundingBox(0, 0, 0, 15, 15, 15))); - assertEquals(4, ModdedStructureHooks.chunkGridSize(new BoundingBox(0, 0, 0, 16, 15, 16))); - assertEquals(9, ModdedStructureHooks.chunkGridSize(new BoundingBox(-16, 0, -16, 16, 15, 16))); + assertEquals(1, NativeStructureOperations.chunkGridSize(new BoundingBox(0, 0, 0, 15, 15, 15))); + assertEquals(4, NativeStructureOperations.chunkGridSize(new BoundingBox(0, 0, 0, 16, 15, 16))); + assertEquals(9, NativeStructureOperations.chunkGridSize(new BoundingBox(-16, 0, -16, 16, 15, 16))); } @Test @@ -53,26 +62,31 @@ public class ModdedStructureHooksTest { Integer.MIN_VALUE / 2, 0, Integer.MIN_VALUE / 2, Integer.MAX_VALUE / 2, 15, Integer.MAX_VALUE / 2); - assertEquals(Integer.MAX_VALUE, ModdedStructureHooks.chunkGridSize(box)); - assertTrue(ModdedStructureHooks.chunkGridSize(box) > ModdedStructureHooks.MAX_PLACEMENT_CHUNKS); + assertEquals(Integer.MAX_VALUE, NativeStructureOperations.chunkGridSize(box)); + assertTrue(NativeStructureOperations.chunkGridSize(box) > 1024); } @Test public void structurePlacementReturnsPaperOrderedBounds() { BoundingBox box = new BoundingBox(-16, -64, 32, 15, 63, 47); - assertArrayEquals(new int[]{-16, -64, 32, 15, 63, 47}, ModdedStructureHooks.bounds(box)); + assertArrayEquals(new int[]{-16, -64, 32, 15, 63, 47}, NativeStructureOperations.bounds(box)); } @Test public void platformWorldMaximumHeightIsExclusive() { - assertEquals(320, ModdedPlatformWorld.exclusiveMaxHeight(-64, 384)); - assertEquals(384, ModdedPlatformWorld.exclusiveMaxHeight(0, 384)); + ServerLevel level = mock(ServerLevel.class); + when(level.getMinY()).thenReturn(-64); + when(level.getHeight()).thenReturn(384); + ModdedPlatformWorld world = new ModdedPlatformWorld(level); + assertEquals(320, world.maxHeight()); + when(level.getMinY()).thenReturn(0); + assertEquals(384, world.maxHeight()); } @Test public void structureRegistryHooksRejectUnavailableServer() { - ModdedStructureHooks hooks = new ModdedStructureHooks(() -> null); + NativeStructureOperations hooks = hooks(() -> null); IllegalStateException structureKeys = assertThrows( IllegalStateException.class, hooks::structureKeys); @@ -101,7 +115,7 @@ public class ModdedStructureHooksTest { @Test public void structureReachabilityHooksRejectUnavailableLevel() { - ModdedStructureHooks hooks = new ModdedStructureHooks(() -> null); + NativeStructureOperations hooks = hooks(() -> null); IllegalStateException reachable = assertThrows( IllegalStateException.class, () -> hooks.reachableStructureKeys(null)); @@ -115,7 +129,7 @@ public class ModdedStructureHooksTest { @Test public void structureRegistryHooksPreserveServerSupplierFailure() { IllegalStateException cause = new IllegalStateException("server supplier failed"); - ModdedStructureHooks hooks = new ModdedStructureHooks(() -> { + NativeStructureOperations hooks = hooks(() -> { throw cause; }); @@ -125,4 +139,9 @@ public class ModdedStructureHooksTest { assertTrue(failure.getMessage().contains("access the Minecraft server")); assertEquals(cause, failure.getCause()); } + private static NativeStructureOperations hooks(Supplier server) { + return new NativeStructureOperations(new NativeStructureOperations.Options(server, 1024, + failure -> { throw new AssertionError(failure); }, warning -> { })); + } + } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionManagerTeleportTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeTeleportBoundsTest.java similarity index 90% rename from adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionManagerTeleportTest.java rename to adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeTeleportBoundsTest.java index f63f9d5e9..e28abca07 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionManagerTeleportTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeTeleportBoundsTest.java @@ -1,10 +1,10 @@ -package art.arcane.iris.modded; +package art.arcane.volmlib.nativelib.minecraft26_2.modded; import org.junit.Test; import static org.junit.Assert.assertEquals; -public class ModdedDimensionManagerTeleportTest { +public class NativeTeleportBoundsTest { @Test public void blockCoordinatesUseFloorAcrossZeroAndChunkBoundaries() { assertEquals(0, ModdedTeleportBounds.blockCoordinate(0.99D)); diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldIdentityTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldIdentityTest.java new file mode 100644 index 000000000..ca5d1bb0f --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldIdentityTest.java @@ -0,0 +1,46 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.level.ServerLevel; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; + +public class NativeWorldIdentityTest { + @BeforeClass + public static void bootstrapRegistries() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void repeatedViewsShareTheSameEngineKey() { + ServerLevel level = mock(ServerLevel.class); + NativeWorld first = new ModdedPlatformWorld(level); + NativeWorld second = new ModdedPlatformWorld(level); + Map engines = new HashMap<>(); + engines.put(first, "bound"); + assertEquals(first, second); + assertEquals(first.hashCode(), second.hashCode()); + assertEquals("bound", engines.get(second)); + } + + @Test + public void reopeningADimensionDoesNotReuseTheRetiredEngineKey() { + NativeWorld retired = new ModdedPlatformWorld(mock(ServerLevel.class)); + NativeWorld reopened = new ModdedPlatformWorld(mock(ServerLevel.class)); + Map engines = new HashMap<>(); + engines.put(retired, "retired"); + assertNotEquals(retired, reopened); + assertNull(engines.get(reopened)); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldInspectionTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldInspectionTest.java new file mode 100644 index 000000000..0f11f71af --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldInspectionTest.java @@ -0,0 +1,79 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockPoint; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import net.minecraft.SharedConstants; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistryAccess; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.entity.SpawnerBlockEntity; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.doReturn; + +public class NativeWorldInspectionTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + if (!Items.STONE.builtInRegistryHolder().areComponentsBound()) { + Items.STONE.builtInRegistryHolder().bindComponents(DataComponentMap.builder() + .set(DataComponents.MAX_STACK_SIZE, 64).build()); + } + } + + @Test + public void preservesSpawnerEntityMetadataAndMissingTileState() { + ServerLevel level = mock(ServerLevel.class); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + when(level.registryAccess()).thenReturn(mock(RegistryAccess.class)); + SpawnerBlockEntity spawner = mock(SpawnerBlockEntity.class); + doReturn(BuiltInRegistries.BLOCK_ENTITY_TYPE.getValue(Identifier.parse("minecraft:mob_spawner"))).when(spawner).getType(); + CompoundTag entity = new CompoundTag(); + entity.putString("id", "minecraft:zombie"); + CompoundTag spawnData = new CompoundTag(); + spawnData.put("entity", entity); + CompoundTag tile = new CompoundTag(); + tile.put("SpawnData", spawnData); + when(spawner.saveWithoutMetadata(any(HolderLookup.Provider.class))).thenReturn(tile); + when(level.getBlockEntity(BlockPos.ZERO)).thenReturn(spawner); + NativeWorldInspection inspection = new NativeWorldInspection(world); + NativeWorldInspection.BlockEntityDetails details = inspection.blockEntity(new NativeBlockPoint(0, 0, 0)); + assertEquals("minecraft:mob_spawner", details.type()); + assertEquals("minecraft:zombie", details.spawnedEntity()); + assertNull(details.lootTable()); + assertNull(inspection.blockEntity(new NativeBlockPoint(1, 0, 0))); + } + + @Test + public void distinguishesEmptyHandAndBlockItemState() { + ServerPlayer handle = mock(ServerPlayer.class); + NativeProtocolPlayer player = NativeProtocolPlayer.fromHandle(handle); + when(handle.getMainHandItem()).thenReturn(ItemStack.EMPTY); + assertNull(NativeWorldInspection.heldItem(player)); + ItemStack stone = new ItemStack(Items.STONE, 3); + when(handle.getMainHandItem()).thenReturn(stone); + NativeWorldInspection.HeldItem item = NativeWorldInspection.heldItem(player); + assertEquals("minecraft:stone", item.key()); + assertEquals("minecraft:stone", item.blockState()); + assertEquals(3, item.count()); + } +} diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldMaintenanceTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldMaintenanceTest.java new file mode 100644 index 000000000..1ad112f14 --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeWorldMaintenanceTest.java @@ -0,0 +1,47 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeWorld; +import net.minecraft.SharedConstants; +import net.minecraft.core.BlockPos; +import net.minecraft.server.Bootstrap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.block.Blocks; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeWorldMaintenanceTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void fluidExposureUsesSidesAndBottomWithoutTreatingAirAboveAsExposure() { + ServerLevel level = mock(ServerLevel.class); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.STONE.defaultBlockState()); + when(level.getBlockState(BlockPos.ZERO)).thenReturn(Blocks.WATER.defaultBlockState()); + when(level.getBlockState(BlockPos.ZERO.above())).thenReturn(Blocks.AIR.defaultBlockState()); + assertFalse(NativeWorldMaintenance.exposedFluid(world, 0, 0, 0)); + when(level.getBlockState(BlockPos.ZERO.below())).thenReturn(Blocks.AIR.defaultBlockState()); + assertTrue(NativeWorldMaintenance.exposedFluid(world, 0, 0, 0)); + } + + @Test + public void emptySpaceBesideSolidBlocksDoesNotScheduleFluidPhysics() { + ServerLevel level = mock(ServerLevel.class); + NativeWorld world = mock(NativeWorld.class); + when(world.nativeHandle()).thenReturn(level); + when(level.getBlockState(any(BlockPos.class))).thenReturn(Blocks.AIR.defaultBlockState()); + when(level.getBlockState(BlockPos.ZERO)).thenReturn(Blocks.STONE.defaultBlockState()); + assertFalse(NativeWorldMaintenance.exposedFluid(world, 0, 0, 0)); + } +} diff --git a/adapters/neoforge/build.gradle b/adapters/neoforge/build.gradle index c4d75e6c0..0d6023e6e 100644 --- a/adapters/neoforge/build.gradle +++ b/adapters/neoforge/build.gradle @@ -76,7 +76,11 @@ java { } } +ext.nativeSourceLoader = 'neoforge' +apply from: file('../../gradle/native-minecraft-sources.gradle') + sourceSets { + modLibraries main { java { srcDir '../modded-common/src/main/java' @@ -94,6 +98,8 @@ sourceSets { } } + + repositories { mavenCentral() maven { @@ -176,10 +182,26 @@ dependencies { } } +def developmentModLibraries = configurations.devBundle.incoming.artifactView { + componentFilter { identifier -> + identifier instanceof org.gradle.api.artifacts.component.ProjectComponentIdentifier + || (identifier instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier + && (identifier.group == 'art.arcane' || identifier.group.startsWith('com.github.VolmitSoftware'))) + } +}.files +def stageDevelopmentModLibraries = tasks.register('stageDevelopmentModLibraries', Sync) { + dependsOn(developmentModLibraries) + from({ developmentModLibraries.collect { File artifact -> zipTree(artifact) } }) + into(layout.buildDirectory.dir('mod-libraries')) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + exclude('META-INF/MANIFEST.MF', 'module-info.class', 'META-INF/versions/**') +} +sourceSets.modLibraries.output.classesDirs.setFrom(stageDevelopmentModLibraries) + neoForge { version = neoForgeVersion - accessTransformers.from('src/main/resources/META-INF/accesstransformer.cfg') + accessTransformers.from(art.arcane.volmit.packaging.NativeAccessRules.prepare(nativeSourceImplementation, "neoforge", layout.buildDirectory.dir("native-access").get().asFile)) runs { client { @@ -212,6 +234,7 @@ neoForge { mods { irisworldgen { sourceSet(sourceSets.main) + sourceSet(sourceSets.modLibraries) } } @@ -228,6 +251,7 @@ tasks.named('compileJava', JavaCompile).configure { tasks.named('test').configure { useJUnitPlatform() + classpath = classpath.minus(developmentModLibraries).plus(sourceSets.modLibraries.output) } processResources { diff --git a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeBootstrap.java b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeBootstrap.java index 687b40a91..34e4ab9c1 100644 --- a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeBootstrap.java +++ b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeBootstrap.java @@ -1,184 +1,29 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.neoforge; import art.arcane.iris.modded.IrisModdedChunkGenerator; -import art.arcane.iris.modded.ModdedBlockBreakHandler; import art.arcane.iris.modded.ModdedEngineBootstrap; -import art.arcane.iris.modded.ModdedForcedDatapack; -import art.arcane.iris.modded.command.IrisModdedCommands; -import art.arcane.iris.modded.command.ModdedWandService; -import com.mojang.serialization.MapCodec; -import net.minecraft.core.registries.Registries; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.server.packs.PackType; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.neoforged.api.distmarker.Dist; -import net.neoforged.bus.api.EventPriority; +import art.arcane.iris.modded.ModdedLifecycleCallbacks; +import art.arcane.iris.modded.service.ModdedTreeFellerService; +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeChunkGeneratorDefinition; +import art.arcane.volmlib.nativelib.minecraft26_2.neoforge.NativeNeoForgeLoader; +import art.arcane.volmlib.nativelib.minecraft26_2.neoforge.NativeNeoForgeBootstrap; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.common.Mod; -import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.common.util.BlockSnapshot; -import net.neoforged.neoforge.event.AddPackFindersEvent; -import net.neoforged.neoforge.event.RegisterCommandsEvent; -import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; -import net.neoforged.neoforge.event.level.BlockDropsEvent; -import net.neoforged.neoforge.event.level.BlockEvent; -import net.neoforged.neoforge.event.level.LevelEvent; -import net.neoforged.neoforge.event.level.block.BreakBlockEvent; -import net.neoforged.neoforge.event.server.ServerAboutToStartEvent; -import net.neoforged.neoforge.event.server.ServerStartedEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; -import net.neoforged.neoforge.event.server.ServerStoppingEvent; -import net.neoforged.neoforge.event.tick.ServerTickEvent; -import net.neoforged.neoforge.registries.DeferredRegister; -import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent; - -import java.util.List; @Mod("irisworldgen") public final class IrisNeoForgeBootstrap { public IrisNeoForgeBootstrap(IEventBus modBus) { - ModdedEngineBootstrap.bootCommon(new NeoForgeModdedLoader(), "NeoForge " + FMLLoader.getCurrent().getVersionInfo().neoForgeVersion(), () -> { - DeferredRegister> chunkGenerators = DeferredRegister.create(Registries.CHUNK_GENERATOR, "irisworldgen"); - chunkGenerators.register("iris", () -> IrisModdedChunkGenerator.CODEC); - chunkGenerators.register(modBus); - }); - - modBus.addListener((AddPackFindersEvent event) -> { - if (event.getPackType() == PackType.SERVER_DATA) { - event.addRepositorySource(ModdedForcedDatapack.repositorySource()); - } - }); - + NativeNeoForgeLoader loader = new NativeNeoForgeLoader(new NativeLoaderOptions( + "irisworldgen", "treefeller", ModdedTreeFellerService::runBreakProbe)); + NativeChunkGeneratorDefinition generator = IrisModdedChunkGenerator.DEFINITION; + ModdedEngineBootstrap.bootCommon(loader, "NeoForge " + FMLLoader.getCurrent().getVersionInfo().neoForgeVersion(), + () -> NativeNeoForgeBootstrap.registerGenerator(modBus, generator)); NeoForgeProtocolNetworking.register(modBus); - - if (FMLEnvironment.getDist() == Dist.CLIENT) { + if (loader.clientEnvironment()) { IrisNeoForgeClient.init(modBus); } - - NeoForge.EVENT_BUS.addListener((ServerAboutToStartEvent event) -> ModdedEngineBootstrap.serverAboutToStart(event.getServer())); - NeoForge.EVENT_BUS.addListener((ServerStartingEvent event) -> ModdedEngineBootstrap.start(event.getServer())); - NeoForge.EVENT_BUS.addListener((ServerStartedEvent event) -> ModdedEngineBootstrap.serverStarted(event.getServer())); - NeoForge.EVENT_BUS.addListener((ServerStoppingEvent event) -> ModdedEngineBootstrap.stop()); - NeoForge.EVENT_BUS.addListener((LevelEvent.Load event) -> { - if (event.getLevel() instanceof ServerLevel level) { - ModdedEngineBootstrap.levelLoaded(level); - } - }); - NeoForge.EVENT_BUS.addListener((LevelEvent.Unload event) -> { - if (event.getLevel() instanceof ServerLevel level) { - ModdedEngineBootstrap.levelUnloaded(level); - } - }); - NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent event) -> IrisModdedCommands.register(event.getDispatcher())); - NeoForge.EVENT_BUS.addListener((PermissionGatherEvent.Nodes event) -> - event.addNodes(NeoForgeModdedLoader.TREE_FELLER_PERMISSION)); - // LOWEST so every other mod has already had its say about cancelling the break before Iris records - // provenance for it. - NeoForge.EVENT_BUS.addListener( - EventPriority.LOWEST, - false, - (BreakBlockEvent event) -> { - if (event.getLevel() instanceof ServerLevel level - && event.getPlayer() instanceof ServerPlayer player) { - ModdedBlockBreakHandler.prepare(level, player, event.getPos(), event.getState()); - } - } - ); - // Parity with the Fabric PlayerBlockBreakEvents.CANCELED hook: drop the prepared entry when the break - // never happens. It only observes cancellations from listeners registered before it at LOWEST; the - // 1-tick sweeper ModdedBlockBreakHandler.prepare schedules covers every other case. - NeoForge.EVENT_BUS.addListener( - EventPriority.LOWEST, - true, - (BreakBlockEvent event) -> { - if (event.isCanceled() && event.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.cancel(level, event.getPos()); - } - } - ); - NeoForge.EVENT_BUS.addListener( - EventPriority.LOWEST, - false, - (BlockEvent.EntityPlaceEvent event) -> { - if (!(event instanceof BlockEvent.EntityMultiPlaceEvent) - && event.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.clearPlacedProvenance(level, event.getPos()); - } - } - ); - NeoForge.EVENT_BUS.addListener( - EventPriority.LOWEST, - false, - (BlockEvent.EntityMultiPlaceEvent event) -> { - for (BlockSnapshot snapshot : event.getReplacedBlockSnapshots()) { - if (snapshot.getLevel() instanceof ServerLevel level) { - ModdedBlockBreakHandler.clearPlacedProvenance(level, snapshot.getPos()); - } - } - } - ); - NeoForge.EVENT_BUS.addListener((BlockDropsEvent event) -> { - if (!(event.getBreaker() instanceof Player)) { - return; - } - ServerLevel level = event.getLevel(); - ModdedBlockBreakHandler.Result result = ModdedBlockBreakHandler.complete(level, event.getPos(), event.getState()); - List vanillaDrops = event.getDrops().stream() - .map(ItemEntity::getItem) - .toList(); - if (result.routeCombinedDrops(vanillaDrops)) { - event.getDrops().clear(); - return; - } - if (result.replaceVanillaDrops()) { - event.getDrops().clear(); - } - for (ItemStack stack : result.drops()) { - ItemEntity item = ModdedBlockBreakHandler.createDrop(level, event.getPos(), stack); - if (item != null) { - event.getDrops().add(item); - } - } - }); - NeoForge.EVENT_BUS.addListener((PlayerInteractEvent.LeftClickBlock event) -> { - if (ModdedWandService.attackBlock(event.getEntity(), event.getLevel(), event.getHand(), event.getPos())) { - event.setCanceled(true); - } - }); - NeoForge.EVENT_BUS.addListener((PlayerInteractEvent.RightClickBlock event) -> { - if (ModdedWandService.useBlock(event.getEntity(), event.getLevel(), event.getHand(), event.getPos())) { - event.setCanceled(true); - event.setCancellationResult(InteractionResult.SUCCESS); - } - }); - NeoForge.EVENT_BUS.addListener((ServerTickEvent.Post event) -> { - ModdedEngineBootstrap.tick(event.getServer()); - ModdedWandService.serverTick(event.getServer()); - }); + NativeNeoForgeBootstrap.install(modBus, loader, ModdedLifecycleCallbacks.create()); } } diff --git a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeClient.java b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeClient.java index e0b5c5694..75f0a8e40 100644 --- a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeClient.java +++ b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/IrisNeoForgeClient.java @@ -19,43 +19,18 @@ package art.arcane.iris.neoforge; import art.arcane.iris.client.IrisClient; +import art.arcane.volmlib.nativelib.minecraft26_2.neoforge.NativeNeoForgeClientHooks; import art.arcane.iris.client.IrisClientHud; -import art.arcane.iris.client.IrisClientKeybinds; -import art.arcane.iris.modded.ModdedIrisPayload; +import art.arcane.iris.modded.ModdedProtocolDefinition; +import art.arcane.volmlib.nativelib.minecraft26_2.neoforge.NativeNeoForgeProtocolNetworking; import net.neoforged.bus.api.IEventBus; -import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; -import net.neoforged.neoforge.client.event.ClientTickEvent; -import net.neoforged.neoforge.client.event.RegisterGuiLayersEvent; -import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; -import net.neoforged.neoforge.client.network.ClientPacketDistributor; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.network.registration.PayloadRegistrar; public final class IrisNeoForgeClient { private IrisNeoForgeClient() { } - public static void registerBidirectional(PayloadRegistrar registrar) { - registrar.playBidirectional(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC, - NeoForgeProtocolNetworking::onInbound, - (payload, context) -> context.enqueueWork(() -> IrisClient.onInbound(payload.data()))); - } - public static void init(IEventBus modBus) { - IrisClient.bindSender(frame -> ClientPacketDistributor.sendToServer(new ModdedIrisPayload(frame))); - modBus.addListener((RegisterGuiLayersEvent event) -> - event.registerAboveAll(IrisClient.HUD_ELEMENT_ID, (graphics, delta) -> IrisClientHud.render(graphics))); - modBus.addListener((RegisterKeyMappingsEvent event) -> { - event.register(IrisClientKeybinds.TOGGLE_HUD); - event.register(IrisClientKeybinds.OPEN_MAP); - event.register(IrisClientKeybinds.TOGGLE_WHAT); - }); - NeoForge.EVENT_BUS.addListener((ClientPlayerNetworkEvent.LoggingIn event) -> IrisClient.onWorldJoin()); - NeoForge.EVENT_BUS.addListener((ClientPlayerNetworkEvent.LoggingOut event) -> IrisClient.onDisconnect()); - NeoForge.EVENT_BUS.addListener((ClientTickEvent.Post event) -> { - IrisClient.tick(); - IrisClientHud.tick(); - IrisClientKeybinds.pollToggle(); - }); + IrisClient.bindSender(frame -> NativeNeoForgeProtocolNetworking.sendClient(ModdedProtocolDefinition.PROTOCOL, frame)); + NativeNeoForgeClientHooks.install(modBus, IrisClientHud.binding(), IrisClient::onWorldJoin, IrisClient::onDisconnect); } } diff --git a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeModdedLoader.java b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeModdedLoader.java deleted file mode 100644 index bd17eea51..000000000 --- a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeModdedLoader.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.neoforge; - -import art.arcane.iris.modded.ModdedLoader; -import art.arcane.iris.modded.service.ModdedTreeFellerService; -import net.minecraft.commands.Commands; -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.level.block.state.BlockState; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.level.LevelEvent; -import net.neoforged.neoforge.event.level.block.BreakBlockEvent; -import net.neoforged.fml.ModList; -import net.neoforged.fml.loading.FMLEnvironment; -import net.neoforged.fml.loading.FMLLoader; -import net.neoforged.fml.loading.FMLPaths; -import net.neoforged.neoforge.server.ServerLifecycleHooks; -import net.neoforged.neoforge.server.permission.PermissionAPI; -import net.neoforged.neoforge.server.permission.nodes.PermissionNode; -import net.neoforged.neoforge.server.permission.nodes.PermissionTypes; -import net.neoforged.neoforgespi.language.IModFileInfo; - -import java.io.File; -import java.nio.file.Path; - -public final class NeoForgeModdedLoader implements ModdedLoader { - public static final PermissionNode TREE_FELLER_PERMISSION = new PermissionNode<>( - "irisworldgen", - "treefeller", - PermissionTypes.BOOLEAN, - (player, playerId, contexts) -> - player != null && Commands.LEVEL_GAMEMASTERS.check(player.permissions()) - ); - - @Override - public String platformName() { - return "neoforge"; - } - - @Override - public String minecraftVersion() { - return FMLLoader.getCurrent().getVersionInfo().mcVersion(); - } - - @Override - public String modVersion() { - return ModList.get().getModContainerById("irisworldgen") - .map((net.neoforged.fml.ModContainer container) -> container.getModInfo().getVersion().toString()) - .orElse("unknown"); - } - - @Override - public MinecraftServer currentServer() { - return ServerLifecycleHooks.getCurrentServer(); - } - - @Override - public void invalidateLevelCache(MinecraftServer server) { - server.markWorldsDirty(); - } - - @Override - public void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level) { - NeoForge.EVENT_BUS.post(new LevelEvent.Load(level)); - } - - @Override - public void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level) { - NeoForge.EVENT_BUS.post(new LevelEvent.Unload(level)); - } - - @Override - public boolean clientEnvironment() { - return FMLEnvironment.getDist().isClient(); - } - - @Override - public Path configDir() { - return FMLPaths.CONFIGDIR.get(); - } - - @Override - public File modJar() { - IModFileInfo info = ModList.get().getModFileById("irisworldgen"); - return info == null ? null : info.getFile().getFilePath().toFile(); - } - - @Override - public boolean hasTreeFellerPermission(ServerPlayer player) { - return PermissionAPI.getPermission(player, TREE_FELLER_PERMISSION); - } - - @Override - public boolean canTreeFellerBreak( - ServerLevel level, - ServerPlayer player, - BlockPos position, - BlockState state - ) { - return ModdedTreeFellerService.runBreakProbe(() -> { - BreakBlockEvent event = new BreakBlockEvent(level, position, state, player); - NeoForge.EVENT_BUS.post(event); - return !event.isCanceled(); - }); - } -} diff --git a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeProtocolNetworking.java b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeProtocolNetworking.java index dd6f9a764..c541e21d7 100644 --- a/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeProtocolNetworking.java +++ b/adapters/neoforge/src/main/java/art/arcane/iris/neoforge/NeoForgeProtocolNetworking.java @@ -1,82 +1,20 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - package art.arcane.iris.neoforge; -import art.arcane.iris.modded.ModdedIrisPayload; -import art.arcane.iris.modded.ModdedProtocolChannel; +import art.arcane.iris.modded.ModdedProtocolDefinition; import art.arcane.iris.modded.ModdedProtocolHandler; -import net.minecraft.server.level.ServerPlayer; -import net.neoforged.api.distmarker.Dist; +import art.arcane.volmlib.nativelib.minecraft26_2.neoforge.NativeNeoForgeProtocolNetworking; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeProtocolCallbacks; +import art.arcane.iris.client.IrisClient; import net.neoforged.bus.api.IEventBus; -import net.neoforged.fml.loading.FMLEnvironment; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.common.extensions.ICommonPacketListener; -import net.neoforged.neoforge.event.entity.player.PlayerEvent; -import net.neoforged.neoforge.network.PacketDistributor; -import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; -import net.neoforged.neoforge.network.handling.IPayloadContext; -import net.neoforged.neoforge.network.registration.PayloadRegistrar; public final class NeoForgeProtocolNetworking { private NeoForgeProtocolNetworking() { } public static void register(IEventBus modBus) { - modBus.addListener((RegisterPayloadHandlersEvent event) -> onRegisterPayloads(event)); - NeoForge.EVENT_BUS.addListener((PlayerEvent.PlayerLoggedInEvent event) -> { - if (event.getEntity() instanceof ServerPlayer player) { - ModdedProtocolHandler.onPlayerJoin(player); - } - }); - NeoForge.EVENT_BUS.addListener((PlayerEvent.PlayerLoggedOutEvent event) -> { - if (event.getEntity() instanceof ServerPlayer player) { - ModdedProtocolHandler.onPlayerDisconnect(player); - } - }); - ModdedProtocolHandler.bindChannel(new NeoForgeProtocolChannel()); - } - - private static void onRegisterPayloads(RegisterPayloadHandlersEvent event) { - PayloadRegistrar registrar = event.registrar("1").optional(); - if (FMLEnvironment.getDist() == Dist.CLIENT) { - IrisNeoForgeClient.registerBidirectional(registrar); - } else { - registrar.playBidirectional(ModdedIrisPayload.TYPE, ModdedIrisPayload.STREAM_CODEC, - NeoForgeProtocolNetworking::onInbound); - } - } - - static void onInbound(ModdedIrisPayload payload, IPayloadContext context) { - if (context.player() instanceof ServerPlayer player) { - ModdedProtocolHandler.onInbound(player, payload.data()); - } - } - - private static final class NeoForgeProtocolChannel implements ModdedProtocolChannel { - @Override - public boolean canReceive(ServerPlayer player) { - return ((ICommonPacketListener) player.connection).hasChannel(ModdedIrisPayload.TYPE); - } - - @Override - public void send(ServerPlayer player, ModdedIrisPayload payload) { - PacketDistributor.sendToPlayer(player, payload); - } + NativeProtocolCallbacks callbacks = new NativeProtocolCallbacks(ModdedProtocolHandler::onInbound, + ModdedProtocolHandler::onPlayerJoin, ModdedProtocolHandler::onPlayerDisconnect); + ModdedProtocolHandler.bindChannel(NativeNeoForgeProtocolNetworking.register(modBus, + ModdedProtocolDefinition.PROTOCOL, callbacks, IrisClient::onInbound)); } } diff --git a/adapters/neoforge/src/main/resources/META-INF/accesstransformer.cfg b/adapters/neoforge/src/main/resources/META-INF/accesstransformer.cfg deleted file mode 100644 index 5a2faeeb3..000000000 --- a/adapters/neoforge/src/main/resources/META-INF/accesstransformer.cfg +++ /dev/null @@ -1,3 +0,0 @@ -public net.minecraft.server.MinecraftServer levels -public net.minecraft.server.MinecraftServer executor -public net.minecraft.server.MinecraftServer storageSource diff --git a/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml index cd81fa963..2c254d9ea 100644 --- a/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -4,10 +4,10 @@ license = "GPL-3.0" issueTrackerURL = "https://github.com/VolmitSoftware/Iris/issues" [[mixins]] -config = "irisworldgen.entity.mixins.json" +config = "volmlib.entity.mixins.json" [[mixins]] -config = "irisworldgen.client.mixins.json" +config = "volmlib.client.mixins.json" [[accessTransformers]] file = "META-INF/accesstransformer.cfg" diff --git a/build.gradle b/build.gradle index 14d52a77c..f08bb4878 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,10 @@ import art.arcane.volmit.packaging.PackagingMode +import art.arcane.volmit.packaging.NativeDevelopmentTargets +import io.papermc.paperweight.userdev.PaperweightUser +import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension +import io.papermc.paperweight.userdev.PaperweightUserExtension +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.api.file.CopySpec import org.gradle.api.file.DeleteSpec import org.gradle.api.file.FileSystemOperations @@ -29,17 +35,6 @@ import javax.inject.Inject * along with this program. If not, see . */ -buildscript { - repositories { - maven { - url = uri('https://jitpack.io') - } - } - dependencies { - classpath('com.github.VolmitSoftware:NMSTools:c88961416f') - } -} - buildscript { repositories { mavenCentral() @@ -139,27 +134,26 @@ String fabricConsumerPath = "${consumerLocation}/fabric-mod-consumers/dropins/mo String forgeConsumerPath = "${consumerLocation}/forge-mod-consumers/dropins/mods" String neoForgeConsumerPath = "${consumerLocation}/neoforge-mod-consumers/dropins/mods" -def nmsBindings = [ - v26_2_R1: '26.1.2.build.74-stable', - v26_3_R1: '26.3.build.25-alpha', -] -Class nmsTypeClass = Class.forName('NMSBinding$Type') -nmsBindings.each { key, value -> +List nmsBindings = ['v26_2_R1', 'v26_3_R1'] +ext.nativeDevelopmentAdapters = nmsBindings +nmsBindings.each { String key -> project(":adapters:bukkit:nms:${key}") { apply plugin: JavaPlugin - - def nmsConfig = new Config() - nmsConfig.jvm = 25 - nmsConfig.version = value - nmsConfig.type = Enum.valueOf(nmsTypeClass, 'USER_DEV') - extensions.extraProperties.set('nms', nmsConfig) - plugins.apply(NMSBinding) - - if (key == 'v26_2_R1') { - sourceSets.main.java.srcDir(rootProject.file('adapters/minecraft-common/src/main/java')) + apply plugin: PaperweightUser + NativeDevelopmentTargets.Target nativeTarget = NativeDevelopmentTargets.require(key) + dependencies.extensions.getByType(PaperweightUserDependenciesExtension) + .paperDevBundle(nativeTarget.paperBundle()) + JavaPluginExtension javaExtension = extensions.getByType(JavaPluginExtension) + javaExtension.toolchain.languageVersion.set(JavaLanguageVersion.of(nativeTarget.javaVersion())) + JavaToolchainService nativeToolchains = extensions.getByType(JavaToolchainService) + extensions.configure(PaperweightUserExtension) { PaperweightUserExtension extension -> + extension.javaLauncher.set(nativeToolchains.launcherFor(javaExtension.toolchain)) } dependencies { + implementation("com.github.VolmitSoftware.VolmLib:native-${key}:${volmLibCoordinate.tokenize(':').last()}") { + transitive = false + } compileOnly(project(':core')) compileOnly(volmLibCoordinate) { transitive = false @@ -170,6 +164,9 @@ nmsBindings.each { key, value -> testImplementation('org.mockito:mockito-core:5.23.0') testImplementation(project(':core')) testImplementation(testFixtures(project(':core'))) + testImplementation("com.github.VolmitSoftware.VolmLib:native-common:${volmLibCoordinate.tokenize(':').last()}") { + transitive = false + } testCompileOnly(volmLibCoordinate) { transitive = false } @@ -180,11 +177,37 @@ nmsBindings.each { key, value -> } def included = configurations.create('included') +def nativeRuntime = configurations.create('nativeRuntime') +def nativeRuntimeOutput = layout.buildDirectory.dir('generated/native-runtime') +def nativeRuntimeRules = layout.buildDirectory.file('generated/native-runtime-keep.pro') +dependencies { + (['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter }).each { String module -> + add('nativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:${volmLibCoordinate.tokenize(':').last()}") { + transitive = false + } + } +} +def generateNativeRuntime = tasks.register('generateNativeRuntime') { + inputs.files(nativeRuntime) + inputs.property('version', volmLibCoordinate.tokenize(':').last()) + inputs.property('adapters', nmsBindings) + outputs.dir(nativeRuntimeOutput) + outputs.file(nativeRuntimeRules) + doLast { + Map artifacts = new TreeMap<>() + nativeRuntime.resolvedConfiguration.resolvedArtifacts.each { artifact -> + String module = artifact.moduleVersion.id.name + if (module.startsWith('v') && module.contains('_R')) { + module = 'native-' + module + } + artifacts.put(module, artifact.file) + } + NativeRuntimeArtifacts.generate(artifacts, volmLibCoordinate.tokenize(':').last(), + nativeRuntimeOutput.get().asFile, nativeRuntimeRules.get().asFile) + } +} def jarJar = configurations.create('jarJar') dependencies { - nmsBindings.keySet().each { key -> - add('included', project(path: ":adapters:bukkit:nms:${key}", configuration: 'runtimeElements')) - } add('included', project(path: ':core', configuration: 'shadow')) add('included', project(path: ':adapters:bukkit:plugin', configuration: 'runtimeElements')) add('jarJar', project(':core:agent')) @@ -192,11 +215,15 @@ dependencies { tasks.named('jar', Jar).configure { inputs.files(included) + dependsOn(generateNativeRuntime) + from(nativeRuntimeOutput) duplicatesStrategy = DuplicatesStrategy.EXCLUDE includeEmptyDirs = false from(jarJar) from(provider { included.resolve().collect { zipTree(it) } }) { exclude('languages/**') + exclude('com/fren_gor/**') + } doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(legacyBukkitJar) } @@ -303,11 +330,18 @@ List moddedSharedInputs = [] 'gradle/libs.versions.toml', 'gradle/volmlib-resolution.settings.gradle', 'gradle/generation-build-revision.gradle', - 'gradle/modded-generation-build-revision.gradle' + 'gradle/modded-generation-build-revision.gradle', + 'gradle/native-minecraft-sources.gradle' ].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) } File localVolmLibCheckout = useLocalVolmLib.equalsIgnoreCase('true') ? resolveLocalVolmLibDirectory() : null if (localVolmLibCheckout != null) { - moddedSharedInputs.add(new File(localVolmLibCheckout, 'shared/src/main')) + moddedSharedInputs.add(fileTree(localVolmLibCheckout) { + include('shared/src/main/**', 'native-api/src/main/**', 'native-terrain-api/src/main/**', + 'native/**/src/main/**', + 'native/minecraft-common/src/**', 'packaging/src/main/**', + '**/build.gradle', 'settings.gradle', 'gradle.properties', 'gradle/libs.versions.toml') + exclude('**/build/**', '**/.gradle/**') + }) } Closure declareModdedJarIo = { Exec task, String adapter, String artifactName, Map versions -> @@ -508,14 +542,18 @@ List commonModdedArtifactEntries = [ 'META-INF/iris/generation-kernels/abi-1.revision', 'META-INF/services/art.arcane.iris.localization.IrisMessageContributor', 'art/arcane/iris/generation/runtime/IrisEngine.class', + 'art/arcane/iris/pack/datapack/DataVersion.class', + 'art/arcane/iris/pack/datapack/IDataFixer.class', 'art/arcane/iris/spi/IrisPlatform.class', 'art/arcane/volmlib/util/mantle/io/Lz4IOWorkerCodecSupport.class', 'art/arcane/iris/platform/bootstrap/getHardware.class', // Both shared mixin configs plus the lang file and mod icon every loader's metadata points at. // A shadowJar exclude or a renamed resource that drops any of these is silent at build time and // shows up as a non-applied mixin or a missing-translation report from a user. - 'irisworldgen.entity.mixins.json', - 'irisworldgen.client.mixins.json', + 'volmlib.entity.mixins.json', + 'volmlib.client.mixins.json', + 'art/arcane/volmlib/nativelib/minecraft26_2/client/mixin/NativeWorldOpenFlowsMixin.class', + 'art/arcane/volmlib/nativelib/minecraft26_2/client/mixin/NativeWorldTypeEntryMixin.class', 'assets/irisworldgen/lang/en_us.json', 'assets/irisworldgen/icon.png' ] @@ -538,8 +576,8 @@ tasks.register('verifyFabricArtifact') { doLast { List requiredEntries = commonModdedArtifactEntries + [ 'fabric.mod.json', - 'irisworldgen.mixins.json', - 'irisworldgen.accesswidener', + 'volmlib.fabric.mixins.json', + 'volmlib.accesswidener', 'art/arcane/iris/fabric/IrisFabricBootstrap.class' ] ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses) @@ -616,13 +654,15 @@ List requiredBukkitArtifactEntries = [ 'paper-plugin.yml', 'slimjar.dat', 'slimjar-resolutions.dat', + 'META-INF/iris/native-runtime.properties', 'iris-language-source.properties', 'agent.jar', 'art/arcane/iris/Iris.class', 'art/arcane/iris/IrisBootstrap.class', 'art/arcane/iris/platform/bukkit/nms/INMS.class', - 'art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.class', - 'art/arcane/iris/platform/bukkit/nms/v26_3_R1/NMSBinding.class', + 'art/arcane/iris/platform/bukkit/nms/BukkitBinding.class', + 'art/arcane/volmlib/nativelib/NativeAdapters.class', + 'art/arcane/volmlib/nativelib/terrain/NativeWorldGeneration.class', 'art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.class', 'art/arcane/iris/platform/generation/PlatformChunkGenerator.class', 'art/arcane/iris/world/lifecycle/WorldLifecycleStaging.class', @@ -648,6 +688,7 @@ tasks.register('verifyBukkitArtifact') { requiredBukkitArtifactEntries, 16, runtimeDownloadedPrefixes) + NativeRuntimeArtifacts.verify(artifact, nmsBindings, volmLibCoordinate.tokenize(':').last()) logger.lifecycle("Verified ${artifact.name} packaging and class reference graph") } } @@ -858,8 +899,8 @@ pluginPackaging { .map { String value -> value.toBoolean() } .getOrElse(!PackagingMode.resolve(providers).development()) prunePrefixes = ['art/arcane/volmlib/'] - keepPrefixes = ['art/arcane/volmlib/util/matter/slices/'] - shrinkRules = [file('packaging/shrink.pro')] + keepPrefixes = ['art/arcane/volmlib/util/matter/slices/', 'art/arcane/volmlib/nativelib/'] + shrinkRules = [file('packaging/shrink.pro'), nativeRuntimeRules.get().asFile] shrinkDontwarn = [ 'java.lang.invoke.MethodHandle', 'java.lang.invoke.VarHandle', @@ -885,8 +926,11 @@ pluginPackaging { ] shrinkLibraries.from(provider { project(':core').configurations.compileClasspath }) shrinkLibraries.from(provider { project(':adapters:bukkit:plugin').configurations.compileClasspath }) - nmsBindings.keySet().each { binding -> - shrinkLibraries.from(provider { project(":adapters:bukkit:nms:${binding}").configurations.compileClasspath }) + nmsBindings.each { binding -> + shrinkLibraries.from(provider { project(":adapters:bukkit:nms:${binding}").configurations.compileClasspath.incoming.artifactView { + Set nativeComponents = nativeRuntime.incoming.artifacts.artifacts.collect { artifact -> artifact.id.componentIdentifier }.toSet() + componentFilter { component -> !nativeComponents.contains(component) } + }.files }) } shrinkRelocations = runtimeDownloadRelocations } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 4f9835e62..67f86ee32 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -32,7 +32,6 @@ dependencies { implementation(gradleApi()) implementation('org.ow2.asm:asm:9.10.1') implementation('org.apache.commons:commons-compress:1.28.0') - implementation('com.github.VolmitSoftware:NMSTools:c88961416f') implementation('io.papermc.paperweight:paperweight-userdev:2.0.0-beta.21') testImplementation('junit:junit:4.13.2') } diff --git a/buildSrc/src/main/java/Config.java b/buildSrc/src/main/java/Config.java deleted file mode 100644 index 5c38dd6e4..000000000 --- a/buildSrc/src/main/java/Config.java +++ /dev/null @@ -1,5 +0,0 @@ -public class Config { - public int jvm = 25; - public NMSBinding.Type type = NMSBinding.Type.DIRECT; - public String version; -} diff --git a/buildSrc/src/main/java/NMSBinding.java b/buildSrc/src/main/java/NMSBinding.java deleted file mode 100644 index 588b759c4..000000000 --- a/buildSrc/src/main/java/NMSBinding.java +++ /dev/null @@ -1,285 +0,0 @@ -import com.volmit.nmstools.NMSToolsExtension; -import com.volmit.nmstools.NMSToolsPlugin; -import io.papermc.paperweight.userdev.PaperweightUser; -import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension; -import io.papermc.paperweight.userdev.PaperweightUserExtension; -import io.papermc.paperweight.userdev.attribute.Obfuscation; -import org.gradle.api.Action; -import org.gradle.api.DefaultTask; -import org.gradle.api.GradleException; -import org.gradle.api.Named; -import org.gradle.api.Plugin; -import org.gradle.api.Project; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.attributes.Bundling; -import org.gradle.api.attributes.Category; -import org.gradle.api.attributes.LibraryElements; -import org.gradle.api.attributes.Usage; -import org.gradle.api.file.FileTree; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.JavaPluginExtension; -import org.gradle.api.plugins.ExtraPropertiesExtension; -import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskAction; -import org.gradle.jvm.toolchain.JavaLanguageVersion; -import org.gradle.jvm.toolchain.JavaToolchainService; -import org.gradle.work.DisableCachingByDefault; - -import javax.inject.Inject; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static io.papermc.paperweight.util.constants.ConstantsKt.REOBF_CONFIG; - -public class NMSBinding implements Plugin { - private static final String NEW_LINE = System.lineSeparator(); - private static final byte[] NEW_LINE_BYTES = NEW_LINE.getBytes(StandardCharsets.UTF_8); - - @Override - public void apply(Project target) { - ExtraPropertiesExtension extra = target.getExtensions().getExtraProperties(); - Object configValue = extra.has("nms") ? extra.get("nms") : null; - if (!(configValue instanceof Config)) { - throw new GradleException("No NMS binding configuration found"); - } - - Config config = (Config) configValue; - int jvm = config.jvm; - Type type = config.type; - - if (type == Type.USER_DEV) { - target.getPlugins().apply(PaperweightUser.class); - - PaperweightUserDependenciesExtension dependenciesExtension = - target.getDependencies().getExtensions().findByType(PaperweightUserDependenciesExtension.class); - if (dependenciesExtension != null) { - dependenciesExtension.paperDevBundle(config.version); - } - - JavaPluginExtension java = target.getExtensions().findByType(JavaPluginExtension.class); - if (java == null) { - throw new GradleException("Java plugin not found"); - } - - java.getToolchain().getLanguageVersion().set(JavaLanguageVersion.of(jvm)); - JavaToolchainService javaToolchains = target.getExtensions().getByType(JavaToolchainService.class); - target.getExtensions().configure(PaperweightUserExtension.class, - extension -> extension.getJavaLauncher().set(javaToolchains.launcherFor(java.getToolchain()))); - } else { - extra.set("nmsTools.useBuildTools", type == Type.BUILD_TOOLS); - target.getPlugins().apply(NMSToolsPlugin.class); - target.getExtensions().configure(NMSToolsExtension.class, extension -> { - extension.getJvm().set(jvm); - extension.getVersion().set(config.version); - }); - - } - - String outgoingArtifactTask = type == Type.USER_DEV ? "jar" : "remap"; - ObjectFactory objects = target.getObjects(); - Configuration reobfConfiguration = target.getConfigurations().findByName(REOBF_CONFIG); - if (reobfConfiguration == null) { - reobfConfiguration = target.getConfigurations().create(REOBF_CONFIG); - } - - target.getConfigurations().named(REOBF_CONFIG).configure(configuration -> { - configuration.setCanBeConsumed(true); - configuration.setCanBeResolved(false); - configuration.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, named(objects, Usage.class, Usage.JAVA_RUNTIME)); - configuration.getAttributes().attribute(Category.CATEGORY_ATTRIBUTE, named(objects, Category.class, Category.LIBRARY)); - configuration.getAttributes().attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(objects, LibraryElements.class, LibraryElements.JAR)); - configuration.getAttributes().attribute(Bundling.BUNDLING_ATTRIBUTE, named(objects, Bundling.class, Bundling.EXTERNAL)); - configuration.getAttributes().attribute(Obfuscation.Companion.getOBFUSCATION_ATTRIBUTE(), named(objects, Obfuscation.class, Obfuscation.OBFUSCATED)); - configuration.getOutgoing().artifact(target.getTasks().named(outgoingArtifactTask)); - }); - - int[] version = parseVersion(config.version); - int major = version[0]; - int minor = version[1]; - if (major <= 20 && minor <= 4) { - return; - } - - target.getTasks().register("convert", ConversionTask.class, type); - target.getTasks().named("compileJava").configure(task -> task.dependsOn("convert")); - target.getRootProject().getTasks() - .matching(task -> task.getName().equals("prepareKotlinBuildScriptModel")) - .configureEach(task -> task.dependsOn(target.getPath() + ":convert")); - } - - public static void nmsBinding(Project project, Action action) { - Config config = new Config(); - action.execute(config); - project.getExtensions().getExtraProperties().set("nms", config); - project.getPlugins().apply(NMSBinding.class); - } - - private static int[] parseVersion(String version) { - String trimmed = version; - int suffix = trimmed.indexOf('-'); - if (suffix >= 0) { - trimmed = trimmed.substring(0, suffix); - } - - String[] parts = trimmed.split("\\."); - if ("1".equals(parts[0])) { - return new int[]{Integer.parseInt(parts[1]), Integer.parseInt(parts[2])}; - } - return new int[]{Integer.parseInt(parts[0]), Integer.parseInt(parts[1])}; - } - - private static T named(ObjectFactory objects, Class type, String name) { - return objects.named(type, name); - } - - @DisableCachingByDefault - public abstract static class ConversionTask extends DefaultTask { - private final Pattern pattern; - private final String replacement; - - @Inject - public ConversionTask(Type type) { - setGroup("nms"); - getInputs().property("type", type); - - JavaPluginExtension java = getProject().getExtensions().findByType(JavaPluginExtension.class); - if (java == null) { - throw new GradleException("Java plugin not found"); - } - - Provider source = java.getSourceSets().named(SourceSet.MAIN_SOURCE_SET_NAME).map(SourceSet::getAllJava); - getInputs().files(source); - getOutputs().files(source); - - if (type == Type.USER_DEV) { - this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\." + getProject().getName()); - this.replacement = "org.bukkit.craftbukkit"; - } else { - this.pattern = Pattern.compile("org\\.bukkit\\.craftbukkit\\.(?!" + getProject().getName() + ")"); - this.replacement = "org.bukkit.craftbukkit." + getProject().getName() + "."; - } - } - - @TaskAction - public void process() { - ExecutorService executor = Executors.newFixedThreadPool(16); - try { - Set files = getInputs().getFiles().getFiles(); - List> futures = new ArrayList<>(files.size()); - for (File file : files) { - if (!file.getName().endsWith(".java")) { - continue; - } - futures.add(executor.submit(() -> processFile(file))); - } - - for (Future future : futures) { - future.get(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new RuntimeException(e); - } catch (ExecutionException e) { - throw new RuntimeException(e.getCause()); - } finally { - executor.shutdown(); - } - } - - private void processFile(File file) { - List output = new ArrayList<>(); - boolean changed = false; - - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { - String line; - while ((line = reader.readLine()) != null) { - if (line.startsWith("package") || line.isBlank()) { - output.add(line); - continue; - } - - if (!line.startsWith("import")) { - if (!changed) { - return; - } - - output.add(line); - continue; - } - - Matcher matcher = pattern.matcher(line); - if (!matcher.find()) { - output.add(line); - continue; - } - - output.add(matcher.replaceAll(replacement)); - changed = true; - } - } catch (IOException e) { - throw new RuntimeException(e); - } - - if (!changed) { - return; - } - - try { - if (hasTrailingNewLine(file)) { - output.add(""); - } - - try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { - for (int i = 0; i < output.size(); i++) { - writer.append(output.get(i)); - if (i + 1 < output.size()) { - writer.append(NEW_LINE); - } - } - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - private boolean hasTrailingNewLine(File file) throws IOException { - if (NEW_LINE_BYTES.length == 0) { - return false; - } - - try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { - if (raf.length() < NEW_LINE_BYTES.length) { - return false; - } - - byte[] bytes = new byte[NEW_LINE_BYTES.length]; - raf.seek(raf.length() - bytes.length); - raf.readFully(bytes); - return Arrays.equals(bytes, NEW_LINE_BYTES); - } - } - } - - public enum Type { - USER_DEV, - BUILD_TOOLS, - DIRECT - } -} diff --git a/buildSrc/src/main/java/NativeRuntimeArtifacts.java b/buildSrc/src/main/java/NativeRuntimeArtifacts.java new file mode 100644 index 000000000..3cca17fd7 --- /dev/null +++ b/buildSrc/src/main/java/NativeRuntimeArtifacts.java @@ -0,0 +1,123 @@ +import org.gradle.api.GradleException; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HexFormat; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public final class NativeRuntimeArtifacts { + public static final String MANIFEST = "META-INF/iris/native-runtime.properties"; + private static final String GROUP = "com.github.VolmitSoftware.VolmLib"; + private static final String NATIVE = "art/arcane/volmlib/nativelib/"; + + private NativeRuntimeArtifacts() { + } + + public static void generate(Map artifacts, String version, File output, File rules) throws IOException { + List manifest = new ArrayList<>(); + manifest.add("modules=" + String.join(",", artifacts.keySet())); + manifest.add("repository=https://jitpack.io/"); + Set retained = new TreeSet<>(); + for (Map.Entry artifact : artifacts.entrySet()) { + String module = artifact.getKey(); + manifest.add(module + ".coordinate=" + GROUP + ":" + module + ":" + version); + manifest.add(module + ".sha256=" + digest(artifact.getValue())); + try (JarFile jar = new JarFile(artifact.getValue())) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.getName().endsWith(".class")) { + continue; + } + try (InputStream input = jar.getInputStream(entry)) { + for (String reference : ClassReferences.read(input.readAllBytes())) { + if (reference.startsWith("art/arcane/volmlib/") && !implementation(reference)) { + retained.add(reference); + } + } + } + } + } + } + File manifestFile = new File(output, MANIFEST); + Files.createDirectories(manifestFile.toPath().getParent()); + Files.write(manifestFile.toPath(), manifest, StandardCharsets.UTF_8); + Path runtimeRoots = output.toPath().resolve("META-INF/volmit/runtime-roots.list"); + Files.createDirectories(runtimeRoots.getParent()); + Files.write(runtimeRoots, retained, StandardCharsets.UTF_8); + List keep = new ArrayList<>(); + keep.add("-keep class art.arcane.volmlib.nativelib.** { *; }"); + for (String reference : retained) { + keep.add("-keep class " + reference.replace('/', '.') + " { *; }"); + } + Files.createDirectories(rules.toPath().getParent()); + Files.write(rules.toPath(), keep, StandardCharsets.UTF_8); + } + + public static void verify(File artifact, List adapters, String version) throws IOException { + try (JarFile jar = new JarFile(artifact)) { + JarEntry entry = jar.getJarEntry(MANIFEST); + if (entry == null) { + throw new GradleException("Missing native runtime dependency manifest"); + } + Properties manifest = new Properties(); + try (InputStream input = jar.getInputStream(entry)) { + manifest.load(input); + } + Set expected = new TreeSet<>(); + expected.add("native-common"); + for (String adapter : adapters) { + expected.add("native-" + adapter); + } + Set actual = new TreeSet<>(List.of(manifest.getProperty("modules", "").split(","))); + if (!expected.equals(actual)) { + throw new GradleException("Native dependency modules differ: " + actual + "; expected " + expected); + } + for (String module : expected) { + String coordinate = GROUP + ":" + module + ":" + version; + if (!coordinate.equals(manifest.getProperty(module + ".coordinate")) + || !manifest.getProperty(module + ".sha256", "").matches("[0-9a-f]{64}")) { + throw new GradleException("Invalid native runtime dependency: " + module); + } + } + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + String name = entries.nextElement().getName(); + if (name.endsWith(".class") && implementation(name)) { + throw new GradleException("Native runtime implementation is bundled: " + name); + } + } + } + } + + private static boolean implementation(String name) { + if (!name.startsWith(NATIVE)) { + return false; + } + String relative = name.substring(NATIVE.length()); + return relative.startsWith("common/") || relative.startsWith("minecraft") + || relative.matches("v[0-9_]+R[0-9]+/.*"); + } + + private static String digest(File file) throws IOException { + try { + return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath()))); + } catch (NoSuchAlgorithmException exception) { + throw new IllegalStateException(exception); + } + } +} diff --git a/buildSrc/src/main/java/art/arcane/iris/buildtools/GenerationRevisionScope.java b/buildSrc/src/main/java/art/arcane/iris/buildtools/GenerationRevisionScope.java index 452d5d760..de4dcc7bf 100644 --- a/buildSrc/src/main/java/art/arcane/iris/buildtools/GenerationRevisionScope.java +++ b/buildSrc/src/main/java/art/arcane/iris/buildtools/GenerationRevisionScope.java @@ -11,8 +11,6 @@ public final class GenerationRevisionScope { "core/src/main/java", "spi/src/main/java", "adapters/bukkit/plugin/src/main/java", - "adapters/bukkit/nms/v26_2_R1/src/main/java", - "adapters/bukkit/nms/v26_3_R1/src/main/java", "adapters/minecraft-common/src/main/java", "adapters/modded-common/src/main/java", "adapters/client-common/src/main/java", @@ -31,8 +29,6 @@ public final class GenerationRevisionScope { "core/src/main/java/art/arcane/iris/world/safeguard/Diagnostic.java", "core/src/main/java/art/arcane/iris/world/safeguard/ValueWithDiagnostics.java", "core/src/main/java/art/arcane/iris/diagnostics/splash/IrisSplashRenderer.java", - "adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/NmsWorldLifecycle.java", - "adapters/bukkit/nms/v26_3_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/NmsWorldLifecycle.java", "adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandDeveloper.java", "adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandPregen.java", "adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/CommandIris.java", diff --git a/buildSrc/src/test/java/GenerationRevisionScopeTest.java b/buildSrc/src/test/java/GenerationRevisionScopeTest.java index 4770600b6..28603201a 100644 --- a/buildSrc/src/test/java/GenerationRevisionScopeTest.java +++ b/buildSrc/src/test/java/GenerationRevisionScopeTest.java @@ -20,7 +20,7 @@ import static org.junit.Assert.assertTrue; public class GenerationRevisionScopeTest { private static final String ENGINE_ROOT = "core/src/main/java/art/arcane/iris/generation/runtime/"; - private static final String NMS_ROOT = "adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/"; + private static final String BUKKIT_POLICY_ROOT = "core/src/main/java/art/arcane/iris/platform/bukkit/nms/"; private static final String COMMAND_ROOT = "adapters/bukkit/plugin/src/main/java/art/arcane/iris/command/"; @Rule @@ -70,7 +70,7 @@ public class GenerationRevisionScopeTest { assertNewSourceChangesRevision(fixture, root + "/NewSource.java"); } assertNewSourceChangesRevision(fixture, ENGINE_ROOT + "NewDiagnostics.java"); - assertNewSourceChangesRevision(fixture, NMS_ROOT + "NmsWorldLifecycleHelper.java"); + assertNewSourceChangesRevision(fixture, BUKKIT_POLICY_ROOT + "NewGenerationPolicy.java"); assertNewSourceChangesRevision(fixture, COMMAND_ROOT + "NewCommand.java"); assertNewSourceChangesRevision(fixture, "core/src/main/java/art/arcane/iris/world/safeguard/NewTask.java"); } @@ -87,10 +87,31 @@ public class GenerationRevisionScopeTest { assertTrue(relative, manifest.sources().containsKey(relative)); } assertFalse(manifest.sources().containsKey(ENGINE_ROOT + "EngineDiagnostics.java")); - assertFalse(manifest.sources().containsKey(NMS_ROOT + "NmsWorldLifecycle.java")); assertFalse(manifest.sources().containsKey(COMMAND_ROOT + "CommandIris.java")); } + @Test + public void publishedNativeSourcesChangeTheGenerationFingerprint() throws Exception { + Fixture fixture = fixture(); + Path nativeSources = fixture.root().resolve("native-sources.jar"); + writeNativeSources(nativeSources, "class NativeGenerator { int height = 1; }"); + Map dependencies = Map.of("volmlib", fixture.dependencies().get("volmlib"), + "com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources", nativeSources); + String original = GenerationBuildRevision.fingerprint( + GenerationBuildRevision.capture(fixture.options(), dependencies)); + writeNativeSources(nativeSources, "class NativeGenerator { int height = 2; }"); + assertNotEquals(original, GenerationBuildRevision.fingerprint( + GenerationBuildRevision.capture(fixture.options(), dependencies))); + } + + private static void writeNativeSources(Path archive, String content) throws IOException { + try (ZipOutputStream output = new ZipOutputStream(Files.newOutputStream(archive))) { + output.putNextEntry(new ZipEntry("modded/art/arcane/volmlib/nativelib/NativeGenerator.java")); + output.write(content.getBytes(StandardCharsets.UTF_8)); + output.closeEntry(); + } + } + private static void assertNewSourceChangesRevision(Fixture fixture, String relative) throws IOException { Path source = fixture.root().resolve(relative); write(source, "class NewSource {}"); @@ -144,9 +165,9 @@ public class GenerationRevisionScopeTest { "core/src/main/java/art/arcane/iris/pack/loading/IrisData.java", "core/src/main/java/art/arcane/iris/configuration/IrisSettings.java", "core/agent/src/main/java/GenerationTransformer.java", - NMS_ROOT + "NmsGenerationHooks.java", - NMS_ROOT + "NmsGenerationRegistry.java", - NMS_ROOT + "NMSBinding.java", + BUKKIT_POLICY_ROOT + "BukkitGeneratorContext.java", + BUKKIT_POLICY_ROOT + "BukkitBiomePolicy.java", + BUKKIT_POLICY_ROOT + "BukkitBinding.java", "spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java", "adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java" ); diff --git a/buildSrc/src/test/java/ModdedArtifactVerifierTest.java b/buildSrc/src/test/java/ModdedArtifactVerifierTest.java index 71fe2b578..3517999ee 100644 --- a/buildSrc/src/test/java/ModdedArtifactVerifierTest.java +++ b/buildSrc/src/test/java/ModdedArtifactVerifierTest.java @@ -25,8 +25,8 @@ public class ModdedArtifactVerifierTest { private static final String METADATA = "fabric.mod.json"; private static final String CODEC_CLASS = "art/arcane/volmlib/util/mantle/io/Lz4IOWorkerCodecSupport.class"; private static final String HARDWARE_CLASS = "art/arcane/iris/platform/bootstrap/getHardware.class"; - private static final String MIXIN_CONFIG = "irisworldgen.entity.mixins.json"; - private static final String CLIENT_MIXIN_CONFIG = "irisworldgen.client.mixins.json"; + private static final String MIXIN_CONFIG = "volmlib.entity.mixins.json"; + private static final String CLIENT_MIXIN_CONFIG = "volmlib.client.mixins.json"; private static final String NEOFORGE_METADATA = "META-INF/neoforge.mods.toml"; private static final List REQUIRED_ENTRIES = List.of(METADATA, CODEC_CLASS, HARDWARE_CLASS); private static final byte[] INTERNAL_CODEC = ( diff --git a/buildSrc/src/test/java/NativeRuntimeArtifactsTest.java b/buildSrc/src/test/java/NativeRuntimeArtifactsTest.java new file mode 100644 index 000000000..dcc752210 --- /dev/null +++ b/buildSrc/src/test/java/NativeRuntimeArtifactsTest.java @@ -0,0 +1,45 @@ +import org.gradle.api.GradleException; +import org.junit.Test; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class NativeRuntimeArtifactsTest { + @Test + public void recordsExactDependenciesAndRetainsSharedProviderReferences() throws Exception { + Path directory = Files.createTempDirectory("native-artifacts"); + File provider = directory.resolve("provider.jar").toFile(); + ClassWriter writer = new ClassWriter(0); + writer.visit(Opcodes.V17, Opcodes.ACC_PUBLIC, "art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider", null, + "art/arcane/volmlib/util/collection/KList", null); + writer.visitEnd(); + try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(provider.toPath()))) { + jar.putNextEntry(new JarEntry("art/arcane/volmlib/nativelib/v26_2_R1/terrain/Provider.class")); + jar.write(writer.toByteArray()); + jar.closeEntry(); + } + File rules = directory.resolve("keep.pro").toFile(); + NativeRuntimeArtifacts.generate(Map.of("native-common", provider, "native-v26_2_R1", provider), + "revision", directory.toFile(), rules); + assertTrue(Files.readString(rules.toPath()).contains("-keep class art.arcane.volmlib.util.collection.KList { *; }")); + File plugin = directory.resolve("plugin.jar").toFile(); + try (JarOutputStream jar = new JarOutputStream(Files.newOutputStream(plugin.toPath()))) { + jar.putNextEntry(new JarEntry(NativeRuntimeArtifacts.MANIFEST)); + Files.copy(directory.resolve(NativeRuntimeArtifacts.MANIFEST), jar); + jar.closeEntry(); + } + NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "revision"); + assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_3_R1"), "revision")); + assertThrows(GradleException.class, () -> NativeRuntimeArtifacts.verify(plugin, List.of("v26_2_R1"), "other")); + } +} diff --git a/core/build.gradle b/core/build.gradle index 913ebb473..a0e3d5509 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -93,6 +93,12 @@ tasks.named('processResources').configure { */ dependencies { api(project(':spi')) + api("com.github.VolmitSoftware.VolmLib:native-api:${volmLibCoordinate.tokenize(':').last()}") { + transitive = false + } + api("com.github.VolmitSoftware.VolmLib:native-terrain-api:${volmLibCoordinate.tokenize(':').last()}") { + transitive = false + } // Provided or Classpath compileOnly(libs.lombok) diff --git a/core/src/externalProviderTest/java/art/arcane/iris/integration/data/CraftEngineDataProviderTest.java b/core/src/externalProviderTest/java/art/arcane/iris/integration/data/CraftEngineDataProviderTest.java index 4d44a567e..325c095c0 100644 --- a/core/src/externalProviderTest/java/art/arcane/iris/integration/data/CraftEngineDataProviderTest.java +++ b/core/src/externalProviderTest/java/art/arcane/iris/integration/data/CraftEngineDataProviderTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.testsupport.BukkitTestServer; import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import art.arcane.volmlib.util.collection.KMap; import net.momirealms.craftengine.bukkit.api.CraftEngineBlocks; import net.momirealms.craftengine.bukkit.api.CraftEngineFurniture; diff --git a/core/src/main/java/art/arcane/iris/command/IrisCommand.java b/core/src/main/java/art/arcane/iris/command/IrisCommand.java index a16e233d5..2352f042d 100644 --- a/core/src/main/java/art/arcane/iris/command/IrisCommand.java +++ b/core/src/main/java/art/arcane/iris/command/IrisCommand.java @@ -26,7 +26,7 @@ import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.world.task.J; import art.arcane.volmlib.util.collection.KList; import lombok.Data; @@ -60,11 +60,11 @@ public class IrisCommand { @Description("The weather that is required for the command to execute.") private IrisWeather weather = IrisWeather.ANY; - public boolean isValid(PlatformWorld world) { + public boolean isValid(NativeWorld world) { return timeBlock.isWithin(world) && weather.is(world); } - public void run(PlatformWorld world, int x, int y, int z) { + public void run(NativeWorld world, int x, int y, int z) { if (!isValid(world)) { return; } diff --git a/core/src/main/java/art/arcane/iris/command/IrisCommandRegistry.java b/core/src/main/java/art/arcane/iris/command/IrisCommandRegistry.java index 651db97b4..23082baf3 100644 --- a/core/src/main/java/art/arcane/iris/command/IrisCommandRegistry.java +++ b/core/src/main/java/art/arcane/iris/command/IrisCommandRegistry.java @@ -26,7 +26,7 @@ import art.arcane.iris.pack.schema.annotation.MinNumber; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; import art.arcane.iris.platform.bukkit.BukkitWorld; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import lombok.Data; @@ -75,7 +75,7 @@ public class IrisCommandRegistry { public void run(Player p) { if (rawCommands.isNotEmpty()) { - PlatformWorld world = new BukkitWorld(p.getWorld()); + NativeWorld world = new BukkitWorld(p.getWorld()); Location part = p.getLocation().clone().add( commandRandomAltX ? RNG.r.d(-commandOffsetX, commandOffsetX) : commandOffsetX, commandRandomAltY ? RNG.r.d(-commandOffsetY, commandOffsetY) : commandOffsetY, diff --git a/core/src/main/java/art/arcane/iris/command/handlers/DataVersionHandler.java b/core/src/main/java/art/arcane/iris/command/handlers/DataVersionHandler.java index 2766d40e2..57ee42b73 100644 --- a/core/src/main/java/art/arcane/iris/command/handlers/DataVersionHandler.java +++ b/core/src/main/java/art/arcane/iris/command/handlers/DataVersionHandler.java @@ -1,6 +1,6 @@ package art.arcane.iris.command.handlers; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.director.DirectorParameterHandler; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; diff --git a/core/src/main/java/art/arcane/iris/diagnostics/LogFilterSVC.java b/core/src/main/java/art/arcane/iris/diagnostics/LogFilterSVC.java index 7431bfb61..356ff040e 100644 --- a/core/src/main/java/art/arcane/iris/diagnostics/LogFilterSVC.java +++ b/core/src/main/java/art/arcane/iris/diagnostics/LogFilterSVC.java @@ -1,5 +1,7 @@ package art.arcane.iris.diagnostics; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.server.NativeServerDiagnostics; import art.arcane.iris.platform.bukkit.plugin.IrisService; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -15,19 +17,20 @@ import java.util.List; public class LogFilterSVC implements IrisService, Filter { private static final String HEIGHTMAP_MISMATCH = "Ignoring heightmap data for chunk"; - private static final String RAID_PERSISTENCE = "Could not save data net.minecraft.world.entity.raid.PersistentRaid"; private static final String DUPLICATE_ENTITY_UUID = "UUID of added entity already exists"; // Immutable: check() runs on arbitrary logger threads, and the root logger is JVM-global, // so a mutable static here was both a CME hazard and grew by three entries per enable. - private static final List FILTERS = List.of(HEIGHTMAP_MISMATCH, RAID_PERSISTENCE, DUPLICATE_ENTITY_UUID); + private static final List FILTERS = List.of(HEIGHTMAP_MISMATCH, DUPLICATE_ENTITY_UUID); + private volatile NativeServerDiagnostics diagnostics; private LoggerConfig installedLogger; public void onEnable() { if (installedLogger != null) { return; } + diagnostics = NativeAdapters.find(NativeServerDiagnostics.class).orElse(null); LoggerConfig logger = ((Logger) LogManager.getRootLogger()).get(); logger.addFilter(this); installedLogger = logger; @@ -135,6 +138,9 @@ public class LogFilterSVC implements IrisService, Filter { if (string == null) { return Result.NEUTRAL; } + if (diagnostics != null && diagnostics.isRaidPersistenceMessage(string)) { + return Result.DENY; + } for (String filter : FILTERS) { if (string.contains(filter)) { return Result.DENY; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiome.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiome.java index eec461d9d..6bdbb7c6f 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiome.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiome.java @@ -46,7 +46,7 @@ import art.arcane.iris.studio.render.RenderType; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.IrisRegistrant; import art.arcane.iris.generation.runtime.IrisComplex; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.cache.LazyBoundedCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; @@ -72,7 +72,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.bukkit.block.Biome; import java.awt.Color; @@ -265,19 +265,19 @@ public class IrisBiome extends IrisRegistrant implements Rarity { return IrisBiomeOres.hasSurfaceOreReplaceableBlocks(this); } - public boolean canReplaceSurfaceOre(PlatformBlockState state) { + public boolean canReplaceSurfaceOre(NativeBlockState state) { return IrisBiomeOres.canReplaceSurfaceOre(this, state); } - public PlatformBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { + public NativeBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { return IrisBiomeOres.generateOres(this, x, y, z, rng, data, surface); } - public PlatformBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { return IrisBiomeOres.generateSurfaceOres(this, x, y, z, rng, data); } - public PlatformBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { return IrisBiomeOres.generateUndergroundOres(this, x, y, z, rng, data); } @@ -522,23 +522,23 @@ public class IrisBiome extends IrisRegistrant implements Rarity { return childrenCell.aquire(() -> getChildStyle().create(random.nextParallelRNG(sig * 2137), getLoader()).bake().scale(scale).bake()); } - public KList generateLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { + public KList generateLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { return IrisBiomeLayerGenerator.generateLayers(this, dim, wx, wz, random, maxDepth, height, rdata, complex); } - public KList generateLayersWithSlope(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, ProceduralStream slopeStream) { + public KList generateLayersWithSlope(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, ProceduralStream slopeStream) { return IrisBiomeLayerGenerator.generateLayers(this, dim, wx, wz, random, maxDepth, height, rdata, slopeStream); } - public KList generateCeilingLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { + public KList generateCeilingLayers(IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { return IrisBiomeLayerGenerator.generateCeilingLayers(this, dim, wx, wz, random, maxDepth, height, rdata, complex); } - public KList generateLockedLayers(double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex) { + public KList generateLockedLayers(double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex) { return IrisBiomeLayerGenerator.generateLockedLayers(this, wx, wz, random, maxDepthf, height, rdata, complex); } - public KList generateSeaLayers(double wx, double wz, RNG random, int maxDepth, IrisData rdata) { + public KList generateSeaLayers(double wx, double wz, RNG random, int maxDepth, IrisData rdata) { return IrisBiomeLayerGenerator.generateSeaLayers(this, wx, wz, random, maxDepth, rdata); } @@ -562,7 +562,7 @@ public class IrisBiome extends IrisRegistrant implements Rarity { return IrisBiomeLayerGenerator.getLayerSeaHeightGenerators(this, rng, data); } - public PlatformBlockState getSurfaceBlock(int x, int z, RNG rng, IrisData idm) { + public NativeBlockState getSurfaceBlock(int x, int z, RNG rng, IrisData idm) { return IrisBiomeLayerGenerator.getSurfaceBlock(this, x, z, rng, idm); } diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustom.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustom.java index 16f839ddf..e7c5a7e6e 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustom.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustom.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.validation.KeyStatus; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.iris.pack.schema.annotation.DependsOn; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomParticle.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomParticle.java index 4c3cba1e1..0a8e7b437 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomParticle.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomParticle.java @@ -18,13 +18,13 @@ package art.arcane.iris.generation.biome; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomSpawn.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomSpawn.java index 3d71d4139..c46e0e254 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomSpawn.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeCustomSpawn.java @@ -18,14 +18,14 @@ package art.arcane.iris.generation.biome; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; import art.arcane.iris.pack.schema.annotation.RegistryListEntityType; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeDerivatives.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeDerivatives.java index ce382c9f1..4e42116e5 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeDerivatives.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeDerivatives.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import org.bukkit.NamespacedKey; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGenLinks.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGenLinks.java index 8d6e21d7d..22e200948 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGenLinks.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGenLinks.java @@ -18,7 +18,7 @@ package art.arcane.iris.generation.biome; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGeneratorLink.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGeneratorLink.java index 37d902749..df3c081ad 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGeneratorLink.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeGeneratorLink.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.generation.noise.IrisGenerator; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.DependsOn; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeLayerGenerator.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeLayerGenerator.java index 0d49710b9..4feb7c38e 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeLayerGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeLayerGenerator.java @@ -25,10 +25,10 @@ import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.BoundBlockState; import art.arcane.volmlib.util.noise.CNG; @@ -46,22 +46,22 @@ final class IrisBiomeLayerGenerator { private IrisBiomeLayerGenerator() { } - static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { + static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { return generateLayers(biome, dim, wx, wz, random, maxDepth, height, rdata, complex, null); } - static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, ProceduralStream slopeStream) { + static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, ProceduralStream slopeStream) { return generateLayers(biome, dim, wx, wz, random, maxDepth, height, rdata, null, slopeStream); } - private static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex, ProceduralStream slopeStream) { + private static KList generateLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex, ProceduralStream slopeStream) { if (biome.isLockLayers()) { - KList data = generateLockedLayers(biome, wx, wz, random, maxDepth, height, rdata, complex, slopeStream); + KList data = generateLockedLayers(biome, wx, wz, random, maxDepth, height, rdata, complex, slopeStream); appendSurfaceFallback(data, biome, dim, wx, wz, random, Math.min(maxDepth, biome.getLockLayersMax()), rdata); return data; } - KList data = new KList<>(); + KList data = new KList<>(); if (maxDepth <= 0) { return data; @@ -120,7 +120,7 @@ final class IrisBiomeLayerGenerator { return data; } - private static void appendSurfaceFallback(KList data, IrisBiome biome, IrisDimension dim, + private static void appendSurfaceFallback(KList data, IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, IrisData rdata) { if (!data.isEmpty() || maxDepth <= 0 || biome.getLayers().isEmpty()) { return; @@ -134,8 +134,8 @@ final class IrisBiomeLayerGenerator { } } - static KList generateCeilingLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { - KList data = new KList<>(); + static KList generateCeilingLayers(IrisBiome biome, IrisDimension dim, double wx, double wz, RNG random, int maxDepth, int height, IrisData rdata, IrisComplex complex) { + KList data = new KList<>(); if (maxDepth <= 0) { return data; @@ -181,12 +181,12 @@ final class IrisBiomeLayerGenerator { return data; } - static KList generateLockedLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex) { + static KList generateLockedLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex) { return generateLockedLayers(biome, wx, wz, random, maxDepthf, height, rdata, complex, null); } - private static KList generateLockedLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex, ProceduralStream slopeStream) { - KList data = new KList<>(); + private static KList generateLockedLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepthf, int height, IrisData rdata, IrisComplex complex, ProceduralStream slopeStream) { + KList data = new KList<>(); int maxDepth = Math.min(maxDepthf, biome.getLockLayersMax()); KList layers = biome.getLayers(); int layerCount = layers.size(); @@ -276,8 +276,8 @@ final class IrisBiomeLayerGenerator { : complex.getSlopeStream().getDouble(x, z); } - static KList generateSeaLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepth, IrisData rdata) { - KList data = new KList<>(); + static KList generateSeaLayers(IrisBiome biome, double wx, double wz, RNG random, int maxDepth, IrisData rdata) { + KList data = new KList<>(); KList seaLayers = biome.getSeaLayers(); int layerCount = seaLayers.size(); @@ -339,7 +339,7 @@ final class IrisBiomeLayerGenerator { }); } - private static void appendLayer(KList blocks, IrisBiomePaletteLayer layer, + private static void appendLayer(KList blocks, IrisBiomePaletteLayer layer, int layerIndex, int thickness, double x, double z, RNG random, int maxDepth, IrisData data) { double zoom = layer.getZoom(); @@ -353,7 +353,7 @@ final class IrisBiomeLayerGenerator { } } - static PlatformBlockState getSurfaceBlock(IrisBiome biome, int x, int z, RNG rng, IrisData idm) { + static NativeBlockState getSurfaceBlock(IrisBiome biome, int x, int z, RNG rng, IrisData idm) { KList layers = biome.getLayers(); if (layers.isEmpty()) { diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeOres.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeOres.java index c9f540807..6fe084f33 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeOres.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomeOres.java @@ -23,8 +23,8 @@ import art.arcane.iris.generation.decoration.IrisOreGeneratorBounds; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.util.cache.AtomicCache; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KSet; @@ -39,16 +39,16 @@ final class IrisBiomeOres { private IrisBiomeOres() { } - static PlatformBlockState generateOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data, boolean surface) { + static NativeBlockState generateOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data, boolean surface) { KList localOres = surface ? getSurfaceOres(biome) : getUndergroundOres(biome); return generateOres(localOres, x, y, z, rng, data); } - static PlatformBlockState generateSurfaceOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data) { + static NativeBlockState generateSurfaceOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data) { return generateOres(getSurfaceOres(biome), x, y, z, rng, data); } - static PlatformBlockState generateUndergroundOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data) { + static NativeBlockState generateUndergroundOres(IrisBiome biome, int x, int y, int z, RNG rng, IrisData data) { return generateOres(getUndergroundOres(biome), x, y, z, rng, data); } @@ -64,7 +64,7 @@ final class IrisBiomeOres { return biome.getSurfaceOreReplaceableBlocks() != null; } - static boolean canReplaceSurfaceOre(IrisBiome biome, PlatformBlockState state) { + static boolean canReplaceSurfaceOre(IrisBiome biome, NativeBlockState state) { if (!hasSurfaceOreReplaceableBlocks(biome)) { return true; } @@ -130,7 +130,7 @@ final class IrisBiomeOres { }); } - private static PlatformBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { + private static NativeBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { if (localOres.isEmpty()) { return null; } @@ -138,7 +138,7 @@ final class IrisBiomeOres { int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - PlatformBlockState ore = oreGenerator.generate(x, y, z, rng, data); + NativeBlockState ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; } @@ -149,7 +149,7 @@ final class IrisBiomeOres { private static KSet resolveSurfaceOreReplaceableBlocks(IrisBiome biome) { KSet resolved = new KSet<>(); for (String key : biome.getSurfaceOreReplaceableBlocks()) { - PlatformBlockState state = B.getStateOrNull(key, false); + NativeBlockState state = B.getStateOrNull(key, false); if (state != null) { resolved.add(IrisProceduralBlocks.materialKey(state)); } diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomePaletteLayer.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomePaletteLayer.java index b035954d0..20237f2fc 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomePaletteLayer.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisBiomePaletteLayer.java @@ -24,7 +24,7 @@ import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.cache.LazyBoundedCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; @@ -42,7 +42,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.Setter; import lombok.experimental.Accessors; @@ -57,7 +57,7 @@ import java.util.concurrent.atomic.AtomicReference; public class IrisBiomePaletteLayer { private static final int LAYER_GENERATOR_CACHE_SIZE = 32; - private final transient AtomicCache> blockData = new AtomicCache<>(); + private final transient AtomicCache> blockData = new AtomicCache<>(); @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) private final transient LazyBoundedCache layerGenerators = @@ -100,7 +100,7 @@ public class IrisBiomePaletteLayer { return heightGenerator.aquire(() -> CNG.signature(rng.nextParallelRNG(minHeight * maxHeight + getBlockData(data).size()))); } - public PlatformBlockState get(RNG rng, double x, double y, double z, IrisData data) { + public NativeBlockState get(RNG rng, double x, double y, double z, IrisData data) { return get(rng, 0, x, y, z, data); } @@ -109,8 +109,8 @@ public class IrisBiomePaletteLayer { * only built inside the lazy layer generator initializer, using the exact same seed derivation * as {@code parent.nextParallelRNG(signature)} followed by the generator signature. */ - public PlatformBlockState get(RNG parent, int signature, double x, double y, double z, IrisData data) { - KList localBlockData = getBlockData(data); + public NativeBlockState get(RNG parent, int signature, double x, double y, double z, IrisData data) { + KList localBlockData = getBlockData(data); if (localBlockData.isEmpty()) { return null; @@ -154,8 +154,8 @@ public class IrisBiomePaletteLayer { return palette; } - public KList getBlockData(IrisData data) { - KList cached = blockData.getIfPresent(); + public KList getBlockData(IrisData data) { + KList cached = blockData.getIfPresent(); if (cached != null) { return cached; @@ -163,9 +163,9 @@ public class IrisBiomePaletteLayer { return blockData.aquire(() -> { - KList blockData = new KList<>(); + KList blockData = new KList<>(); for (IrisBlockData ix : palette) { - PlatformBlockState bx = ix.getBlockData(data); + NativeBlockState bx = ix.getBlockData(data); if (bx != null) { for (int i = 0; i < ix.getWeight(); i++) { blockData.add(bx); diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisCustomBiomeAliasResolver.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisCustomBiomeAliasResolver.java index e362bccf4..374abc7a8 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisCustomBiomeAliasResolver.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisCustomBiomeAliasResolver.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import java.io.IOException; import java.util.Collection; diff --git a/core/src/main/java/art/arcane/iris/generation/biome/IrisFloatingChildBiomes.java b/core/src/main/java/art/arcane/iris/generation/biome/IrisFloatingChildBiomes.java index af7c5c03a..2cef212b8 100644 --- a/core/src/main/java/art/arcane/iris/generation/biome/IrisFloatingChildBiomes.java +++ b/core/src/main/java/art/arcane/iris/generation/biome/IrisFloatingChildBiomes.java @@ -32,7 +32,7 @@ import art.arcane.iris.structure.object.IrisObjectScale; import art.arcane.volmlib.util.math.Rarity; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/generation/block/B.java b/core/src/main/java/art/arcane/iris/generation/block/B.java index e5997d2e9..7686f7a62 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/B.java +++ b/core/src/main/java/art/arcane/iris/generation/block/B.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.block; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.collection.KList; @@ -12,111 +12,111 @@ public class B { private record BoundRegistries(IrisPlatform platform, PlatformRegistries registries) { } - public static PlatformBlockState getState(String bdxf) { + public static NativeBlockState getState(String bdxf) { return registries().block(bdxf); } - public static PlatformBlockState getStateOrNull(String bdxf) { + public static NativeBlockState getStateOrNull(String bdxf) { return registries().blockOrNull(bdxf); } - public static PlatformBlockState getStateOrNull(String bdxf, boolean warn) { + public static NativeBlockState getStateOrNull(String bdxf, boolean warn) { return registries().blockOrNull(bdxf, warn); } - public static KList getStates(KList find) { - KList states = new KList<>(find.size()); + public static KList getStates(KList find) { + KList states = new KList<>(find.size()); for (String key : find) { states.add(getState(key)); } return states; } - public static PlatformBlockState getAirState() { + public static NativeBlockState getAirState() { return registries().air(); } - public static PlatformBlockState toDeepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public static NativeBlockState toDeepSlateOre(NativeBlockState block, NativeBlockState ore) { return registries().deepSlateOre(block, ore); } - public static boolean isAir(PlatformBlockState state) { + public static boolean isAir(NativeBlockState state) { return state == null || state.isAir(); } - public static boolean isSolid(PlatformBlockState state) { + public static boolean isSolid(NativeBlockState state) { return state != null && state.isSolid(); } - public static boolean isOccluding(PlatformBlockState state) { + public static boolean isOccluding(NativeBlockState state) { return state != null && state.isOccluding(); } - public static boolean isFluid(PlatformBlockState state) { + public static boolean isFluid(NativeBlockState state) { return state != null && state.isFluid(); } - public static boolean isAirOrFluid(PlatformBlockState state) { + public static boolean isAirOrFluid(NativeBlockState state) { return state == null || state.isAirOrFluid(); } - public static boolean isWater(PlatformBlockState state) { + public static boolean isWater(NativeBlockState state) { return state != null && state.isWater(); } - public static boolean isWaterLogged(PlatformBlockState state) { + public static boolean isWaterLogged(NativeBlockState state) { return state != null && state.isWaterLogged(); } - public static boolean isLit(PlatformBlockState state) { + public static boolean isLit(NativeBlockState state) { return state != null && state.isLit(); } - public static boolean isUpdatable(PlatformBlockState state) { + public static boolean isUpdatable(NativeBlockState state) { return state != null && state.isUpdatable(); } - public static boolean isFoliage(PlatformBlockState state) { + public static boolean isFoliage(NativeBlockState state) { return state != null && state.isFoliage(); } - public static boolean isTreeBlock(PlatformBlockState state) { + public static boolean isTreeBlock(NativeBlockState state) { return state != null && state.isTreeBlock(); } - public static boolean isFoliagePlantable(PlatformBlockState state) { + public static boolean isFoliagePlantable(NativeBlockState state) { return state != null && state.isFoliagePlantable(); } - public static boolean isDecorant(PlatformBlockState state) { + public static boolean isDecorant(NativeBlockState state) { return state != null && state.isDecorant(); } - public static boolean isStorage(PlatformBlockState state) { + public static boolean isStorage(NativeBlockState state) { return state != null && state.isStorage(); } - public static boolean isStorageChest(PlatformBlockState state) { + public static boolean isStorageChest(NativeBlockState state) { return state != null && state.isStorageChest(); } - public static boolean isOre(PlatformBlockState state) { + public static boolean isOre(NativeBlockState state) { return state != null && state.isOre(); } - public static boolean isDeepSlate(PlatformBlockState state) { + public static boolean isDeepSlate(NativeBlockState state) { return state != null && state.isDeepSlate(); } - public static boolean isVineBlock(PlatformBlockState state) { + public static boolean isVineBlock(NativeBlockState state) { return state != null && state.isVineBlock(); } - public static boolean canPlaceOnto(PlatformBlockState mat, PlatformBlockState onto) { + public static boolean canPlaceOnto(NativeBlockState mat, NativeBlockState onto) { return mat != null && onto != null && mat.canPlaceOnto(onto); } - public static boolean matches(PlatformBlockState filter, PlatformBlockState state) { + public static boolean matches(NativeBlockState filter, NativeBlockState state) { return filter != null && state != null && filter.matches(state); } diff --git a/core/src/main/java/art/arcane/iris/generation/block/BlockDataMergeSupport.java b/core/src/main/java/art/arcane/iris/generation/block/BlockDataMergeSupport.java index c228fdb7c..628f02cc7 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/BlockDataMergeSupport.java +++ b/core/src/main/java/art/arcane/iris/generation/block/BlockDataMergeSupport.java @@ -1,7 +1,7 @@ package art.arcane.iris.generation.block; import art.arcane.iris.platform.bukkit.BukkitBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import org.bukkit.block.data.BlockData; @@ -15,7 +15,7 @@ public final class BlockDataMergeSupport { } public interface StateMerger { - PlatformBlockState merge(PlatformBlockState base, PlatformBlockState update); + NativeBlockState merge(NativeBlockState base, NativeBlockState update); } public static synchronized StateMerger bindPlatformMerger(StateMerger merger) { @@ -28,7 +28,7 @@ public final class BlockDataMergeSupport { PLATFORM_MERGER = merger; } - public static PlatformBlockState merge(PlatformBlockState base, PlatformBlockState update) { + public static NativeBlockState merge(NativeBlockState base, NativeBlockState update) { StateMerger merger = PLATFORM_MERGER; if (merger != null) { return merger.merge(base, update); diff --git a/core/src/main/java/art/arcane/iris/generation/block/BoundBlockState.java b/core/src/main/java/art/arcane/iris/generation/block/BoundBlockState.java index 86f7fea94..75ea2c366 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/BoundBlockState.java +++ b/core/src/main/java/art/arcane/iris/generation/block/BoundBlockState.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.block; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public final class BoundBlockState { private final String key; @@ -34,7 +34,7 @@ public final class BoundBlockState { return new BoundBlockState(key); } - public PlatformBlockState get() { + public NativeBlockState get() { Bound current = bound; IrisPlatform platform = IrisPlatforms.getOrNull(); @@ -48,6 +48,6 @@ public final class BoundBlockState { return resolved.state(); } - private record Bound(IrisPlatform platform, PlatformBlockState state) { + private record Bound(IrisPlatform platform, NativeBlockState state) { } } diff --git a/core/src/main/java/art/arcane/iris/generation/block/IrisBlockData.java b/core/src/main/java/art/arcane/iris/generation/block/IrisBlockData.java index e0000b5c7..66debf672 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/IrisBlockData.java +++ b/core/src/main/java/art/arcane/iris/generation/block/IrisBlockData.java @@ -23,7 +23,7 @@ import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.integration.Identifier; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.IrisRegistrant; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -31,7 +31,7 @@ import art.arcane.iris.pack.schema.annotation.RegistryListBlockType; import art.arcane.iris.pack.schema.annotation.RegistryMapBlockState; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import lombok.AllArgsConstructor; @@ -50,7 +50,7 @@ import java.util.Map; @Data @EqualsAndHashCode(callSuper = false) public class IrisBlockData extends IrisRegistrant { - private final transient AtomicCache blockdata = new AtomicCache<>(); + private final transient AtomicCache blockdata = new AtomicCache<>(); private final transient AtomicCache realProperties = new AtomicCache<>(); @RegistryListBlockType @Required @@ -154,13 +154,13 @@ public class IrisBlockData extends IrisRegistrant { return keyify(getBlock()) + computeProperties(); } - public PlatformBlockState getBlockData(IrisData data) { + public NativeBlockState getBlockData(IrisData data) { return blockdata.aquire(() -> { IrisBlockData customData = data.getBlockLoader() == null ? null : data.getBlockLoader().load(getBlock(), false); if (customData != null) { - PlatformBlockState customState = customData.getBlockData(data); + NativeBlockState customState = customData.getBlockData(data); if (customState != null) { if (getData().isEmpty()) { @@ -175,7 +175,7 @@ public class IrisBlockData extends IrisRegistrant { IrisLogging.debug("Block Data used " + sx + " (CUSTOM)"); } - PlatformBlockState bx = resolve(data, sx); + NativeBlockState bx = resolve(data, sx); if (bx != null) { return bx; @@ -186,7 +186,7 @@ public class IrisBlockData extends IrisRegistrant { } String ss = stateKey(); - PlatformBlockState resolved = resolve(data, ss); + NativeBlockState resolved = resolve(data, ss); if (debug) { IrisLogging.debug("Block Data used " + ss); @@ -210,7 +210,7 @@ public class IrisBlockData extends IrisRegistrant { * (after custom blocks, the gate chain and every backup) yields a {@link art.arcane.iris.pack.validation.MissingBlockState} * carrying the key instead of air, so a palette placeholder with the same key can still be matched and rewritten. */ - public PlatformBlockState getBlockDataOrPlaceholder(IrisData data) { + public NativeBlockState getBlockDataOrPlaceholder(IrisData data) { ContentGate gate = data.getContentGate(); String state = stateKey(); @@ -222,7 +222,7 @@ public class IrisBlockData extends IrisRegistrant { return backup.getBlockDataOrPlaceholder(data); } - PlatformBlockState placeholder = gate.resolveBlockOrPlaceholder(state); + NativeBlockState placeholder = gate.resolveBlockOrPlaceholder(state); return placeholder == null ? getBlockData(data) : placeholder; } @@ -237,7 +237,7 @@ public class IrisBlockData extends IrisRegistrant { * {@link art.arcane.iris.pack.validation.KeyStatus#UNKNOWN}) gates nothing and answers null for every key, so this * keeps the plain registry lookup in that case - otherwise every block in the pack would resolve to air. */ - private static PlatformBlockState resolve(IrisData data, String state) { + private static NativeBlockState resolve(IrisData data, String state) { ContentGate gate = data.getContentGate(); if (gate == null || !gate.ready()) { @@ -250,7 +250,7 @@ public class IrisBlockData extends IrisRegistrant { public TileData tryGetTile(IrisData data) { //TODO Do like a registry thing with the tile data registry. Also update the parsing of data to include **block** entities. - PlatformBlockState state = getBlockData(data); + NativeBlockState state = getBlockData(data); String stateKey = state.key(); int bracket = stateKey.indexOf('['); String blockKey = bracket >= 0 ? stateKey.substring(0, bracket) : stateKey; diff --git a/core/src/main/java/art/arcane/iris/generation/block/LegacyTileData.java b/core/src/main/java/art/arcane/iris/generation/block/LegacyTileData.java index f16a1659b..fe537b4ca 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/LegacyTileData.java +++ b/core/src/main/java/art/arcane/iris/generation/block/LegacyTileData.java @@ -11,8 +11,8 @@ import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Sign; -import art.arcane.iris.platform.bukkit.nms.container.Pair; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.collection.Pair; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.iris.platform.reflect.KeyedType; diff --git a/core/src/main/java/art/arcane/iris/generation/block/TileData.java b/core/src/main/java/art/arcane/iris/generation/block/TileData.java index 7b47715ce..9ec61df1a 100644 --- a/core/src/main/java/art/arcane/iris/generation/block/TileData.java +++ b/core/src/main/java/art/arcane/iris/generation/block/TileData.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.block; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.Strictness; @@ -72,7 +72,7 @@ public class TileData implements Cloneable { } public interface TileFactory { - TileData create(PlatformBlockState state, KMap properties); + TileData create(NativeBlockState state, KMap properties); } public static synchronized TileReader bindPlatformReader(TileReader reader) { @@ -136,7 +136,7 @@ public class TileData implements Cloneable { return new TileData().fromBukkit(block); } - public static TileData of(PlatformBlockState state, KMap properties) { + public static TileData of(NativeBlockState state, KMap properties) { TileFactory factory = PLATFORM_FACTORY; if (factory != null) { return factory.create(state, properties); diff --git a/core/src/main/java/art/arcane/iris/generation/cache/AtomicCache.java b/core/src/main/java/art/arcane/iris/generation/cache/AtomicCache.java deleted file mode 100644 index 97aff5c41..000000000 --- a/core/src/main/java/art/arcane/iris/generation/cache/AtomicCache.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.generation.cache; - -import art.arcane.iris.spi.IrisLogging; -import art.arcane.volmlib.util.function.NastySupplier; - -import java.util.function.Supplier; - -public class AtomicCache { - private static final Object NULL_VALUE = new Object(); - private transient final Object initLock = new Object(); - private transient final boolean nullSupport; - private transient volatile Object value; - private transient volatile Throwable failure; - - public AtomicCache() { - this(false); - } - - public AtomicCache(boolean nullSupport) { - this.nullSupport = nullSupport; - } - - public void reset() { - synchronized (initLock) { - value = null; - failure = null; - } - } - - public T getIfPresent() { - return unwrap(value); - } - - public T aquireNasty(NastySupplier t) { - return aquire(() -> { - try { - return t.get(); - } catch (Throwable e) { - return null; - } - }); - } - - public T aquireNastyPrint(NastySupplier t) { - return aquire(() -> { - try { - return t.get(); - } catch (Throwable e) { - IrisLogging.reportError("Atomic cache supplier failed.", e); - return null; - } - }); - } - - /** - * Like {@link #aquire(Supplier)} but propagates a supplier failure to the caller instead - * of swallowing it into a null return. For values that are mandatory: a caller of a - * "@NotNull" accessor should see the supplier's real exception, not a downstream NPE. - *

- * The supplier is retried on every call until it produces a value, which is what a caller that can - * repair the cause between attempts needs. Use {@link #aquireOnceOrThrow(Supplier)} when re-running a - * failed supplier is itself the problem. - */ - public T aquireOrThrow(Supplier t) { - Object v = value; - - if (v != null) { - return unwrap(v); - } - - synchronized (initLock) { - v = value; - - if (v != null) { - return unwrap(v); - } - - T computed = t.get(); - if (computed == null) { - throw new IllegalStateException("Atomic cache supplier produced null"); - } - value = computed; - return computed; - } - } - - /** - * Like {@link #aquireOrThrow(Supplier)} but memoizes the failure as well as the value, so a supplier - * that cannot produce one is run exactly once and every later caller is told the same reason. For - * values whose supplier rebuilds shared state, where re-running it per call repeats that work and - * hides the original cause behind whatever the retry happens to fail on. {@link #reset()} clears it. - */ - public T aquireOnceOrThrow(Supplier t) { - Object v = value; - - if (v != null) { - return unwrap(v); - } - rethrowFailure(failure); - - synchronized (initLock) { - v = value; - - if (v != null) { - return unwrap(v); - } - rethrowFailure(failure); - - T computed; - try { - computed = t.get(); - if (computed == null) { - throw new IllegalStateException("Atomic cache supplier produced null"); - } - } catch (Throwable e) { - failure = e; - throw e; - } - value = computed; - return computed; - } - } - - private static void rethrowFailure(Throwable memoized) { - if (memoized == null) { - return; - } - if (memoized instanceof RuntimeException runtimeFailure) { - throw runtimeFailure; - } - if (memoized instanceof Error error) { - throw error; - } - throw new IllegalStateException("Atomic cache supplier failed", memoized); - } - - public T aquire(Supplier t) { - Object v = value; - - if (v != null) { - return unwrap(v); - } - - synchronized (initLock) { - v = value; - - if (v != null) { - return unwrap(v); - } - - try { - T computed = t.get(); - - if (computed != null) { - value = computed; - return computed; - } - - if (nullSupport) { - value = NULL_VALUE; - } - } catch (Throwable e) { - // aquire retries on every call, so a supplier that stays broken is reached per sample. - // The first statement of it carries the stack; the rest are traces of the same cause. - if (IrisLogging.warnOnce("atomic-cache:" + e.getClass().getName() + ":" + e.getMessage(), - "Atomic cache supplier failed: %s: %s", e.getClass().getSimpleName(), e.getMessage())) { - IrisLogging.reportError(e); - } - } - - return null; - } - } - - @SuppressWarnings("unchecked") - private T unwrap(Object v) { - return v == null || v == NULL_VALUE ? null : (T) v; - } -} diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkBiomeHunkView.java b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkBiomeHunkView.java index 963ee84d4..4dd7268b0 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkBiomeHunkView.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkBiomeHunkView.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.chunk; import art.arcane.iris.generation.runtime.BlockEditAccess; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.platform.bukkit.BukkitBiome; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.volmlib.util.hunk.Hunk; import org.bukkit.Chunk; import org.bukkit.World; @@ -29,7 +29,7 @@ import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @SuppressWarnings("ClassCanBeRecord") -public class ChunkBiomeHunkView extends art.arcane.volmlib.util.hunk.view.ChunkWorldHunkView implements Hunk { +public class ChunkBiomeHunkView extends art.arcane.volmlib.util.hunk.view.ChunkWorldHunkView implements Hunk { public ChunkBiomeHunkView(Chunk chunk) { super(chunk, chunk.getWorld().getMaxHeight(), diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkHolder.java b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkHolder.java index 878c47be3..a7e6d5c69 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkHolder.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkHolder.java @@ -19,7 +19,8 @@ package art.arcane.iris.generation.chunk; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockVolume; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.BoundBlockState; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.volmlib.util.hunk.storage.AtomicHunk; @@ -27,7 +28,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.generator.ChunkGenerator.ChunkData; @SuppressWarnings("ClassCanBeRecord") -public class ChunkDataHunkHolder extends AtomicHunk { +public class ChunkDataHunkHolder extends AtomicHunk implements NativeBlockVolume { private static final BoundBlockState AIR = BoundBlockState.of("AIR"); private final ChunkData chunk; @@ -48,7 +49,7 @@ public class ChunkDataHunkHolder extends AtomicHunk { } @Override - public void setRaw(int x, int y, int z, PlatformBlockState t) { + public void setRaw(int x, int y, int z, NativeBlockState t) { // Block-hunk contract: null means "no write", never "erase" — ChunkDataHunkView and // the modded ModdedBlockBuffer already discard nulls, and storing one here made the // Bukkit output diverge from the modded loaders for the same engine emission. @@ -59,17 +60,17 @@ public class ChunkDataHunkHolder extends AtomicHunk { } @Override - public PlatformBlockState getRaw(int x, int y, int z) { + public NativeBlockState getRaw(int x, int y, int z) { if (y < 0 || y >= getHeight()) { return AIR.get(); } - PlatformBlockState b = super.getRaw(x, y, z); + NativeBlockState b = super.getRaw(x, y, z); return b != null ? b : AIR.get(); } - public PlatformBlockState getStoredRaw(int x, int y, int z) { + public NativeBlockState getStoredRaw(int x, int y, int z) { return super.getRaw(x, y, z); } @@ -89,7 +90,7 @@ public class ChunkDataHunkHolder extends AtomicHunk { int runStart = -1; for (int y = 0; y < height; y++) { - PlatformBlockState state = super.getRaw(x, y, z); + NativeBlockState state = super.getRaw(x, y, z); BlockData block = state == null ? null : (BlockData) state.nativeHandle(); // Custom wrappers are not real Bukkit data; write the vanilla base like the // NMS fast path (NMSBinding.applyChunkDataBlocks) does. diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkView.java b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkView.java index 2bd392e28..113459ebc 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkView.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkDataHunkView.java @@ -21,14 +21,14 @@ package art.arcane.iris.generation.chunk; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import art.arcane.iris.platform.bukkit.BukkitBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.volmlib.util.hunk.Hunk; import org.bukkit.block.data.BlockData; import org.bukkit.generator.ChunkGenerator.ChunkData; @SuppressWarnings("ClassCanBeRecord") -public class ChunkDataHunkView implements Hunk { +public class ChunkDataHunkView implements Hunk { private final art.arcane.volmlib.util.hunk.view.ChunkDataHunkView view; public ChunkDataHunkView(ChunkData chunk) { @@ -51,7 +51,7 @@ public class ChunkDataHunkView implements Hunk { } @Override - public void set(int x1, int y1, int z1, int x2, int y2, int z2, PlatformBlockState t) { + public void set(int x1, int y1, int z1, int x2, int y2, int z2, NativeBlockState t) { if (t == null) { return; } @@ -60,12 +60,12 @@ public class ChunkDataHunkView implements Hunk { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return BukkitBlockState.of(view.get(x, y, z)); } @Override - public void set(int x, int y, int z, PlatformBlockState t) { + public void set(int x, int y, int z, NativeBlockState t) { if (t == null) { return; } @@ -74,7 +74,7 @@ public class ChunkDataHunkView implements Hunk { } @Override - public void setRaw(int x, int y, int z, PlatformBlockState t) { + public void setRaw(int x, int y, int z, NativeBlockState t) { if (t == null) { return; } @@ -83,7 +83,7 @@ public class ChunkDataHunkView implements Hunk { } @Override - public PlatformBlockState getRaw(int x, int y, int z) { + public NativeBlockState getRaw(int x, int y, int z) { return BukkitBlockState.of(view.getRaw(x, y, z)); } } diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkHunkView.java b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkHunkView.java index b2b1a1a74..c9914514c 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/ChunkHunkView.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/ChunkHunkView.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.chunk; import art.arcane.iris.generation.runtime.BlockEditAccess; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.platform.bukkit.BukkitBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.bukkit.Chunk; import org.bukkit.World; @@ -29,7 +29,7 @@ import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @SuppressWarnings("ClassCanBeRecord") -public class ChunkHunkView extends art.arcane.volmlib.util.hunk.view.ChunkWorldHunkView implements Hunk { +public class ChunkHunkView extends art.arcane.volmlib.util.hunk.view.ChunkWorldHunkView implements Hunk { public ChunkHunkView(Chunk chunk) { super(chunk, chunk.getWorld().getMaxHeight(), diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/LinkedTerrainChunk.java b/core/src/main/java/art/arcane/iris/generation/chunk/LinkedTerrainChunk.java index 47764b4ee..c9809f866 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/LinkedTerrainChunk.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/LinkedTerrainChunk.java @@ -20,8 +20,8 @@ package art.arcane.iris.generation.chunk; import art.arcane.iris.platform.bukkit.BukkitBiome; import art.arcane.iris.platform.bukkit.BukkitBlockState; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import org.bukkit.Bukkit; import org.bukkit.World; @@ -50,14 +50,14 @@ public class LinkedTerrainChunk implements TerrainChunk { } @Override - public PlatformBiome getBiome(int x, int y, int z) { + public NativeBiome getBiome(int x, int y, int z) { int index = biomeIndex(x, y, z); Biome biome = biomes[index]; return BukkitBiome.of(biome == null ? Biome.PLAINS : biome); } @Override - public void setBiome(int x, int y, int z, PlatformBiome bio) { + public void setBiome(int x, int y, int z, NativeBiome bio) { biomes[biomeIndex(x, y, z)] = (Biome) bio.nativeHandle(); } @@ -65,7 +65,7 @@ public class LinkedTerrainChunk implements TerrainChunk { * Writes the whole vertical column in one pass, striding the flat biome array directly. Equivalent to * calling setBiome for every y in [minHeight, maxHeight). */ - public void fillBiomeColumn(int x, int z, PlatformBiome bio) { + public void fillBiomeColumn(int x, int z, NativeBiome bio) { Biome handle = (Biome) bio.nativeHandle(); int stride = CHUNK_SIZE * CHUNK_SIZE; int index = (z & (CHUNK_SIZE - 1)) * CHUNK_SIZE + (x & (CHUNK_SIZE - 1)); @@ -87,7 +87,7 @@ public class LinkedTerrainChunk implements TerrainChunk { } @Override - public synchronized void setBlock(int x, int y, int z, PlatformBlockState state) { + public synchronized void setBlock(int x, int y, int z, NativeBlockState state) { BlockData blockData = (BlockData) state.nativeHandle(); if (blockData instanceof IrisCustomData data) { blockData = data.getBase(); @@ -96,7 +96,7 @@ public class LinkedTerrainChunk implements TerrainChunk { } @Override - public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, PlatformBlockState state) { + public synchronized void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, NativeBlockState state) { BlockData blockData = (BlockData) state.nativeHandle(); if (blockData instanceof IrisCustomData data) { blockData = data.getBase(); @@ -105,7 +105,7 @@ public class LinkedTerrainChunk implements TerrainChunk { } @Override - public PlatformBlockState getBlockData(int x, int y, int z) { + public NativeBlockState getBlockData(int x, int y, int z) { return BukkitBlockState.of(rawChunkData.getBlockData(x, y, z)); } diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunk.java b/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunk.java index cbf52c6b5..4343ab973 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunk.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunk.java @@ -18,12 +18,13 @@ package art.arcane.iris.generation.chunk; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.bukkit.World; +import art.arcane.volmlib.nativelib.terrain.BukkitTerrainBuffer; import org.bukkit.generator.ChunkGenerator.ChunkData; -public interface TerrainChunk { +public interface TerrainChunk extends BukkitTerrainBuffer { static TerrainChunk create(World world) { return new LinkedTerrainChunk(world); } @@ -32,13 +33,4 @@ public interface TerrainChunk { return new LinkedTerrainChunk(raw); } - PlatformBiome getBiome(int x, int y, int z); - - void setBiome(int x, int y, int z, PlatformBiome bio); - int getMinHeight(); - int getMaxHeight(); - void setBlock(int x, int y, int z, PlatformBlockState blockData); - void setRegion(int xMin, int yMin, int zMin, int xMax, int yMax, int zMax, PlatformBlockState blockData); - PlatformBlockState getBlockData(int x, int y, int z); - ChunkData getChunkData(); } diff --git a/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunkBiomeHunkView.java b/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunkBiomeHunkView.java index f731c9b74..bb0ac6dff 100644 --- a/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunkBiomeHunkView.java +++ b/core/src/main/java/art/arcane/iris/generation/chunk/TerrainChunkBiomeHunkView.java @@ -18,12 +18,12 @@ package art.arcane.iris.generation.chunk; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.hunk.storage.StorageHunk; import art.arcane.volmlib.util.hunk.HunkMutationSupport; -public class TerrainChunkBiomeHunkView extends StorageHunk implements Hunk { +public class TerrainChunkBiomeHunkView extends StorageHunk implements Hunk { private final TerrainChunk chunk; public TerrainChunkBiomeHunkView(TerrainChunk chunk) { @@ -37,7 +37,7 @@ public class TerrainChunkBiomeHunkView extends StorageHunk implem * through to the generic element wise write. */ @Override - public void set(int x1, int y1, int z1, int x2, int y2, int z2, PlatformBiome biome) { + public void set(int x1, int y1, int z1, int x2, int y2, int z2, NativeBiome biome) { if (x1 == x2 && z1 == z2 && y1 == 0 && y2 == getHeight() - 1 && chunk instanceof LinkedTerrainChunk linked) { linked.fillBiomeColumn(x1, z1, biome); return; @@ -47,12 +47,12 @@ public class TerrainChunkBiomeHunkView extends StorageHunk implem } @Override - public void setRaw(int x, int y, int z, PlatformBiome biome) { + public void setRaw(int x, int y, int z, NativeBiome biome) { chunk.setBiome(x, y + chunk.getMinHeight(), z, biome); } @Override - public PlatformBiome getRaw(int x, int y, int z) { + public NativeBiome getRaw(int x, int y, int z) { return chunk.getBiome(x, y + chunk.getMinHeight(), z); } } diff --git a/core/src/main/java/art/arcane/iris/generation/context/ChunkContext.java b/core/src/main/java/art/arcane/iris/generation/context/ChunkContext.java index 6ab1d9da4..b092ffbdd 100644 --- a/core/src/main/java/art/arcane/iris/generation/context/ChunkContext.java +++ b/core/src/main/java/art/arcane/iris/generation/context/ChunkContext.java @@ -10,8 +10,8 @@ import art.arcane.iris.generation.biome.FloatingIslandBoundarySampler; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.world.history.FloatingBiomeOverlay; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.iris.generation.concurrent.MultiBurst; @@ -22,7 +22,7 @@ import java.util.concurrent.CompletableFuture; public class ChunkContext { private boolean naturalTerrain = true; private boolean speculativeTerrain; - private Hunk terrainBiomes; + private Hunk terrainBiomes; private FloatingBiomeOverlay floatingBiomes; private final int x; private final int z; @@ -32,8 +32,8 @@ public class ChunkContext { private final int[] roundedHeight; private final ChunkedDataCache biome; private final ChunkedDataCache cave; - private final ChunkedDataCache rock; - private final ChunkedDataCache fluid; + private final ChunkedDataCache rock; + private final ChunkedDataCache fluid; private final ChunkedDataCache region; private final FloatingIslandBoundarySampler floatingIslandBoundarySampler; private final DimensionStackContext dimensionStackContext; @@ -139,7 +139,7 @@ public class ChunkContext { return !MultiBurst.burst.ownsCurrentThread(); } - public void setTerrainBiomeOutput(Hunk terrainBiomes) { + public void setTerrainBiomeOutput(Hunk terrainBiomes) { this.terrainBiomes = terrainBiomes; } @@ -158,7 +158,7 @@ public class ChunkContext { return floatingBiomes; } - public void setNaturalBiome(int localX, int y, int localZ, PlatformBiome biome) { + public void setNaturalBiome(int localX, int y, int localZ, NativeBiome biome) { if (terrainBiomes != null) { terrainBiomes.setRaw(localX, y, localZ, biome); } @@ -227,11 +227,11 @@ public class ChunkContext { return cave; } - public ChunkedDataCache getRock() { + public ChunkedDataCache getRock() { return rock; } - public ChunkedDataCache getFluid() { + public ChunkedDataCache getFluid() { return fluid; } diff --git a/core/src/main/java/art/arcane/iris/generation/context/IrisContext.java b/core/src/main/java/art/arcane/iris/generation/context/IrisContext.java index 5ab70bf1c..2131b4609 100644 --- a/core/src/main/java/art/arcane/iris/generation/context/IrisContext.java +++ b/core/src/main/java/art/arcane/iris/generation/context/IrisContext.java @@ -18,6 +18,8 @@ package art.arcane.iris.generation.context; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.mantle.EngineMantle; @@ -97,7 +99,7 @@ public final class IrisContext { .qput("queued", mantle.getUnloadRegionCount())); } - public static final class Scope implements AutoCloseable { + public static final class Scope implements NativeGenerationScope { private final Thread owner; private final IrisContext previous; private final IrisContext installed; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorCore.java b/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorCore.java index 1b3e7a9c9..36eb76536 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorCore.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorCore.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; @@ -40,10 +40,10 @@ final class DecoratorCore { private static final String WEEPING_VINES_PLANT = "minecraft:weeping_vines_plant"; private static final String TWISTING_VINES = "minecraft:twisting_vines"; private static final String TWISTING_VINES_PLANT = "minecraft:twisting_vines_plant"; - private static volatile PlatformBlockState weepingVines; - private static volatile PlatformBlockState weepingVinesPlant; - private static volatile PlatformBlockState twistingVines; - private static volatile PlatformBlockState twistingVinesPlant; + private static volatile NativeBlockState weepingVines; + private static volatile NativeBlockState weepingVinesPlant; + private static volatile NativeBlockState twistingVines; + private static volatile NativeBlockState twistingVinesPlant; static final ThreadLocal SCRATCH_OPTS = ThreadLocal.withInitial(PlaceOpts::new); @@ -106,14 +106,14 @@ final class DecoratorCore { static void placeSurfaceSingle(IrisDecorator decorator, int x, int z, int realX, int height, int realZ, - Hunk data, RNG rng, IrisData irisData, + Hunk data, RNG rng, IrisData irisData, boolean underwater, boolean caveSkipFluid, EngineMantle mantle) { if (height < 0 || height >= data.getHeight()) { return; } - PlatformBlockState bdx = data.get(x, height, z); - PlatformBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); + NativeBlockState bdx = data.get(x, height, z); + NativeBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); if (!IrisSpeleothems.isSpike(bd) && !underwater && !canGoOn(bd, bdx) && !decorator.isForcePlace() && decorator.getForceBlock() == null) { @@ -156,8 +156,8 @@ final class DecoratorCore { } try { - PlatformBlockState upper = bd.withProperty("half", topHalfValue(half)); - PlatformBlockState lower = fixFacesForHunk( + NativeBlockState upper = bd.withProperty("half", topHalfValue(half)); + NativeBlockState lower = fixFacesForHunk( bd.withProperty("half", bottomHalfValue(half)), data, x, z, realX, lowerY, realZ, mantle); data.set(x, lowerY, z, lower); @@ -174,7 +174,7 @@ final class DecoratorCore { } } - private static boolean matchesPalette(PlatformBlockState[] palette, PlatformBlockState surface) { + private static boolean matchesPalette(NativeBlockState[] palette, NativeBlockState surface) { if (surface == null) { return false; } @@ -188,9 +188,9 @@ final class DecoratorCore { } static void placeSingleAt(IrisDecorator decorator, int x, int z, - int realX, int height, int realZ, Hunk data, + int realX, int height, int realZ, Hunk data, RNG rng, IrisData irisData, boolean applyFixFaces, EngineMantle mantle) { - PlatformBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); + NativeBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); if (bd == null) { return; } @@ -211,13 +211,13 @@ final class DecoratorCore { } static void placeStackUp(IrisDecorator decorator, int x, int z, int realX, int realZ, - int height, int max, Hunk data, + int height, int max, Hunk data, RNG rng, IrisData irisData, PlaceOpts opts) { if (height < 0 || height >= data.getHeight()) { return; } - PlatformBlockState support = data.get(x, height, z); + NativeBlockState support = data.get(x, height, z); if (!allowsSurface(decorator, support, irisData)) { return; } @@ -230,13 +230,13 @@ final class DecoratorCore { if (y >= data.getHeight()) { break; } - PlatformBlockState existing = data.get(x, y, z); + NativeBlockState existing = data.get(x, y, z); if (!canReplaceStackTarget(existing, opts.underwater) || (opts.caveSkipFluid && B.isFluid(existing))) { break; } double threshold = stack == 1 ? 1.0 : ((double) i) / (stack - 1); - PlatformBlockState block = threshold >= decorator.getTopThreshold() + NativeBlockState block = threshold >= decorator.getTopThreshold() ? decorator.pickBlockDataTop(rng, irisData, realX, realZ) : decorator.pickBlockData(rng, irisData, realX, realZ); if (block == null) { @@ -267,12 +267,12 @@ final class DecoratorCore { } static void placeStackDown(IrisDecorator decorator, int x, int z, int realX, int realZ, - int height, int minHeight, Hunk data, + int height, int minHeight, Hunk data, RNG rng, IrisData irisData, int max, PlaceOpts opts, EngineMantle mantle) { if (height < 0 || height >= data.getHeight()) { return; } - PlatformBlockState support = height + 1 < data.getHeight() ? data.get(x, height + 1, z) : null; + NativeBlockState support = height + 1 < data.getHeight() ? data.get(x, height + 1, z) : null; if (!allowsSurface(decorator, support, irisData)) { return; } @@ -284,13 +284,13 @@ final class DecoratorCore { if (y < 0 || y < minHeight) { break; } - PlatformBlockState existing = data.get(x, y, z); + NativeBlockState existing = data.get(x, y, z); if (!canReplaceStackTarget(existing, opts.underwater) || (opts.caveSkipFluid && B.isFluid(existing))) { break; } double threshold = stack == 1 ? 1.0 : ((double) i) / (stack - 1); - PlatformBlockState block = threshold >= decorator.getTopThreshold() + NativeBlockState block = threshold >= decorator.getTopThreshold() ? decorator.pickBlockDataTop(rng, irisData, realX, realZ) : decorator.pickBlockData(rng, irisData, realX, realZ); if (block == null) { @@ -321,9 +321,9 @@ final class DecoratorCore { static void placeFloatingSimple(IrisDecorator decorator, int xf, int zf, int realX, int realZ, - int height, int max, Hunk data, + int height, int max, Hunk data, RNG rng, IrisData irisData, EngineMantle mantle) { - PlatformBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); + NativeBlockState bd = decorator.pickBlockData(rng, irisData, realX, realZ); if (bd == null) { return; } @@ -348,8 +348,8 @@ final class DecoratorCore { } try { - PlatformBlockState upper = bd.withProperty("half", topHalfValue(half)); - PlatformBlockState lower = bd.withProperty("half", bottomHalfValue(half)); + NativeBlockState upper = bd.withProperty("half", topHalfValue(half)); + NativeBlockState lower = bd.withProperty("half", bottomHalfValue(half)); data.set(xf, lowerY, zf, lower); data.set(xf, upperY, zf, upper); } catch (Throwable e) { @@ -365,7 +365,7 @@ final class DecoratorCore { static int placeFloatingStacked(IrisDecorator decorator, int xf, int zf, int realX, int realZ, - int height, int max, Hunk data, + int height, int max, Hunk data, RNG rng, IrisData irisData, EngineMantle mantle) { int stack = decorator.getHeight(rng, realX, realZ, irisData); if (decorator.isScaleStack()) { @@ -382,7 +382,7 @@ final class DecoratorCore { break; } double threshold = stack == 1 ? 0.0 : ((double) i) / (stack - 1); - PlatformBlockState bd = threshold >= decorator.getTopThreshold() + NativeBlockState bd = threshold >= decorator.getTopThreshold() ? decorator.pickBlockDataTop(rng, irisData, realX, realZ) : decorator.pickBlockData(rng, irisData, realX, realZ); if (bd == null) { @@ -413,7 +413,7 @@ final class DecoratorCore { return placed; } - static PlatformBlockState fixFacesForHunk(PlatformBlockState b, Hunk hunk, int rX, int rZ, + static NativeBlockState fixFacesForHunk(NativeBlockState b, Hunk hunk, int rX, int rZ, int x, int y, int z, EngineMantle mantle) { if (!B.isVineBlock(b)) { return b; @@ -434,9 +434,9 @@ final class DecoratorCore { } int yy = y + f.getModY(); - PlatformBlockState rs = null; + NativeBlockState rs = null; if (mantle != null) { - rs = mantle.getMantle().get(x + f.getModX(), yy, z + f.getModZ(), PlatformBlockState.class); + rs = mantle.getMantle().get(x + f.getModX(), yy, z + f.getModZ(), NativeBlockState.class); } BlockData r = rs == null ? (BlockData) EngineMantle.AIR.get().nativeHandle() : (BlockData) rs.nativeHandle(); if (r.isFaceSturdy(f.getOppositeFace(), BlockSupport.FULL)) { @@ -470,31 +470,31 @@ final class DecoratorCore { return BukkitBlockState.of(cloned); } - static boolean canGoOn(PlatformBlockState decorator, PlatformBlockState surface) { + static boolean canGoOn(NativeBlockState decorator, NativeBlockState surface) { if (!B.canPlaceOnto(decorator, surface)) { return false; } return IrisSugarCane.isSugarCane(decorator) || IrisSpeleothems.isSturdy(surface, true); } - static boolean isValidShorelineSupport(IrisDecorator decorator, PlatformBlockState decorant, PlatformBlockState surface) { + static boolean isValidShorelineSupport(IrisDecorator decorator, NativeBlockState decorant, NativeBlockState surface) { return surface != null && B.isSolid(surface) && (decorator.isForcePlace() || canGoOn(decorant, surface)); } - static boolean canReplaceStackTarget(PlatformBlockState state, boolean allowFluid) { + static boolean canReplaceStackTarget(NativeBlockState state, boolean allowFluid) { return B.isAir(state) || allowFluid && B.isFluid(state); } - private static boolean canPlaceTwoBlockPlant(Hunk data, int x, int z, + private static boolean canPlaceTwoBlockPlant(Hunk data, int x, int z, int lowerY, int upperY, boolean caveSkipFluid) { if (lowerY < 0 || upperY >= data.getHeight()) { return false; } - PlatformBlockState lower = data.get(x, lowerY, z); - PlatformBlockState upper = data.get(x, upperY, z); + NativeBlockState lower = data.get(x, lowerY, z); + NativeBlockState upper = data.get(x, upperY, z); return B.isAir(lower) && B.isAir(upper) && (!caveSkipFluid || !B.isFluid(lower) && !B.isFluid(upper)); } @@ -510,13 +510,13 @@ final class DecoratorCore { return stack; } - private static boolean allowsSurface(IrisDecorator decorator, PlatformBlockState surface, IrisData data) { + private static boolean allowsSurface(IrisDecorator decorator, NativeBlockState surface, IrisData data) { return decorator.isForcePlace() || (decorator.getWhitelist() == null || matchesPalette(decorator.getWhitelistArray(data), surface)) && (decorator.getBlacklist() == null || !matchesPalette(decorator.getBlacklistArray(data), surface)); } - private static void placeSingleSpike(PlatformBlockState spike, Hunk data, + private static void placeSingleSpike(NativeBlockState spike, Hunk data, int x, int z, int y, boolean upward, boolean allowWater) { if (!IrisSpeleothems.canPlace(spike, data, x, z, y, upward, allowWater)) { return; @@ -525,15 +525,15 @@ final class DecoratorCore { IrisSpeleothems.finishColumn(data, x, z, y, 1, upward); } - private static void finishVineTip(Hunk data, int x, int z, int y) { - PlatformBlockState state = data.get(x, y, z); - PlatformBlockState tip = stackedVineBlock(state, 1, 0); + private static void finishVineTip(Hunk data, int x, int z, int y) { + NativeBlockState state = data.get(x, y, z); + NativeBlockState tip = stackedVineBlock(state, 1, 0); if (tip != state) { data.set(x, y, z, tip); } } - static String stackedVineKey(PlatformBlockState state, int stack, int index) { + static String stackedVineKey(NativeBlockState state, int stack, int index) { String material = IrisProceduralBlocks.materialKey(state); boolean tip = index == stack - 1; return switch (material) { @@ -543,7 +543,7 @@ final class DecoratorCore { }; } - private static PlatformBlockState stackedVineBlock(PlatformBlockState state, int stack, int index) { + private static NativeBlockState stackedVineBlock(NativeBlockState state, int stack, int index) { String key = stackedVineKey(state, stack, index); if (key == null) { return state; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorPlatformHooks.java b/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorPlatformHooks.java index 4b71c0e57..3dc97e42c 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorPlatformHooks.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/DecoratorPlatformHooks.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.decoration; import art.arcane.iris.generation.mantle.EngineMantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; public final class DecoratorPlatformHooks { @@ -30,11 +30,11 @@ public final class DecoratorPlatformHooks { } public interface FaceFixer { - PlatformBlockState fixFaces(PlatformBlockState state, Hunk hunk, int rX, int rZ, int x, int y, int z, EngineMantle mantle); + NativeBlockState fixFaces(NativeBlockState state, Hunk hunk, int rX, int rZ, int x, int y, int z, EngineMantle mantle); } public interface SurfaceSturdiness { - boolean canGoOn(PlatformBlockState surface, boolean upward); + boolean canGoOn(NativeBlockState surface, boolean upward); } public static synchronized Bindings bind(FaceFixer faceFixer, SurfaceSturdiness surfaceSturdiness) { diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/FloatingDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/FloatingDecorator.java index ac9d5a647..70aa470fc 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/FloatingDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/FloatingDecorator.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; /* * Floating island decoration path. Bypasses all canGoOn, slope, whitelist, and blacklist @@ -33,7 +33,7 @@ public class FloatingDecorator { public static int decorateColumn(Engine engine, IrisBiome target, IrisDecorationPart part, int xf, int zf, int realX, int realZ, - int height, int max, Hunk data, RNG rng, + int height, int max, Hunk data, RNG rng, Runnable candidatesNullCallback) { RNG gRNG = new RNG(DecoratorCore.partSeed(engine.getSeedManager().getDecorator(), part)); IrisDecorator decorator = DecoratorCore.pickDecorator(target, part, gRNG, rng, engine.getData(), realX, realZ); diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisCeilingDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisCeilingDecorator.java index 3fee5203c..4b8e176e8 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisCeilingDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisCeilingDecorator.java @@ -25,7 +25,7 @@ import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public class IrisCeilingDecorator extends IrisEngineDecorator { private final RNG partRNG; @@ -38,13 +38,13 @@ public class IrisCeilingDecorator extends IrisEngineDecorator { @BlockCoordinates @Override public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, int height, int max) { + Hunk data, IrisBiome biome, int height, int max) { decorate(x, z, realX, realX1, realX_1, realZ, realZ1, realZ_1, data, biome, biome.getInferredType(), height, max); } @BlockCoordinates public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, InferredType inferredType, int height, int max) { + Hunk data, IrisBiome biome, InferredType inferredType, int height, int max) { boolean caveSkipFluid = IrisSurfaceDecorator.skipsFluid(inferredType); RNG rng = getRNG(realX, realZ); IrisDecorator decorator = DecoratorCore.pickDecorator(biome, getPart(), partRNG, rng, getData(), realX, realZ); @@ -55,7 +55,7 @@ public class IrisCeilingDecorator extends IrisEngineDecorator { if (!decorator.isStacking()) { if (caveSkipFluid) { - PlatformBlockState state = data.get(x, height, z); + NativeBlockState state = data.get(x, height, z); if (B.isFluid(state)) { return; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDecorator.java index a58c2fae6..3cfdfac5a 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDecorator.java @@ -25,7 +25,7 @@ import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.iris.pack.schema.annotation.DependsOn; import art.arcane.volmlib.util.documentation.Description; @@ -40,7 +40,7 @@ import art.arcane.volmlib.util.noise.CNG; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; @Snippet("decorator") @@ -53,12 +53,12 @@ public class IrisDecorator { private final transient AtomicCache layerGenerator = new AtomicCache<>(); private final transient AtomicCache varianceGenerator = new AtomicCache<>(); private final transient AtomicCache heightGenerator = new AtomicCache<>(); - private final transient AtomicCache> blockData = new AtomicCache<>(); - private final transient AtomicCache> blockDataTops = new AtomicCache<>(); - private final transient AtomicCache blockDataArray = new AtomicCache<>(); - private final transient AtomicCache blockDataTopsArray = new AtomicCache<>(); - private final transient AtomicCache whitelistArray = new AtomicCache<>(); - private final transient AtomicCache blacklistArray = new AtomicCache<>(); + private final transient AtomicCache> blockData = new AtomicCache<>(); + private final transient AtomicCache> blockDataTops = new AtomicCache<>(); + private final transient AtomicCache blockDataArray = new AtomicCache<>(); + private final transient AtomicCache blockDataTopsArray = new AtomicCache<>(); + private final transient AtomicCache whitelistArray = new AtomicCache<>(); + private final transient AtomicCache blacklistArray = new AtomicCache<>(); @Description("The varience dispersion is used when multiple blocks are put in the palette. Scatter scrambles them, Wispy shows streak-looking varience") private IrisGeneratorStyle variance = NoiseStyle.STATIC.style(); @Description("Forcefully place this decorant anywhere it is supposed to go even if it should not go on a specific surface block. For example, you could force tallgrass to place on top of stone by using this.") @@ -176,7 +176,7 @@ public class IrisDecorator { return getGenerator(rng, data).fitDouble(0D, 1D, xx, zz) <= chance; } - public PlatformBlockState getBlockData(IrisBiome b, RNG rng, double x, double z, IrisData data) { + public NativeBlockState getBlockData(IrisBiome b, RNG rng, double x, double z, IrisData data) { if (!passesChanceGate(rng, x, z, data)) { return null; } @@ -186,7 +186,7 @@ public class IrisDecorator { return getVarianceGenerator(rng, data).fit(getBlockData(data), z, x); } - public PlatformBlockState getBlockData100(IrisBiome b, RNG rng, double x, double y, double z, IrisData data) { + public NativeBlockState getBlockData100(IrisBiome b, RNG rng, double x, double y, double z, IrisData data) { if (getBlockData(data).isEmpty()) { IrisLogging.warnOnce("decorator-empty:" + b.getName(), "Empty Block Data for " + b.getName()); return null; @@ -199,7 +199,7 @@ public class IrisDecorator { return getVarianceGenerator(rng, data).fit(getBlockData(data), z, y, x); //X and Z must be switched } - public PlatformBlockState getBlockDataForTop(IrisBiome b, RNG rng, double x, double y, double z, IrisData data) { + public NativeBlockState getBlockDataForTop(IrisBiome b, RNG rng, double x, double y, double z, IrisData data) { if (getBlockDataTops(data).isEmpty()) { return getBlockData100(b, rng, x, y, z, data); } @@ -218,8 +218,8 @@ public class IrisDecorator { return null; } - public KList getBlockData(IrisData data) { - KList cached = blockData.getIfPresent(); + public KList getBlockData(IrisData data) { + KList cached = blockData.getIfPresent(); if (cached != null) { return cached; @@ -227,9 +227,9 @@ public class IrisDecorator { return blockData.aquire(() -> { - KList blockData = new KList<>(); + KList blockData = new KList<>(); for (IrisBlockData i : palette) { - PlatformBlockState bx = i.getBlockData(data); + NativeBlockState bx = i.getBlockData(data); if (bx != null) { for (int n = 0; n < i.getWeight(); n++) { blockData.add(bx); @@ -241,8 +241,8 @@ public class IrisDecorator { }); } - public KList getBlockDataTops(IrisData data) { - KList cached = blockDataTops.getIfPresent(); + public KList getBlockDataTops(IrisData data) { + KList cached = blockDataTops.getIfPresent(); if (cached != null) { return cached; @@ -250,9 +250,9 @@ public class IrisDecorator { return blockDataTops.aquire(() -> { - KList blockDataTops = new KList<>(); + KList blockDataTops = new KList<>(); for (IrisBlockData i : topPalette) { - PlatformBlockState bx = i.getBlockData(data); + NativeBlockState bx = i.getBlockData(data); if (bx != null) { for (int n = 0; n < i.getWeight(); n++) { blockDataTops.add(bx); @@ -264,29 +264,29 @@ public class IrisDecorator { }); } - public PlatformBlockState[] getBlockDataArray(IrisData data) { - PlatformBlockState[] cached = blockDataArray.getIfPresent(); + public NativeBlockState[] getBlockDataArray(IrisData data) { + NativeBlockState[] cached = blockDataArray.getIfPresent(); if (cached != null) { return cached; } return blockDataArray.aquire(() -> { - KList list = getBlockData(data); - return list.toArray(new PlatformBlockState[0]); + KList list = getBlockData(data); + return list.toArray(new NativeBlockState[0]); }); } - public PlatformBlockState[] getBlockDataTopsArray(IrisData data) { - PlatformBlockState[] cached = blockDataTopsArray.getIfPresent(); + public NativeBlockState[] getBlockDataTopsArray(IrisData data) { + NativeBlockState[] cached = blockDataTopsArray.getIfPresent(); if (cached != null) { return cached; } return blockDataTopsArray.aquire(() -> { - KList list = getBlockDataTops(data); - return list.toArray(new PlatformBlockState[0]); + KList list = getBlockDataTops(data); + return list.toArray(new NativeBlockState[0]); }); } @@ -294,8 +294,8 @@ public class IrisDecorator { * The resolved whitelist palette. Empty when no whitelist is configured; callers must still gate on * {@link #getWhitelist()} being non null, because an explicitly empty whitelist blocks all placement. */ - public PlatformBlockState[] getWhitelistArray(IrisData data) { - PlatformBlockState[] cached = whitelistArray.getIfPresent(); + public NativeBlockState[] getWhitelistArray(IrisData data) { + NativeBlockState[] cached = whitelistArray.getIfPresent(); if (cached != null) { return cached; @@ -304,8 +304,8 @@ public class IrisDecorator { return whitelistArray.aquire(() -> resolvePalette(whitelist, data)); } - public PlatformBlockState[] getBlacklistArray(IrisData data) { - PlatformBlockState[] cached = blacklistArray.getIfPresent(); + public NativeBlockState[] getBlacklistArray(IrisData data) { + NativeBlockState[] cached = blacklistArray.getIfPresent(); if (cached != null) { return cached; @@ -314,12 +314,12 @@ public class IrisDecorator { return blacklistArray.aquire(() -> resolvePalette(blacklist, data)); } - private static PlatformBlockState[] resolvePalette(KList list, IrisData data) { + private static NativeBlockState[] resolvePalette(KList list, IrisData data) { if (list == null) { - return new PlatformBlockState[0]; + return new NativeBlockState[0]; } - PlatformBlockState[] resolved = new PlatformBlockState[list.size()]; + NativeBlockState[] resolved = new NativeBlockState[list.size()]; for (int i = 0; i < resolved.length; i++) { resolved[i] = list.get(i).getBlockData(data); @@ -328,8 +328,8 @@ public class IrisDecorator { return resolved; } - public PlatformBlockState pickBlockData(RNG rng, IrisData data, double x, double z) { - PlatformBlockState[] arr = getBlockDataArray(data); + public NativeBlockState pickBlockData(RNG rng, IrisData data, double x, double z) { + NativeBlockState[] arr = getBlockDataArray(data); if (arr.length == 0) { return null; } @@ -339,8 +339,8 @@ public class IrisDecorator { return arr[Math.abs((int) getVarianceGenerator(rng, data).fit(0, arr.length - 1, z, x))]; } - public PlatformBlockState pickBlockDataTop(RNG rng, IrisData data, double x, double z) { - PlatformBlockState[] arr = getBlockDataTopsArray(data); + public NativeBlockState pickBlockDataTop(RNG rng, IrisData data, double x, double z) { + NativeBlockState[] arr = getBlockDataTopsArray(data); if (arr.length == 0) { return pickBlockData(rng, data, x, z); } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositGenerator.java index c51d4c7cf..bb73646a6 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositGenerator.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; @@ -31,7 +31,7 @@ import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; @@ -57,7 +57,7 @@ public class IrisDepositGenerator { private static final long FNV_PRIME = 0x100000001b3L; private final transient ConcurrentMap> objects = new ConcurrentHashMap<>(); - private final transient AtomicCache> blockData = new AtomicCache<>(); + private final transient AtomicCache> blockData = new AtomicCache<>(); private final transient AtomicCache ore = new AtomicCache<>(); private final transient AtomicCache> replaceableBlockData = new AtomicCache<>(); private final transient AtomicCache> surfaceReplaceableBlockData = new AtomicCache<>(); @@ -210,9 +210,9 @@ public class IrisDepositGenerator { hash = mix(hash, Double.doubleToLongBits(spawnChance)); hash = mix(hash, Double.doubleToLongBits(perClumpSpawnChance)); hash = mix(hash, Double.doubleToLongBits(discardChanceOnAirExposure)); - KList resolvedPalette = getBlockData(rdata); + KList resolvedPalette = getBlockData(rdata); hash = mix(hash, resolvedPalette.size()); - for (PlatformBlockState block : resolvedPalette) { + for (NativeBlockState block : resolvedPalette) { hash = mixString(hash, block == null ? null : block.key()); } hash = mix(hash, varience); @@ -443,17 +443,17 @@ public class IrisDepositGenerator { return o; } - private PlatformBlockState nextBlock(RNG rngv, IrisData rdata) { + private NativeBlockState nextBlock(RNG rngv, IrisData rdata) { return getBlockData(rdata).get(rngv.i(0, getBlockData(rdata).size())); } - public KList getBlockData(IrisData rdata) { + public KList getBlockData(IrisData rdata) { return blockData.aquire(() -> { - KList blockData = new KList<>(); + KList blockData = new KList<>(); for (IrisBlockData ix : palette) { - PlatformBlockState bx = ix.getBlockData(rdata); + NativeBlockState bx = ix.getBlockData(rdata); if (bx != null) { blockData.add(bx); @@ -466,7 +466,7 @@ public class IrisDepositGenerator { public boolean isOre(IrisData rdata) { return ore.aquire(() -> { - for (PlatformBlockState block : getBlockData(rdata)) { + for (NativeBlockState block : getBlockData(rdata)) { if (block.isOre()) { return true; } @@ -476,7 +476,7 @@ public class IrisDepositGenerator { }); } - public boolean canReplace(PlatformBlockState state) { + public boolean canReplace(NativeBlockState state) { if (replaceableBlocks == null || replaceableBlocks.isEmpty()) { return true; } @@ -492,7 +492,7 @@ public class IrisDepositGenerator { return this; } - public boolean canReplaceSurface(PlatformBlockState state) { + public boolean canReplaceSurface(NativeBlockState state) { if (surfaceReplaceableBlocks == null || surfaceReplaceableBlocks.isEmpty()) { return true; } @@ -507,7 +507,7 @@ public class IrisDepositGenerator { return surfaceReplaceableBlocks != null && !surfaceReplaceableBlocks.isEmpty(); } - public boolean canReplaceSurface(PlatformBlockState state, IrisBiome surfaceBiome) { + public boolean canReplaceSurface(NativeBlockState state, IrisBiome surfaceBiome) { if (surfaceBiome != null && surfaceBiome.hasSurfaceOreReplaceableBlocks()) { return surfaceBiome.canReplaceSurfaceOre(state); } @@ -539,7 +539,7 @@ public class IrisDepositGenerator { private KSet resolveBlockKeys(KList keys) { KSet resolved = new KSet<>(); for (String key : keys) { - PlatformBlockState state = B.getStateOrNull(key, false); + NativeBlockState state = B.getStateOrNull(key, false); if (state != null) { resolved.add(IrisProceduralBlocks.materialKey(state)); } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositVariant.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositVariant.java index 5845b49f9..1eeddd002 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositVariant.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisDepositVariant.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.decoration; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -29,7 +29,7 @@ import art.arcane.volmlib.util.collection.KMap; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import lombok.experimental.Accessors; @@ -40,7 +40,7 @@ import lombok.experimental.Accessors; @Description("Remaps ore block ids to alternate block ids within a vertical band. Ores declared at dimension, region, and biome scope can be rewritten at placement time (for example, iron_ore -> deepslate_iron_ore inside a deep carving band, or yourmod:iron -> yourmod:moon_iron inside a lunar biome).") @Data public class IrisDepositVariant { - private final transient AtomicCache> resolved = new AtomicCache<>(); + private final transient AtomicCache> resolved = new AtomicCache<>(); @Required @MinNumber(-2048) @@ -58,21 +58,21 @@ public class IrisDepositVariant { @Description("Source block id (for example `minecraft:iron_ore`) -> replacement block id (for example `minecraft:deepslate_iron_ore`). Any block id the data loader resolves is accepted, including external/mod blocks. Source match is by material only, so block properties on the source key are ignored.") private KMap remap = new KMap<>(); - public PlatformBlockState remapOrNull(PlatformBlockState ore, IrisData rdata) { + public NativeBlockState remapOrNull(NativeBlockState ore, IrisData rdata) { if (ore == null || remap == null || remap.isEmpty()) { return null; } - KMap map = resolved.aquire(() -> buildResolved(rdata)); + KMap map = resolved.aquire(() -> buildResolved(rdata)); return map.get(IrisProceduralBlocks.materialKey(ore)); } - private KMap buildResolved(IrisData rdata) { - KMap out = new KMap<>(); + private KMap buildResolved(IrisData rdata) { + KMap out = new KMap<>(); for (java.util.Map.Entry entry : remap.entrySet()) { - PlatformBlockState source = B.getStateOrNull(entry.getKey(), false); - PlatformBlockState target = B.getStateOrNull(entry.getValue(), true); + NativeBlockState source = B.getStateOrNull(entry.getKey(), false); + NativeBlockState target = B.getStateOrNull(entry.getValue(), true); if (source == null || target == null) { continue; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisOreGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisOreGenerator.java index fbd112510..aeacc660b 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisOreGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisOreGenerator.java @@ -24,7 +24,7 @@ import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.pack.value.IrisRange; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -33,7 +33,7 @@ import art.arcane.volmlib.util.noise.CNG; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; @Accessors(chain = true) @@ -62,7 +62,7 @@ public class IrisOreGenerator { palette.getLayerGenerator(rng, data); } - public PlatformBlockState generate(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generate(int x, int y, int z, RNG rng, IrisData data) { if (threshold <= 0 || !range.contains(y)) { return null; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisProceduralBlocks.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisProceduralBlocks.java index b7b84049a..4159143f1 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisProceduralBlocks.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisProceduralBlocks.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; @@ -57,7 +57,7 @@ public final class IrisProceduralBlocks { return palette != null && palette.getPalette() != null && !palette.getPalette().isEmpty(); } - public static PlatformBlockState resolve(String block, IrisMaterialPalette palette, IrisData data, int x, int y, int z, RNG paletteRng) { + public static NativeBlockState resolve(String block, IrisMaterialPalette palette, IrisData data, int x, int y, int z, RNG paletteRng) { if (paletteSet(palette)) { return palette.get(paletteRng, x, y, z, data); } @@ -67,7 +67,7 @@ public final class IrisProceduralBlocks { return null; } - public static boolean hasProperty(PlatformBlockState state, String property) { + public static boolean hasProperty(NativeBlockState state, String property) { String key = state.key(); int start = propertyStart(key, property); if (start < 0) { @@ -78,7 +78,7 @@ public final class IrisProceduralBlocks { } /** Blocks that fall when unsupported: sand, gravel and concrete powder. */ - public static boolean isGravityAffected(PlatformBlockState state) { + public static boolean isGravityAffected(NativeBlockState state) { if (state == null) { return false; } @@ -91,7 +91,7 @@ public final class IrisProceduralBlocks { || key.endsWith("_concrete_powder"); } - public static String materialKey(PlatformBlockState state) { + public static String materialKey(NativeBlockState state) { String memoized = state.materialKey(); if (memoized != null) { return memoized; @@ -101,7 +101,7 @@ public final class IrisProceduralBlocks { return bracket < 0 ? key : key.substring(0, bracket); } - public static String propertyValue(PlatformBlockState state, String property) { + public static String propertyValue(NativeBlockState state, String property) { String key = state.key(); int start = propertyStart(key, property); if (start < 0) { @@ -142,7 +142,7 @@ public final class IrisProceduralBlocks { return -1; } - public static IrisObject assemble(Map blocks) { + public static IrisObject assemble(Map blocks) { if (blocks == null || blocks.isEmpty()) { return null; } @@ -170,7 +170,7 @@ public final class IrisProceduralBlocks { int cz = d / 2; IrisObject object = new IrisObject(w, h, d); - for (Map.Entry entry : blocks.entrySet()) { + for (Map.Entry entry : blocks.entrySet()) { Vector3i v = entry.getKey(); int nx = v.getBlockX() - minX - cx; int ny = v.getBlockY() - cy + 1; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaFloorDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaFloorDecorator.java index e974bc75c..2d8b98482 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaFloorDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaFloorDecorator.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public class IrisSeaFloorDecorator extends IrisEngineDecorator { private final RNG partRNG; @@ -36,7 +36,7 @@ public class IrisSeaFloorDecorator extends IrisEngineDecorator { @BlockCoordinates @Override public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, int height, int max) { + Hunk data, IrisBiome biome, int height, int max) { int limit = Math.min(max, Math.min(getEngine().getHeight(), data.getHeight())); if (height < 0 || height >= limit) { return; @@ -53,7 +53,7 @@ public class IrisSeaFloorDecorator extends IrisEngineDecorator { && !decorator.getSlopeCondition().isValid(getComplex().getSlopeStream().get(realX, realZ))) { return; } - PlatformBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); + NativeBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); if (block != null) { data.set(x, height, z, block); } @@ -65,7 +65,7 @@ public class IrisSeaFloorDecorator extends IrisEngineDecorator { for (int i = 0; i < stack; i++) { int h = height + i; double threshold = stack == 1 ? 1.0 : ((double) i) / (stack - 1); - PlatformBlockState block = threshold >= decorator.getTopThreshold() + NativeBlockState block = threshold >= decorator.getTopThreshold() ? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) : decorator.getBlockData100(biome, rng, realX, h, realZ, getData()); if (block == null) { diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaSurfaceDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaSurfaceDecorator.java index 2a08450ed..1687638bc 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaSurfaceDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSeaSurfaceDecorator.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public class IrisSeaSurfaceDecorator extends IrisEngineDecorator { private final RNG partRNG; @@ -36,7 +36,7 @@ public class IrisSeaSurfaceDecorator extends IrisEngineDecorator { @BlockCoordinates @Override public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, int height, int max) { + Hunk data, IrisBiome biome, int height, int max) { int maxBaseY = Math.min(max, Math.min(getEngine().getHeight(), data.getHeight()) - 1); if (height < 0 || height >= maxBaseY) { return; @@ -51,7 +51,7 @@ public class IrisSeaSurfaceDecorator extends IrisEngineDecorator { if (!decorator.isStacking()) { int targetY = height + 1; if (DecoratorCore.canReplaceStackTarget(data.get(x, targetY, z), false)) { - PlatformBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); + NativeBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); if (block != null) { data.set(x, targetY, z, block); } @@ -68,7 +68,7 @@ public class IrisSeaSurfaceDecorator extends IrisEngineDecorator { break; } double threshold = stack == 1 ? 1.0 : ((double) i) / (stack - 1); - PlatformBlockState block = threshold >= decorator.getTopThreshold() + NativeBlockState block = threshold >= decorator.getTopThreshold() ? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) : decorator.getBlockData100(biome, rng, realX, h, realZ, getData()); if (block == null) { diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisShoreLineDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisShoreLineDecorator.java index 72e9b6828..918c66c00 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisShoreLineDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisShoreLineDecorator.java @@ -24,7 +24,7 @@ import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public class IrisShoreLineDecorator extends IrisEngineDecorator { private final RNG partRNG; @@ -37,7 +37,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { @BlockCoordinates @Override public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, int height, int max) { + Hunk data, IrisBiome biome, int height, int max) { double localFluidHeight = getEngine().getMantle().getFluidHeight(realX, realZ); if (height != Math.round(localFluidHeight)) { return; @@ -59,7 +59,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { int z, int realX, int realZ, - Hunk data, + Hunk data, IrisBiome biome, int height, int max @@ -72,7 +72,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { int z, int realX, int realZ, - Hunk data, + Hunk data, IrisBiome biome, int height, int max @@ -89,7 +89,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { return; } - PlatformBlockState support = data.get(x, height, z); + NativeBlockState support = data.get(x, height, z); if (support == null || !support.isSolid()) { return; } @@ -102,7 +102,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { || !DecoratorCore.canReplaceStackTarget(data.get(x, targetY, z), false)) { return; } - PlatformBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); + NativeBlockState block = decorator.getBlockData100(biome, rng, realX, height, realZ, getData()); if (block != null && DecoratorCore.isValidShorelineSupport(decorator, block, support) && IrisSugarCane.canPlace(block, data, x, targetY, z, realX, realZ, getEngine())) { data.set(x, targetY, z, block); @@ -124,7 +124,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { return; } - PlatformBlockState block = decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()); + NativeBlockState block = decorator.getBlockDataForTop(biome, rng, realX, height, realZ, getData()); if (block != null && DecoratorCore.isValidShorelineSupport(decorator, block, support) && IrisSugarCane.canPlace(block, data, x, targetY, z, realX, realZ, getEngine())) { data.set(x, targetY, z, block); @@ -141,7 +141,7 @@ public class IrisShoreLineDecorator extends IrisEngineDecorator { break; } double threshold = ((double) i) / (stack - 1); - PlatformBlockState block = threshold >= decorator.getTopThreshold() + NativeBlockState block = threshold >= decorator.getTopThreshold() ? decorator.getBlockDataForTop(biome, rng, realX, h, realZ, getData()) : decorator.getBlockData100(biome, rng, realX, h, realZ, getData()); if (block == null) { diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSpeleothems.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSpeleothems.java index 465da6f87..63b74ddc4 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSpeleothems.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSpeleothems.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.decoration; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.hunk.Hunk; import org.bukkit.block.BlockFace; @@ -11,12 +11,12 @@ import java.util.concurrent.ConcurrentHashMap; public final class IrisSpeleothems { private static final String[] SPIKE_THICKNESSES = {"tip", "frustum", "base", "middle", "tip_merge"}; - private static final ConcurrentHashMap SPIKE_STATES = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap SPIKE_STATES = new ConcurrentHashMap<>(); private IrisSpeleothems() { } - public static boolean isSpike(PlatformBlockState state) { + public static boolean isSpike(NativeBlockState state) { if (state == null || state.key() == null) { return false; } @@ -24,29 +24,29 @@ public final class IrisSpeleothems { return material.equals("minecraft:pointed_dripstone") || material.equals("minecraft:sulfur_spike"); } - public static boolean isSupported(PlatformBlockState state, Hunk data, int x, int z, int y) { + public static boolean isSupported(NativeBlockState state, Hunk data, int x, int z, int y) { return isSupported(state, data, x, z, y, "up".equals(IrisProceduralBlocks.propertyValue(state, "vertical_direction"))); } - public static void finishAtTip(Hunk data, int x, int z, int y) { - PlatformBlockState state = data.get(x, y, z); + public static void finishAtTip(Hunk data, int x, int z, int y) { + NativeBlockState state = data.get(x, y, z); if (!isSpike(state)) { return; } boolean upward = "up".equals(IrisProceduralBlocks.propertyValue(state, "vertical_direction")); int frontY = y + (upward ? 1 : -1); - PlatformBlockState front = frontY >= 0 && frontY < data.getHeight() ? data.get(x, frontY, z) : null; + NativeBlockState front = frontY >= 0 && frontY < data.getHeight() ? data.get(x, frontY, z) : null; if (!sameSpike(state, front, upward)) { finishColumn(data, x, z, y, 1, upward); } } - public static void finishColumn(Hunk data, int x, int z, int start, int placed, boolean upward) { + public static void finishColumn(Hunk data, int x, int z, int start, int placed, boolean upward) { int step = upward ? 1 : -1; for (int i = placed - 1; i >= 0; i--) { int y = start + step * i; - PlatformBlockState state = data.get(x, y, z); + NativeBlockState state = data.get(x, y, z); if (isSpike(state)) { finishSpike(data, x, z, y, state, upward); } @@ -54,12 +54,12 @@ public final class IrisSpeleothems { if (placed == 0) { return; } - PlatformBlockState state = data.get(x, start, z); + NativeBlockState state = data.get(x, start, z); if (!isSpike(state)) { return; } for (int y = start - step; y >= 0 && y < data.getHeight(); y -= step) { - PlatformBlockState behind = data.get(x, y, z); + NativeBlockState behind = data.get(x, y, z); if (!sameSpike(state, behind, upward)) { break; } @@ -67,27 +67,27 @@ public final class IrisSpeleothems { } } - static boolean canPlace(PlatformBlockState spike, Hunk data, + static boolean canPlace(NativeBlockState spike, Hunk data, int x, int z, int y, boolean upward, boolean allowWater) { int supportY = y + (upward ? -1 : 1); if (y < 0 || y >= data.getHeight() || supportY < 0 || supportY >= data.getHeight()) { return false; } - PlatformBlockState existing = data.get(x, y, z); + NativeBlockState existing = data.get(x, y, z); if (!B.isAir(existing) && !(allowWater && existing != null && existing.isWater())) { return false; } return isSupported(spike, data, x, z, y, upward); } - static PlatformBlockState orient(PlatformBlockState state, PlatformBlockState existing, boolean upward) { + static NativeBlockState orient(NativeBlockState state, NativeBlockState existing, boolean upward) { if (existing != null && existing.isWater() && !state.isWaterLogged()) { state = state.withProperty("waterlogged", "true"); } return spikeBlock(state, upward, 0); } - static boolean isSturdy(PlatformBlockState surface, boolean upward) { + static boolean isSturdy(NativeBlockState surface, boolean upward) { if (surface == null || B.isAir(surface) || B.isFluid(surface)) { return false; } @@ -98,20 +98,20 @@ public final class IrisSpeleothems { return ((BlockData) surface.nativeHandle()).isFaceSturdy(upward ? BlockFace.UP : BlockFace.DOWN, BlockSupport.FULL); } - private static boolean isSupported(PlatformBlockState state, Hunk data, + private static boolean isSupported(NativeBlockState state, Hunk data, int x, int z, int y, boolean upward) { int supportY = y + (upward ? -1 : 1); if (supportY < 0 || supportY >= data.getHeight()) { return false; } - PlatformBlockState support = data.get(x, supportY, z); + NativeBlockState support = data.get(x, supportY, z); return sameSpike(state, support, upward) || isSturdy(support, upward); } - private static PlatformBlockState[][] buildSpikeStates(PlatformBlockState state) { - PlatformBlockState[][] states = new PlatformBlockState[2][SPIKE_THICKNESSES.length]; + private static NativeBlockState[][] buildSpikeStates(NativeBlockState state) { + NativeBlockState[][] states = new NativeBlockState[2][SPIKE_THICKNESSES.length]; for (int direction = 0; direction < states.length; direction++) { - PlatformBlockState directed = state.withProperty("vertical_direction", direction == 0 ? "up" : "down"); + NativeBlockState directed = state.withProperty("vertical_direction", direction == 0 ? "up" : "down"); for (int thickness = 0; thickness < SPIKE_THICKNESSES.length; thickness++) { states[direction][thickness] = directed.withProperty("thickness", SPIKE_THICKNESSES[thickness]); } @@ -119,22 +119,22 @@ public final class IrisSpeleothems { return states; } - private static boolean sameSpike(PlatformBlockState state, PlatformBlockState other, boolean upward) { + private static boolean sameSpike(NativeBlockState state, NativeBlockState other, boolean upward) { return isSpike(other) && IrisProceduralBlocks.materialKey(state).equals(IrisProceduralBlocks.materialKey(other)) && (upward ? "up" : "down").equals(IrisProceduralBlocks.propertyValue(other, "vertical_direction")); } - private static PlatformBlockState spikeBlock(PlatformBlockState state, boolean upward, int thickness) { + private static NativeBlockState spikeBlock(NativeBlockState state, boolean upward, int thickness) { return SPIKE_STATES.computeIfAbsent(state, IrisSpeleothems::buildSpikeStates)[upward ? 0 : 1][thickness]; } - private static void finishSpike(Hunk data, int x, int z, int y, PlatformBlockState state, boolean upward) { + private static void finishSpike(Hunk data, int x, int z, int y, NativeBlockState state, boolean upward) { int step = upward ? 1 : -1; int frontY = y + step; int backY = y - step; - PlatformBlockState front = frontY >= 0 && frontY < data.getHeight() ? data.get(x, frontY, z) : null; - PlatformBlockState back = backY >= 0 && backY < data.getHeight() ? data.get(x, backY, z) : null; + NativeBlockState front = frontY >= 0 && frontY < data.getHeight() ? data.get(x, frontY, z) : null; + NativeBlockState back = backY >= 0 && backY < data.getHeight() ? data.get(x, backY, z) : null; int thickness = 0; if (sameSpike(state, front, !upward)) { String frontThickness = IrisProceduralBlocks.propertyValue(front, "thickness"); diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSugarCane.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSugarCane.java index 5219b50ff..f3206802a 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSugarCane.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSugarCane.java @@ -6,7 +6,7 @@ import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.generation.hydrology.HydrologyColumnLayer; import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.collection.KList; @@ -20,7 +20,7 @@ final class IrisSugarCane { private IrisSugarCane() { } - static boolean isSugarCane(PlatformBlockState state) { + static boolean isSugarCane(NativeBlockState state) { if (state == null) { return false; } @@ -28,7 +28,7 @@ final class IrisSugarCane { return key != null && (key.equals("minecraft:sugar_cane") || key.startsWith("minecraft:sugar_cane[")); } - static boolean canPlace(PlatformBlockState block, Hunk data, + static boolean canPlace(NativeBlockState block, Hunk data, int x, int y, int z, int worldX, int worldZ, Engine engine) { if (!isSugarCane(block)) { return true; @@ -37,8 +37,8 @@ final class IrisSugarCane { return false; } int baseY = y - 1; - PlatformBlockState support = data.get(x, baseY, z); - PlatformBlockState target = data.get(x, y, z); + NativeBlockState support = data.get(x, baseY, z); + NativeBlockState target = data.get(x, y, z); if (support == null || !block.canPlaceOnto(support) || target != null && !target.isAir()) { return false; } @@ -63,7 +63,7 @@ final class IrisSugarCane { return false; } - static boolean supportsAdjacent(PlatformBlockState state) { + static boolean supportsAdjacent(NativeBlockState state) { if (state == null) { return false; } @@ -91,7 +91,7 @@ final class IrisSugarCane { return false; } IrisBiome biome = complex.getTrueBiomeStream().get(x, z); - KList layers = biome.generateSeaLayers(x, z, + KList layers = biome.generateSeaLayers(x, z, new RNG(engine.getSeedManager().getTerrain()), fluidHeight - terrainHeight, engine.getData()); int depth = fluidHeight - y; return supportsAdjacent(layers.hasIndex(depth) ? layers.get(depth) : complex.resolveSurfaceFluid(x, z)); diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSurfaceDecorator.java b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSurfaceDecorator.java index a60a29d40..97d20bba3 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/IrisSurfaceDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/IrisSurfaceDecorator.java @@ -25,7 +25,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.math.RNG; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public class IrisSurfaceDecorator extends IrisEngineDecorator { private final RNG partRNG; @@ -53,13 +53,13 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { @BlockCoordinates @Override public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, int height, int max) { + Hunk data, IrisBiome biome, int height, int max) { decorate(x, z, realX, realX1, realX_1, realZ, realZ1, realZ_1, data, biome, biome.getInferredType(), height, max); } @BlockCoordinates public void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, - Hunk data, IrisBiome biome, InferredType inferredType, int height, int max) { + Hunk data, IrisBiome biome, InferredType inferredType, int height, int max) { int fluidHeight = getEngine().getMantle().getFluidHeight(realX, realZ); if (inferredType == InferredType.SHORE && height < fluidHeight) { return; @@ -104,11 +104,11 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { return inferredType == InferredType.CAVE; } - static boolean hasFluidAbove(Hunk data, int x, int height, int z) { + static boolean hasFluidAbove(Hunk data, int x, int height, int z) { return height + 1 < data.getHeight() && data.get(x, height + 1, z).isFluid(); } - public static boolean isAquaticPlacement(PlatformBlockState state) { + public static boolean isAquaticPlacement(NativeBlockState state) { if (state == null) { return false; } @@ -129,7 +129,7 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { && !material.contains(":dead_"); } - public static boolean hasConnectedWater(Hunk data, int x, int y, int z) { + public static boolean hasConnectedWater(Hunk data, int x, int y, int z) { if (y < 0 || y >= data.getHeight() || !isWater(data.get(x, y, z))) { return false; } @@ -142,7 +142,7 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { static AquaticPlacementSnapshot captureAquaticPlacement( IrisDecorator decorator, IrisData irisData, - Hunk data, + Hunk data, int x, int z, int height, @@ -161,7 +161,7 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { return AquaticPlacementSnapshot.EMPTY; } - PlatformBlockState[] originals = new PlatformBlockState[upperY - lowerY + 1]; + NativeBlockState[] originals = new NativeBlockState[upperY - lowerY + 1]; boolean[] connectedWater = new boolean[originals.length]; for (int i = 0; i < originals.length; i++) { int y = lowerY + i; @@ -172,17 +172,17 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { } private static boolean mayPlaceAquatic(IrisDecorator decorator, IrisData irisData) { - PlatformBlockState[] palette = decorator.getBlockDataArray(irisData); + NativeBlockState[] palette = decorator.getBlockDataArray(irisData); if (palette != null) { - for (PlatformBlockState state : palette) { + for (NativeBlockState state : palette) { if (isAquaticPlacement(state)) { return true; } } } - PlatformBlockState[] topPalette = decorator.getBlockDataTopsArray(irisData); + NativeBlockState[] topPalette = decorator.getBlockDataTopsArray(irisData); if (topPalette != null) { - for (PlatformBlockState state : topPalette) { + for (NativeBlockState state : topPalette) { if (isAquaticPlacement(state)) { return true; } @@ -199,28 +199,28 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { return Math.max(1, Math.min(Math.max(1, max), decorator.getStackMax())); } - private static boolean isWaterNeighbor(Hunk data, int x, int y, int z) { + private static boolean isWaterNeighbor(Hunk data, int x, int y, int z) { if (x < 0 || x >= data.getWidth() || z < 0 || z >= data.getDepth()) { return false; } return isWater(data.get(x, y, z)); } - private static boolean isWater(PlatformBlockState state) { + private static boolean isWater(NativeBlockState state) { return state != null && state.isWater(); } static final class AquaticPlacementSnapshot { private static final AquaticPlacementSnapshot EMPTY = new AquaticPlacementSnapshot( - 0, new PlatformBlockState[0], new boolean[0]); + 0, new NativeBlockState[0], new boolean[0]); private final int lowerY; - private final PlatformBlockState[] originals; + private final NativeBlockState[] originals; private final boolean[] connectedWater; private AquaticPlacementSnapshot( int lowerY, - PlatformBlockState[] originals, + NativeBlockState[] originals, boolean[] connectedWater ) { this.lowerY = lowerY; @@ -228,7 +228,7 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { this.connectedWater = connectedWater; } - void restoreIfUnsupported(Hunk data, int x, int z) { + void restoreIfUnsupported(Hunk data, int x, int z) { if (!containsUnsupportedAquatic(data, x, z)) { return; } @@ -237,9 +237,9 @@ public class IrisSurfaceDecorator extends IrisEngineDecorator { } } - private boolean containsUnsupportedAquatic(Hunk data, int x, int z) { + private boolean containsUnsupportedAquatic(Hunk data, int x, int z) { for (int i = 0; i < originals.length; i++) { - PlatformBlockState placed = data.get(x, lowerY + i, z); + NativeBlockState placed = data.get(x, lowerY + i, z); if (placed != originals[i] && isAquaticPlacement(placed) && !connectedWater[i]) { return true; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/coral/CoralGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/coral/CoralGenerator.java index a1ed15838..8dfc98b1c 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/coral/CoralGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/coral/CoralGenerator.java @@ -22,7 +22,7 @@ import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.generation.decoration.tree.TreeFunctions; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -50,7 +50,7 @@ public final class CoralGenerator { case TENDRIL -> buildTendril(canvas, coral, height, shapeSeed, rng); } - Map resolved = resolve(canvas, coral, data); + Map resolved = resolve(canvas, coral, data); return IrisProceduralBlocks.assemble(resolved); } @@ -233,15 +233,15 @@ public final class CoralGenerator { }; } - private static Map resolve(CoralCanvas canvas, IrisCoral coral, IrisData data) { - Map out = new HashMap<>(); + private static Map resolve(CoralCanvas canvas, IrisCoral coral, IrisData data) { + Map out = new HashMap<>(); RNG paletteRng = new RNG(coral.getSeed()); for (Map.Entry entry : canvas.getCells().entrySet()) { int[] xyz = CoralCanvas.decode(entry.getKey()); int x = xyz[0]; int y = xyz[1]; int z = xyz[2]; - PlatformBlockState state; + NativeBlockState state; if (entry.getValue() == CoralCanvas.Role.TIP) { state = IrisProceduralBlocks.resolve(coral.getTipBlock(), coral.getTipPalette(), data, x, y, z, paletteRng); if (state == null) { diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/coral/IrisCoral.java b/core/src/main/java/art/arcane/iris/generation/decoration/coral/IrisCoral.java index a926799ea..233d4eb0d 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/coral/IrisCoral.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/coral/IrisCoral.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/crystal/CrystalGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/crystal/CrystalGenerator.java index aa7e27bc8..35fe14a46 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/crystal/CrystalGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/crystal/CrystalGenerator.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.crystal; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -39,7 +39,7 @@ public final class CrystalGenerator { CrystalBaseBuilder.build(canvas, crystal, structureSeed + 977L); CrystalShardBuilder.build(canvas, crystal, rng); - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); RNG paletteRng = new RNG(crystal.getSeed()); RNG tipRng = new RNG(crystal.getSeed() + (variantIndex * 31337L) + 4099L); @@ -48,7 +48,7 @@ public final class CrystalGenerator { int x = position.getBlockX(); int y = position.getBlockY(); int z = position.getBlockZ(); - PlatformBlockState blockData = resolveRole(crystal, entry.getValue(), data, x, y, z, paletteRng, tipRng); + NativeBlockState blockData = resolveRole(crystal, entry.getValue(), data, x, y, z, paletteRng, tipRng); if (blockData == null) { continue; } @@ -58,7 +58,7 @@ public final class CrystalGenerator { return IrisProceduralBlocks.assemble(resolved); } - private static PlatformBlockState resolveRole(IrisCrystal crystal, CrystalRole role, IrisData data, int x, int y, int z, RNG paletteRng, RNG tipRng) { + private static NativeBlockState resolveRole(IrisCrystal crystal, CrystalRole role, IrisData data, int x, int y, int z, RNG paletteRng, RNG tipRng) { return switch (role) { case BASE -> resolveBase(crystal, data, x, y, z, paletteRng); case SHARD -> resolveShard(crystal, data, x, y, z, paletteRng); @@ -66,11 +66,11 @@ public final class CrystalGenerator { }; } - private static PlatformBlockState resolveBase(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng) { + private static NativeBlockState resolveBase(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng) { boolean hasBase = IrisProceduralBlocks.paletteSet(crystal.getBasePalette()) || (crystal.getBaseBlock() != null && !crystal.getBaseBlock().isEmpty()); if (hasBase) { - PlatformBlockState base = IrisProceduralBlocks.resolve(crystal.getBaseBlock(), crystal.getBasePalette(), data, x, y, z, paletteRng); + NativeBlockState base = IrisProceduralBlocks.resolve(crystal.getBaseBlock(), crystal.getBasePalette(), data, x, y, z, paletteRng); if (base != null) { return base; } @@ -78,17 +78,17 @@ public final class CrystalGenerator { return resolveShard(crystal, data, x, y, z, paletteRng); } - private static PlatformBlockState resolveShard(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng) { + private static NativeBlockState resolveShard(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng) { return IrisProceduralBlocks.resolve(crystal.getBlock(), crystal.getBlockPalette(), data, x, y, z, paletteRng); } - private static PlatformBlockState resolveTip(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng, RNG tipRng) { + private static NativeBlockState resolveTip(IrisCrystal crystal, IrisData data, int x, int y, int z, RNG paletteRng, RNG tipRng) { boolean hasTip = IrisProceduralBlocks.paletteSet(crystal.getTipPalette()) || (crystal.getTipBlock() != null && !crystal.getTipBlock().isEmpty()); if (hasTip) { if (tipRng.chance(crystal.getTipChance())) { - PlatformBlockState tip = IrisProceduralBlocks.resolve(crystal.getTipBlock(), crystal.getTipPalette(), data, x, y, z, paletteRng); + NativeBlockState tip = IrisProceduralBlocks.resolve(crystal.getTipBlock(), crystal.getTipPalette(), data, x, y, z, paletteRng); if (tip != null) { return tip; } @@ -98,7 +98,7 @@ public final class CrystalGenerator { if (crystal.isGlow() && crystal.getGlowBlock() != null && !crystal.getGlowBlock().isEmpty()) { if (tipRng.chance(crystal.getTipChance())) { - PlatformBlockState glow = IrisProceduralBlocks.resolve(crystal.getGlowBlock(), null, data, x, y, z, paletteRng); + NativeBlockState glow = IrisProceduralBlocks.resolve(crystal.getGlowBlock(), null, data, x, y, z, paletteRng); if (glow != null) { return glow; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/crystal/IrisCrystal.java b/core/src/main/java/art/arcane/iris/generation/decoration/crystal/IrisCrystal.java index abe4f3bf9..6c0dc7152 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/crystal/IrisCrystal.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/crystal/IrisCrystal.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolver.java b/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolver.java index 05be4f927..ca5f8253a 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolver.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolver.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.formation; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -38,13 +38,13 @@ public final class FormationBlockResolver { paletteRng = new RNG(formation.getSeed()); } - public PlatformBlockState resolve(FormationCanvas.Role role, Vector3i raw) { + public NativeBlockState resolve(FormationCanvas.Role role, Vector3i raw) { int x = raw.getBlockX(); int y = raw.getBlockY(); int z = raw.getBlockZ(); if (role == FormationCanvas.Role.CAP && capDefined(formation)) { - PlatformBlockState cap = IrisProceduralBlocks.resolve(formation.getCapBlock(), formation.getCapPalette(), data, x, y, z, paletteRng); + NativeBlockState cap = IrisProceduralBlocks.resolve(formation.getCapBlock(), formation.getCapPalette(), data, x, y, z, paletteRng); if (cap != null) { return cap; } @@ -59,7 +59,7 @@ public final class FormationBlockResolver { strataRng = new RNG(formation.getSeed() + band * 31L); strataRngs.put(band, strataRng); } - PlatformBlockState strataState = strata.get(strataRng, x, band, z, data); + NativeBlockState strataState = strata.get(strataRng, x, band, z, data); if (strataState != null) { return strataState; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationGenerator.java index 5d870e3ee..7d1f7b59c 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/formation/FormationGenerator.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.formation; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -60,10 +60,10 @@ public final class FormationGenerator { return null; } - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); FormationBlockResolver resolver = new FormationBlockResolver(f, data); for (Map.Entry entry : canvas.getCells().entrySet()) { - PlatformBlockState bd = resolver.resolve(entry.getValue(), entry.getKey()); + NativeBlockState bd = resolver.resolve(entry.getValue(), entry.getKey()); if (bd == null) { continue; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/formation/IrisFormation.java b/core/src/main/java/art/arcane/iris/generation/decoration/formation/IrisFormation.java index c005cb06d..6cae80b83 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/formation/IrisFormation.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/formation/IrisFormation.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/fungus/FungusGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/fungus/FungusGenerator.java index 9a8c30131..6ed024ac1 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/fungus/FungusGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/fungus/FungusGenerator.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.fungus; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -47,10 +47,10 @@ public final class FungusGenerator { } RNG paletteRng = new RNG(fungus.getSeed()); - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); for (Map.Entry entry : roles.entrySet()) { Vector3i pos = entry.getKey(); - PlatformBlockState bd = resolveRole(fungus, entry.getValue(), data, pos, paletteRng); + NativeBlockState bd = resolveRole(fungus, entry.getValue(), data, pos, paletteRng); if (bd == null) { continue; } @@ -79,7 +79,7 @@ public final class FungusGenerator { return rng.i(lo, hi + 1); } - private static PlatformBlockState resolveRole(IrisFungus fungus, FungusCellRole role, IrisData data, Vector3i pos, RNG paletteRng) { + private static NativeBlockState resolveRole(IrisFungus fungus, FungusCellRole role, IrisData data, Vector3i pos, RNG paletteRng) { int x = pos.getBlockX(); int y = pos.getBlockY(); int z = pos.getBlockZ(); @@ -87,11 +87,11 @@ public final class FungusGenerator { case STEM -> IrisProceduralBlocks.resolve(fungus.getStem(), fungus.getStemPalette(), data, x, y, z, paletteRng); case CAP -> IrisProceduralBlocks.resolve(fungus.getCap(), fungus.getCapPalette(), data, x, y, z, paletteRng); case GILL -> { - PlatformBlockState gill = IrisProceduralBlocks.resolve(fungus.getGillBlock(), fungus.getGillPalette(), data, x, y, z, paletteRng); + NativeBlockState gill = IrisProceduralBlocks.resolve(fungus.getGillBlock(), fungus.getGillPalette(), data, x, y, z, paletteRng); yield gill != null ? gill : IrisProceduralBlocks.resolve(fungus.getCap(), fungus.getCapPalette(), data, x, y, z, paletteRng); } case SPOT -> { - PlatformBlockState spot = IrisProceduralBlocks.resolve(fungus.getSpotBlock(), fungus.getSpotPalette(), data, x, y, z, paletteRng); + NativeBlockState spot = IrisProceduralBlocks.resolve(fungus.getSpotBlock(), fungus.getSpotPalette(), data, x, y, z, paletteRng); yield spot != null ? spot : IrisProceduralBlocks.resolve(fungus.getCap(), fungus.getCapPalette(), data, x, y, z, paletteRng); } }; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/fungus/IrisFungus.java b/core/src/main/java/art/arcane/iris/generation/decoration/fungus/IrisFungus.java index cddd975d4..1089e1a0b 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/fungus/IrisFungus.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/fungus/IrisFungus.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/IrisRuin.java b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/IrisRuin.java index cf2178b01..3606bdf14 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/IrisRuin.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/IrisRuin.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinBlockCanvas.java b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinBlockCanvas.java index 4cddfeac6..c5599d618 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinBlockCanvas.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinBlockCanvas.java @@ -18,7 +18,7 @@ package art.arcane.iris.generation.decoration.ruin; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import java.util.HashMap; @@ -33,7 +33,7 @@ final class RuinBlockCanvas { static final class Cell { private Role role; private boolean structural; - private PlatformBlockState accentData; + private NativeBlockState accentData; Cell(Role role, boolean structural) { this.role = role; @@ -48,7 +48,7 @@ final class RuinBlockCanvas { return structural; } - PlatformBlockState accentData() { + NativeBlockState accentData() { return accentData; } @@ -57,7 +57,7 @@ final class RuinBlockCanvas { this.structural = this.structural || structuralNext; } - void makeAccent(PlatformBlockState data) { + void makeAccent(NativeBlockState data) { this.role = Role.ACCENT; this.structural = false; this.accentData = data; @@ -80,14 +80,14 @@ final class RuinBlockCanvas { } } - void accent(int x, int y, int z, PlatformBlockState data) { + void accent(int x, int y, int z, NativeBlockState data) { if (data == null) { return; } cells.put(new Vector3i(x, y, z), accentCell(data)); } - private static Cell accentCell(PlatformBlockState data) { + private static Cell accentCell(NativeBlockState data) { Cell cell = new Cell(Role.ACCENT, false); cell.makeAccent(data); return cell; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinDecoratorApplier.java b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinDecoratorApplier.java index f41a87f4e..6a92d2adf 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinDecoratorApplier.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinDecoratorApplier.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.decoration.ruin; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -47,7 +47,7 @@ final class RuinDecoratorApplier { if (!rng.chance(decorator.getChance())) { continue; } - PlatformBlockState bd = IrisProceduralBlocks.resolve(decorator.getBlock(), decorator.getPalette(), data, v.getBlockX(), v.getBlockY(), v.getBlockZ(), paletteRng); + NativeBlockState bd = IrisProceduralBlocks.resolve(decorator.getBlock(), decorator.getPalette(), data, v.getBlockX(), v.getBlockY(), v.getBlockZ(), paletteRng); if (bd != null) { canvas.accent(v.getBlockX(), v.getBlockY(), v.getBlockZ(), bd); } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinGenerator.java index f603c3b19..e09966f00 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/ruin/RuinGenerator.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.ruin; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.decoration.tree.TreeFunctions; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.math.RNG; @@ -147,11 +147,11 @@ public final class RuinGenerator { } int span = Math.max(1, maxY - minY); - Map blocks = new HashMap<>(); + Map blocks = new HashMap<>(); for (Map.Entry entry : canvas.cells().entrySet()) { Vector3i v = entry.getKey(); RuinBlockCanvas.Cell cell = entry.getValue(); - PlatformBlockState bd; + NativeBlockState bd; if (cell.role() == RuinBlockCanvas.Role.ACCENT) { bd = cell.accentData(); } else { @@ -165,7 +165,7 @@ public final class RuinGenerator { return IrisProceduralBlocks.assemble(blocks); } - private static PlatformBlockState structuralBlock(IrisRuin ruin, RuinBlockCanvas.Cell cell, Vector3i v, IrisData data, RNG paletteRng, double scale, double mossiness, long weatherSeed, int minY, int span) { + private static NativeBlockState structuralBlock(IrisRuin ruin, RuinBlockCanvas.Cell cell, Vector3i v, IrisData data, RNG paletteRng, double scale, double mossiness, long weatherSeed, int minY, int span) { boolean weathered = false; if (mossiness > 0.0) { int sx = (int) Math.round(v.getBlockX() * scale); @@ -178,7 +178,7 @@ public final class RuinGenerator { } if (weathered) { - PlatformBlockState wd = IrisProceduralBlocks.resolve(ruin.getWeatheredBlock(), ruin.getWeatheringPalette(), data, v.getBlockX(), v.getBlockY(), v.getBlockZ(), paletteRng); + NativeBlockState wd = IrisProceduralBlocks.resolve(ruin.getWeatheredBlock(), ruin.getWeatheringPalette(), data, v.getBlockX(), v.getBlockY(), v.getBlockZ(), paletteRng); if (wd != null) { return wd; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/tree/IrisProceduralTree.java b/core/src/main/java/art/arcane/iris/generation/decoration/tree/IrisProceduralTree.java index 483fef040..af850ea5d 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/tree/IrisProceduralTree.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/tree/IrisProceduralTree.java @@ -31,7 +31,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGenerator.java b/core/src/main/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGenerator.java index 8842dd919..2c662a7f3 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGenerator.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.decoration.tree; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.structure.object.IrisObject; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.RNG; @@ -62,13 +62,13 @@ public final class ProceduralTreeGenerator { TreeSupport.ensureLeavesSupported(canvas, 24); } - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); Set trunkPositions = new HashSet<>(); Set leafPositions = new HashSet<>(); RNG paletteRng = new RNG(tree.getSeed()); for (Map.Entry entry : canvas.getCells().entrySet()) { TreeBlockCanvas.Cell cell = entry.getValue(); - PlatformBlockState state = TreeBlockResolver.resolve(tree, data, cell, entry.getKey(), paletteRng); + NativeBlockState state = TreeBlockResolver.resolve(tree, data, cell, entry.getKey(), paletteRng); if (state == null) { continue; } @@ -85,7 +85,7 @@ public final class ProceduralTreeGenerator { return assemble(resolved); } - private static IrisObject assemble(Map resolved) { + private static IrisObject assemble(Map resolved) { if (resolved.isEmpty()) { return null; } @@ -113,7 +113,7 @@ public final class ProceduralTreeGenerator { int cz = d / 2; IrisObject object = new IrisObject(w, h, d); - for (Map.Entry entry : resolved.entrySet()) { + for (Map.Entry entry : resolved.entrySet()) { TreeBlockCanvas.Vec v = entry.getKey(); int nx = v.x() - minX - cx; int ny = v.y() - cy + 1; diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockMaterial.java b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockMaterial.java index ddf8e4594..2498809fc 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockMaterial.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockMaterial.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.decoration.tree; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import java.util.Objects; @@ -12,7 +12,7 @@ public record TreeBlockMaterial(String materialKey) { } } - public static TreeBlockMaterial of(PlatformBlockState state) { + public static TreeBlockMaterial of(NativeBlockState state) { return of(Objects.requireNonNull(state, "state").key()); } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolver.java b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolver.java index 941916808..9695e0d0b 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolver.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolver.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.decoration.tree; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.math.RNG; @@ -29,11 +29,11 @@ public final class TreeBlockResolver { private TreeBlockResolver() { } - public static PlatformBlockState resolve(IrisProceduralTree tree, IrisData data, TreeBlockCanvas.Cell cell, TreeBlockCanvas.Vec pos, RNG paletteRng) { + public static NativeBlockState resolve(IrisProceduralTree tree, IrisData data, TreeBlockCanvas.Cell cell, TreeBlockCanvas.Vec pos, RNG paletteRng) { return switch (cell.role()) { case TRUNK -> finishTrunk(resolveBlock(tree.getTrunk(), tree.getTrunkPalette(), data, pos, paletteRng), cell); case SECONDARY_TRUNK -> { - PlatformBlockState state = resolveBlock(tree.getSecondaryTrunk(), tree.getSecondaryTrunkPalette(), data, pos, paletteRng); + NativeBlockState state = resolveBlock(tree.getSecondaryTrunk(), tree.getSecondaryTrunkPalette(), data, pos, paletteRng); if (state == null) { state = resolveBlock(tree.getTrunk(), tree.getTrunkPalette(), data, pos, paletteRng); } @@ -41,7 +41,7 @@ public final class TreeBlockResolver { } case LEAF -> resolveBlock(tree.getLeaves(), tree.getLeavesPalette(), data, pos, paletteRng); case SECONDARY_LEAF -> { - PlatformBlockState state = resolveSecondaryLeaf(tree, data, pos, paletteRng); + NativeBlockState state = resolveSecondaryLeaf(tree, data, pos, paletteRng); if (state == null) { state = resolveBlock(tree.getLeaves(), tree.getLeavesPalette(), data, pos, paletteRng); } @@ -53,7 +53,7 @@ public final class TreeBlockResolver { yield null; } IrisTreeDecorator dec = tree.getDecorators().get(index); - PlatformBlockState state = resolveBlock(dec.getBlock(), dec.getPalette(), data, pos, paletteRng); + NativeBlockState state = resolveBlock(dec.getBlock(), dec.getPalette(), data, pos, paletteRng); if (state != null && cell.facing() != null && IrisProceduralBlocks.hasProperty(state, "facing")) { try { state = state.withProperty("facing", cell.facing().toLowerCase()); @@ -66,7 +66,7 @@ public final class TreeBlockResolver { }; } - private static PlatformBlockState finishTrunk(PlatformBlockState state, TreeBlockCanvas.Cell cell) { + private static NativeBlockState finishTrunk(NativeBlockState state, TreeBlockCanvas.Cell cell) { if (state == null) { return null; } @@ -82,7 +82,7 @@ public final class TreeBlockResolver { return state; } - private static PlatformBlockState resolveSecondaryLeaf(IrisProceduralTree tree, IrisData data, TreeBlockCanvas.Vec pos, RNG paletteRng) { + private static NativeBlockState resolveSecondaryLeaf(IrisProceduralTree tree, IrisData data, TreeBlockCanvas.Vec pos, RNG paletteRng) { if (IrisProceduralBlocks.paletteSet(tree.getSecondaryLeavesPalette())) { return tree.getSecondaryLeavesPalette().get(paletteRng, pos.x(), pos.y(), pos.z(), data); } @@ -115,7 +115,7 @@ public final class TreeBlockResolver { return tree.getWeightedSecondaryLeaves().get(tree.getWeightedSecondaryLeaves().size() - 1).getBlock(); } - private static PlatformBlockState resolveBlock(String block, IrisMaterialPalette palette, IrisData data, TreeBlockCanvas.Vec pos, RNG paletteRng) { + private static NativeBlockState resolveBlock(String block, IrisMaterialPalette palette, IrisData data, TreeBlockCanvas.Vec pos, RNG paletteRng) { if (IrisProceduralBlocks.paletteSet(palette)) { return palette.get(paletteRng, pos.x(), pos.y(), pos.z(), data); } @@ -125,7 +125,7 @@ public final class TreeBlockResolver { return null; } - private static PlatformBlockState woodCap(PlatformBlockState state) { + private static NativeBlockState woodCap(NativeBlockState state) { String key = state.key(); int bracket = key.indexOf('['); String base = bracket < 0 ? key : key.substring(0, bracket); @@ -138,7 +138,7 @@ public final class TreeBlockResolver { if (woodKey == null) { return state; } - PlatformBlockState wood = B.getStateOrNull(woodKey, false); + NativeBlockState wood = B.getStateOrNull(woodKey, false); if (wood == null) { return state; } diff --git a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreePlausibility.java b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreePlausibility.java index 4f8df67ac..9f30d3dbb 100644 --- a/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreePlausibility.java +++ b/core/src/main/java/art/arcane/iris/generation/decoration/tree/TreePlausibility.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.decoration.tree; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import java.util.ArrayDeque; import java.util.HashMap; @@ -36,11 +36,11 @@ public final class TreePlausibility { private TreePlausibility() { } - public static void apply(Map resolved, Set trunkPositions, + public static void apply(Map resolved, Set trunkPositions, Set leafPositions, IrisProceduralTree tree) { Set realLeaves = new HashSet<>(); for (TreeBlockCanvas.Vec v : leafPositions) { - PlatformBlockState state = resolved.get(v); + NativeBlockState state = resolved.get(v); if (state != null && IrisProceduralBlocks.hasProperty(state, "persistent") && IrisProceduralBlocks.hasProperty(state, "distance")) { realLeaves.add(v); } @@ -54,7 +54,7 @@ public final class TreePlausibility { : Map.of(); for (TreeBlockCanvas.Vec v : realLeaves) { - PlatformBlockState leaf = resolved.get(v); + NativeBlockState leaf = resolved.get(v); Integer d = distance.get(v); if (!tree.isPlausible()) { leaf = leaf.withProperty("persistent", "true").withProperty("distance", "1"); diff --git a/core/src/main/java/art/arcane/iris/generation/image/IrisImageMapRuntime.java b/core/src/main/java/art/arcane/iris/generation/image/IrisImageMapRuntime.java index cb3278ccf..921e042e2 100644 --- a/core/src/main/java/art/arcane/iris/generation/image/IrisImageMapRuntime.java +++ b/core/src/main/java/art/arcane/iris/generation/image/IrisImageMapRuntime.java @@ -6,7 +6,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.generation.terrain.InferredType; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import java.util.ArrayList; @@ -116,7 +116,7 @@ public final class IrisImageMapRuntime { return target == null ? null : binding.biomes.get(target); } - public PlatformBlockState sampleSurfaceBlock(double worldX, double worldZ) { + public NativeBlockState sampleSurfaceBlock(double worldX, double worldZ) { RuntimeBinding binding = applications.get(IrisImageMapApplication.SURFACE_BLOCK); if (binding == null || !selectCategorical(binding.maskWeight(worldX, worldZ))) { return null; @@ -172,7 +172,7 @@ public final class IrisImageMapRuntime { } Map regions = new LinkedHashMap<>(); Map biomes = new LinkedHashMap<>(); - Map blocks = new LinkedHashMap<>(); + Map blocks = new LinkedHashMap<>(); Set targets = legendTargets(definition); if (application == IrisImageMapApplication.REGION) { @@ -197,7 +197,7 @@ public final class IrisImageMapRuntime { } } else if (application == IrisImageMapApplication.SURFACE_BLOCK) { for (String target : targets) { - PlatformBlockState block = B.getStateOrNull(target, false); + NativeBlockState block = B.getStateOrNull(target, false); if (block == null) { throw validation("Image-map '" + key + "' references unknown surface block target '" + target + "'"); } @@ -351,7 +351,7 @@ public final class IrisImageMapRuntime { private final CompiledIrisImageMap compiled; private final Map regions; private final Map biomes; - private final Map blocks; + private final Map blocks; private final List maskDefinitions; private IrisImageMapMaskSampler masks = IrisImageMapMaskSampler.empty(); @@ -361,7 +361,7 @@ public final class IrisImageMapRuntime { CompiledIrisImageMap compiled, Map regions, Map biomes, - Map blocks, + Map blocks, List maskDefinitions ) { this.key = key; diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransaction.java b/core/src/main/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransaction.java index bafde6ba8..61d53bff4 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransaction.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransaction.java @@ -25,7 +25,7 @@ import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; @@ -43,7 +43,7 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { private final int minDepthBelowSurface; private final int worldHeight; private final KList mutations; - private final Map bufferedBlocks; + private final Map bufferedBlocks; private final Long2IntOpenHashMap surfaceHeights; private final Long2IntOpenHashMap caveCeilings; private int blockWrites; @@ -127,7 +127,7 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (state == null) { return; } @@ -137,8 +137,8 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { } @Override - public PlatformBlockState get(int x, int y, int z) { - PlatformBlockState buffered = bufferedBlocks.get(new PositionKey(x, y, z)); + public NativeBlockState get(int x, int y, int z) { + NativeBlockState buffered = bufferedBlocks.get(new PositionKey(x, y, z)); return buffered == null ? delegate.get(x, y, z) : buffered; } @@ -159,7 +159,7 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { @Override public boolean isSolid(int x, int y, int z) { - PlatformBlockState buffered = bufferedBlocks.get(new PositionKey(x, y, z)); + NativeBlockState buffered = bufferedBlocks.get(new PositionKey(x, y, z)); return buffered == null ? delegate.isSolid(x, y, z) : B.isSolid(buffered); } @@ -191,7 +191,7 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { return; } mutations.add(new DataMutation(x, y, z, data)); - if (data instanceof PlatformBlockState state) { + if (data instanceof NativeBlockState state) { bufferedBlocks.put(new PositionKey(x, y, z), state); blockWrites++; } @@ -276,7 +276,7 @@ final class CaveObjectPlacementTransaction implements IObjectPlacer { void apply(IObjectPlacer placer); } - private record BlockMutation(int x, int y, int z, PlatformBlockState value) implements BufferedMutation { + private record BlockMutation(int x, int y, int z, NativeBlockState value) implements BufferedMutation { @Override public void apply(IObjectPlacer placer) { placer.set(x, y, z, value); diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/EngineMantle.java b/core/src/main/java/art/arcane/iris/generation/mantle/EngineMantle.java index 95d84bab3..42b0a204c 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/EngineMantle.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/EngineMantle.java @@ -49,7 +49,7 @@ import art.arcane.volmlib.util.matter.MatterMarker; import art.arcane.volmlib.util.matter.Matter; import art.arcane.volmlib.util.matter.slices.UpdateMatter; import art.arcane.iris.generation.concurrent.MultiBurst; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.jetbrains.annotations.UnmodifiableView; import java.util.List; @@ -128,7 +128,7 @@ public interface EngineMantle extends MatterGenerator { if (resolved.isPresent()) { PreObjectMatterCell cell = getMantle().get(x, h, z, PreObjectMatterCell.class); if (cell != null && cell.blockCaptured()) { - PlatformBlockState block = getMantle().get(x, h, z, PlatformBlockState.class); + NativeBlockState block = getMantle().get(x, h, z, NativeBlockState.class); if (block != null) { return (block.isAir() || block.isFluid()) && resolved.get().geometry().hasSolidAbove(h + getEngine().getMinHeight()); @@ -147,14 +147,14 @@ public interface EngineMantle extends MatterGenerator { || getComplex().isTerrain3DOpening(x, h, z); } - default PlatformBlockState get(int x, int y, int z) { - PlatformBlockState block = getMantle().get(x, y, z, PlatformBlockState.class); + default NativeBlockState get(int x, int y, int z) { + NativeBlockState block = getMantle().get(x, y, z, NativeBlockState.class); Optional resolved = getComplex().resolvedTerrainColumn(x, z); if (resolved.isPresent()) { PreObjectMatterCell cell = getMantle().get(x, y, z, PreObjectMatterCell.class); if (cell == null || !cell.blockCaptured()) { String stateKey = resolved.get().geometry().voxelAt(y + getEngine().getMinHeight()).stateKey(); - PlatformBlockState natural = IrisPlatforms.get().registries().blockOrNull(stateKey); + NativeBlockState natural = IrisPlatforms.get().registries().blockOrNull(stateKey); if (natural == null) { throw new IllegalStateException("Saved terrain state is unavailable: " + stateKey); } @@ -239,7 +239,7 @@ public interface EngineMantle extends MatterGenerator { default void insertMatter( int x, int z, - Hunk blocks, + Hunk blocks, boolean multicore, ChunkContext context ) { @@ -260,7 +260,7 @@ public interface EngineMantle extends MatterGenerator { int localZ = i & 15; layouts[i] = context.getDimensionStackLayout(localX, localZ); } - chunk.iterate(PlatformBlockState.class, (localX, y, localZ, value) -> { + chunk.iterate(NativeBlockState.class, (localX, y, localZ, value) -> { DimensionStackLayout layout = layouts[(localX << 4) | (localZ & 15)]; if (!layout.isHostFeatureProtectedY(y)) { blocks.set(localX, y, localZ, value); @@ -277,14 +277,14 @@ public interface EngineMantle extends MatterGenerator { int worldZ = chunkBlockZ + lz; upperYs[i] = upperCtx.getEffectiveSurfaceY(worldX, worldZ); } - chunk.iterate(PlatformBlockState.class, (lx, y, lz, value) -> { + chunk.iterate(NativeBlockState.class, (lx, y, lz, value) -> { int colIdx = (lx << 4) | (lz & 15); if (y < upperYs[colIdx]) { blocks.set(lx, y, lz, value); } }); } else { - chunk.iterate(PlatformBlockState.class, (lx, y, lz, value) -> blocks.set(lx, y, lz, value)); + chunk.iterate(NativeBlockState.class, (lx, y, lz, value) -> blocks.set(lx, y, lz, value)); } } finally { chunk.release(); @@ -371,7 +371,7 @@ public interface EngineMantle extends MatterGenerator { } private void cleanupSlices(MantleChunk chunk, boolean force) { - MantleSliceRetention.deleteUnlessRetained(chunk, PlatformBlockState.class); + MantleSliceRetention.deleteUnlessRetained(chunk, NativeBlockState.class); if (force) { MantleSliceRetention.deleteUnlessRetained(chunk, String.class); } diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransaction.java b/core/src/main/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransaction.java index b1540b9f3..4a419fad5 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransaction.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransaction.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import org.jetbrains.annotations.Nullable; @@ -34,7 +34,7 @@ import java.util.Map; final class FloatingObjectPlacementTransaction implements IObjectPlacer { private final IslandObjectPlacer delegate; private final KList mutations; - private final Map bufferedBlocks; + private final Map bufferedBlocks; private int blockWrites; FloatingObjectPlacementTransaction(IslandObjectPlacer delegate) { @@ -85,7 +85,7 @@ final class FloatingObjectPlacementTransaction implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (state == null) { return; } @@ -95,8 +95,8 @@ final class FloatingObjectPlacementTransaction implements IObjectPlacer { } @Override - public PlatformBlockState get(int x, int y, int z) { - PlatformBlockState state = bufferedBlocks.get(new PositionKey(x, y, z)); + public NativeBlockState get(int x, int y, int z) { + NativeBlockState state = bufferedBlocks.get(new PositionKey(x, y, z)); return state == null ? delegate.get(x, y, z) : state; } @@ -117,7 +117,7 @@ final class FloatingObjectPlacementTransaction implements IObjectPlacer { @Override public boolean isSolid(int x, int y, int z) { - PlatformBlockState state = bufferedBlocks.get(new PositionKey(x, y, z)); + NativeBlockState state = bufferedBlocks.get(new PositionKey(x, y, z)); return state == null ? delegate.isSolid(x, y, z) : B.isSolid(state); } @@ -149,7 +149,7 @@ final class FloatingObjectPlacementTransaction implements IObjectPlacer { return; } mutations.add(new DataMutation(x, y, z, data)); - if (data instanceof PlatformBlockState state) { + if (data instanceof NativeBlockState state) { bufferedBlocks.put(new PositionKey(x, y, z), state); blockWrites++; } @@ -194,7 +194,7 @@ final class FloatingObjectPlacementTransaction implements IObjectPlacer { void apply(IObjectPlacer placer); } - private record BlockMutation(int x, int y, int z, PlatformBlockState value) implements BufferedMutation { + private record BlockMutation(int x, int y, int z, NativeBlockState value) implements BufferedMutation { @Override public void apply(IObjectPlacer placer) { placer.set(x, y, z, value); diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/GoldenDebugObjectPlacer.java b/core/src/main/java/art/arcane/iris/generation/mantle/GoldenDebugObjectPlacer.java index cd513bec0..f7987cf74 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/GoldenDebugObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/GoldenDebugObjectPlacer.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; final class GoldenDebugObjectPlacer implements IObjectPlacer { private static final int[] GOLDEN_DEBUG_TARGET = parseGoldenDebugTarget(resolveGoldenDebugSpec()); @@ -89,12 +89,12 @@ final class GoldenDebugObjectPlacer implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState d) { + public void set(int x, int y, int z, NativeBlockState d) { delegate.set(x, y, z, d); } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return delegate.get(x, y, z); } diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/IrisStructureComponent.java b/core/src/main/java/art/arcane/iris/generation/mantle/IrisStructureComponent.java index 342dd804c..be1cbae21 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/IrisStructureComponent.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/IrisStructureComponent.java @@ -18,6 +18,10 @@ package art.arcane.iris.generation.mantle; +import art.arcane.volmlib.util.structure.StructureCarvingFootprint; + +import art.arcane.volmlib.util.structure.StructureCarveEnvelope; + import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.cache.Cache; @@ -35,13 +39,13 @@ import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.structure.placement.IrisStructure; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; +import art.arcane.volmlib.util.structure.StructureCarveShape; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureStiltSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.iris.world.storage.matter.TileWrapper; @@ -114,8 +118,8 @@ public class IrisStructureComponent extends IrisMantleComponent { } IrisStructureTerrain terrain = placement.resolvedTerrain(); int[] bounds = computePieceBounds(pieces); - int horizontalPadding = terrain.resolvedMode() == IrisStructureTerrainMode.FORCE_CARVE - || terrain.resolvedMode() == IrisStructureTerrainMode.BORE + int horizontalPadding = terrain.resolvedMode() == StructureTerrainMode.FORCE_CARVE + || terrain.resolvedMode() == StructureTerrainMode.BORE ? Math.max(0, terrain.getHorizontalPadding()) : 0; if (bounds == null || !complex.allowsNewGenerationFootprint( @@ -135,13 +139,13 @@ public class IrisStructureComponent extends IrisMantleComponent { clearIntersectingObjectTrees(writer, resolved); } - IrisStructureTerrainMode terrainMode = terrain.resolvedMode(); - if (terrainMode == IrisStructureTerrainMode.FORCE_CARVE) { + StructureTerrainMode terrainMode = terrain.resolvedMode(); + if (terrainMode == StructureTerrainMode.FORCE_CARVE) { forceCarveStructure(writer, pieces, terrain); - } else if (terrainMode == IrisStructureTerrainMode.BORE) { + } else if (terrainMode == StructureTerrainMode.BORE) { boreStructure(writer, pieces, terrain); - } else if (terrainMode != IrisStructureTerrainMode.PRESERVE - && terrainMode != IrisStructureTerrainMode.SOURCE) { + } else if (terrainMode != StructureTerrainMode.PRESERVE + && terrainMode != StructureTerrainMode.SOURCE) { throw new IllegalStateException("Iris assembly terrain mode " + terrainMode + " is not implemented for structure '" + key + "'"); } @@ -198,8 +202,8 @@ public class IrisStructureComponent extends IrisMantleComponent { int worldX = StructureFoundationPlanner.unpackX(column.getLongKey()); int worldZ = StructureFoundationPlanner.unpackZ(column.getLongKey()); int foundationY = column.getIntValue(); - PlatformBlockState foundationState = writer.getDataIfPresent( - worldX, foundationY, worldZ, PlatformBlockState.class); + NativeBlockState foundationState = writer.getDataIfPresent( + worldX, foundationY, worldZ, NativeBlockState.class); if (foundationState == null || !foundationState.isSolid()) { continue; } @@ -207,8 +211,8 @@ public class IrisStructureComponent extends IrisMantleComponent { int groundY = StructureFoundationPlanner.findGroundY( foundationY, maxDepth, 0, y -> { - PlatformBlockState overlay = writer.getDataIfPresent( - worldX, y, worldZ, PlatformBlockState.class); + NativeBlockState overlay = writer.getDataIfPresent( + worldX, y, worldZ, NativeBlockState.class); boolean carved = writer.getDataIfPresent( worldX, y, worldZ, MatterCavern.class) != null; return surfaceStructure @@ -231,7 +235,7 @@ public class IrisStructureComponent extends IrisMantleComponent { IrisData data, int worldX, int mantleY, int worldZ, int mantleOffset) { int worldY = mantleY + mantleOffset; - PlatformBlockState support = palette.get(rng, worldX, worldY, worldZ, data); + NativeBlockState support = palette.get(rng, worldX, worldY, worldZ, data); if (support == null) { throw new IllegalStateException("Structure stilt palette resolved no block at " + worldX + "," + worldY + "," + worldZ); @@ -301,7 +305,7 @@ public class IrisStructureComponent extends IrisMantleComponent { if (bounds == null) { return; } - IrisStructureCarveShape shape = terrain.resolvedShape(); + StructureCarveShape shape = terrain.resolvedShape(); int margin = Math.max(0, terrain.getHorizontalPadding()); int head = Math.max(0, terrain.getCeilingPadding()); int floorCut = Math.max(0, terrain.getFloorPadding()); @@ -311,20 +315,20 @@ public class IrisStructureComponent extends IrisMantleComponent { int worldMax = getEngineMantle().getEngine().getMinHeight() + getEngineMantle().getEngine().getHeight() - 1; int upExtension = shape.maximumCeilingExtension(head, strength); - if (shape == IrisStructureCarveShape.BOX) { + if (shape == StructureCarveShape.BOX) { carveOverboreBox(writer, bounds, margin, head, floorCut, mantleOffset, worldMin, worldMax); return; } long work = overboreCandidateVolume(bounds, margin, upExtension, floorCut); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from( + StructureCarvingFootprint footprint = StructurePieceColumns.from( pieces, margin, Integer.MAX_VALUE); if (footprint == null) { throw new IllegalStateException("Structure force-carve footprint is empty or exceeds addressable memory"); } double frequency = terrain.resolvedErosionFrequency(); - boolean eroded = shape == IrisStructureCarveShape.ERODED && strength > 0D; + boolean eroded = shape == StructureCarveShape.ERODED && strength > 0D; CNG blob = null; CNG roll = null; if (eroded) { @@ -428,7 +432,7 @@ public class IrisStructureComponent extends IrisMantleComponent { Map intersectingObjects = new HashMap<>(); for (PlacedStructurePiece piece : resolved.pieces()) { for (IrisBlockVector local : piece.getObject().getBlocks().keys()) { - PlatformBlockState structureState = piece.getObject().getBlocks().get(local); + NativeBlockState structureState = piece.getObject().getBlocks().get(local); if (!isOccupiedStructureState(structureState)) { continue; } @@ -446,13 +450,13 @@ public class IrisStructureComponent extends IrisMantleComponent { } } - static boolean isOccupiedStructureState(PlatformBlockState state) { + static boolean isOccupiedStructureState(NativeBlockState state) { return !B.isAir(state); } private void collectIntersectingObjectTreeMarker(MantleWriter writer, int x, int y, int z, Map intersectingObjects) { - PlatformBlockState state = writer.getDataIfPresent(x, y, z, PlatformBlockState.class); + NativeBlockState state = writer.getDataIfPresent(x, y, z, NativeBlockState.class); if (state == null || !state.isTreeBlock()) { return; } @@ -585,7 +589,7 @@ public class IrisStructureComponent extends IrisMantleComponent { }, null, getData()); } - static boolean shouldWriteStructureMarker(PlatformBlockState state) { + static boolean shouldWriteStructureMarker(NativeBlockState state) { return state != null && state.isStorageChest(); } @@ -631,8 +635,8 @@ public class IrisStructureComponent extends IrisMantleComponent { continue; } IrisStructureTerrain terrain = placement.resolvedTerrain(); - int carvePadding = terrain.resolvedMode() == IrisStructureTerrainMode.FORCE_CARVE - || terrain.resolvedMode() == IrisStructureTerrainMode.BORE + int carvePadding = terrain.resolvedMode() == StructureTerrainMode.FORCE_CARVE + || terrain.resolvedMode() == StructureTerrainMode.BORE ? Math.max(0, terrain.getHorizontalPadding()) : 0; for (String key : placement.getStructures()) { IrisStructure structure = getData().load(IrisStructure.class, key, false); diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/IslandObjectPlacer.java b/core/src/main/java/art/arcane/iris/generation/mantle/IslandObjectPlacer.java index 7fc5113a6..74e6dedd3 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/IslandObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/IslandObjectPlacer.java @@ -24,7 +24,7 @@ import art.arcane.iris.generation.biome.FloatingIslandSample; import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.biome.IrisFloatingChildBiomes; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.jetbrains.annotations.Nullable; public final class IslandObjectPlacer implements IObjectPlacer { @@ -110,14 +110,14 @@ public final class IslandObjectPlacer implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (!shouldSkipAirColumn(x, y, z)) { wrapped.set(x, y, z, state); } } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return wrapped.get(x, y, z); } diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleCarvingComponent.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleCarvingComponent.java index de0d25939..237ff2041 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleCarvingComponent.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleCarvingComponent.java @@ -27,7 +27,7 @@ import art.arcane.iris.generation.terrain.IrisDimensionCarvingEntry; import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.pack.value.IrisRange; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.stream.ProceduralStream; diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleFloatingObjectComponent.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleFloatingObjectComponent.java index 23371526f..ebee9a7e7 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleFloatingObjectComponent.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleFloatingObjectComponent.java @@ -33,7 +33,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.iris.generation.context.ChunkContext; @@ -484,11 +484,11 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent { return rotation.rotate(new IrisBlockVector(fp.getTallestKx(), fp.getLowestSolidKeyY(), fp.getTallestKz()), 0, 0, 0); } - private static boolean shouldWritePlacementMarker(IObjectPlacer placer, PlatformBlockState state, int x, int y, int z) { + private static boolean shouldWritePlacementMarker(IObjectPlacer placer, NativeBlockState state, int x, int y, int z) { if (state == null) { return false; } - PlatformBlockState existingState = placer.get(x, y, z); + NativeBlockState existingState = placer.get(x, y, z); boolean wouldReplace = B.isSolid(existingState) && B.isVineBlock(state); String material = IrisProceduralBlocks.materialKey(state); boolean placesBlock = !material.equals("minecraft:air") && !material.equals("minecraft:cave_air") && !wouldReplace; diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelView.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelView.java index baf68415c..6ebdb8723 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelView.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelView.java @@ -10,7 +10,7 @@ import art.arcane.iris.generation.hydrology.cave.CavePosition; import art.arcane.iris.generation.hydrology.cave.CaveVoxel; import art.arcane.iris.generation.hydrology.cave.CaveVoxelView; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.mantle.flag.ReservedFlag; import art.arcane.volmlib.util.mantle.runtime.Mantle; @@ -130,7 +130,7 @@ public final class MantleHydrologyCaveVoxelView implements CaveVoxelView { } return CaveVoxel.CAVE_AIR; } - PlatformBlockState block = dataIfPresent(position, PlatformBlockState.class); + NativeBlockState block = dataIfPresent(position, NativeBlockState.class); if (block != null) { if (!block.isFluid()) { return CaveVoxel.SOLID; diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyComponent.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyComponent.java index 75d9b51e4..b2aaf802a 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyComponent.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleHydrologyComponent.java @@ -17,7 +17,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.hydrology.IrisHydrology; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.generation.hydrology.IrisRiverHydrology; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.mantle.flag.MantleFlag; import art.arcane.volmlib.util.mantle.flag.ReservedFlag; @@ -731,16 +731,16 @@ public final class MantleHydrologyComponent extends IrisMantleComponent { ChunkContext context, Publication publication ) { - LinkedHashMap resolvedSurfaceWrites = new LinkedHashMap<>(); + LinkedHashMap resolvedSurfaceWrites = new LinkedHashMap<>(); for (Map.Entry entry : sortedEntries(publication.surfaceWrites())) { CavePosition position = entry.getKey(); SurfaceFluidWrite write = entry.getValue(); - PlatformBlockState fluid = Objects.requireNonNull(context.getComplex().resolveHydrologyFluid( + NativeBlockState fluid = Objects.requireNonNull(context.getComplex().resolveHydrologyFluid( write.profileKey(), position.x(), position.z() )); - PlatformBlockState state = write.action() == HydrologyCaveAction.FALLING_FLUID + NativeBlockState state = write.action() == HydrologyCaveAction.FALLING_FLUID ? fallingFluidState(fluid) : fluid; resolvedSurfaceWrites.put(position, state); @@ -762,7 +762,7 @@ public final class MantleHydrologyComponent extends IrisMantleComponent { )); } } - for (Map.Entry entry : resolvedSurfaceWrites.entrySet()) { + for (Map.Entry entry : resolvedSurfaceWrites.entrySet()) { CavePosition position = entry.getKey(); writer.setData(position.x(), position.y(), position.z(), entry.getValue()); } @@ -825,7 +825,7 @@ public final class MantleHydrologyComponent extends IrisMantleComponent { }; } - private static PlatformBlockState fallingFluidState(PlatformBlockState fluid) { + private static NativeBlockState fallingFluidState(NativeBlockState fluid) { if (!IrisProceduralBlocks.hasProperty(fluid, "level") || "8".equals(IrisProceduralBlocks.propertyValue(fluid, "level"))) { return fluid; diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleObjectComponent.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleObjectComponent.java index e1eab608b..f946b0f46 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleObjectComponent.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleObjectComponent.java @@ -63,7 +63,7 @@ import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.noise.NoiseType; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.DataProvider; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -115,7 +115,7 @@ public class MantleObjectComponent extends IrisMantleComponent { || (placement != null && placement.getTrees() != null && placement.getTrees().isNotEmpty()); } - private static void writeTreeMaterial(IObjectPlacer placer, int x, int y, int z, PlatformBlockState data) { + private static void writeTreeMaterial(IObjectPlacer placer, int x, int y, int z, NativeBlockState data) { placer.setData(x, y, z, TreeBlockMaterial.of(data)); } diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleSliceRetention.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleSliceRetention.java index c268e8f1c..e38593f5c 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleSliceRetention.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleSliceRetention.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.matter.Matter; @@ -42,7 +42,7 @@ public final class MantleSliceRetention { } public static void retain(String className) { - if (className == null || PlatformBlockState.class.getCanonicalName().equals(className)) { + if (className == null || NativeBlockState.class.getCanonicalName().equals(className)) { return; } if (retained.add(className)) { diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/MantleWriter.java b/core/src/main/java/art/arcane/iris/generation/mantle/MantleWriter.java index c7e318a96..81c77193c 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/MantleWriter.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/MantleWriter.java @@ -56,7 +56,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import org.bukkit.util.Vector; @@ -304,7 +304,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { return; } - if (y == 0 && t instanceof PlatformBlockState && engineMantle.getEngine().getDimension().isBedrock()) { + if (y == 0 && t instanceof NativeBlockState && engineMantle.getEngine().getDimension().isBedrock()) { return; } @@ -313,14 +313,14 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { synchronized (chunk) { Matter matter = chunk.getOrCreate(y >> 4); - if ((t instanceof PlatformBlockState || t instanceof MatterCavern) + if ((t instanceof NativeBlockState || t instanceof MatterCavern) && hasProtectedHydrology(matter, x, y, z)) { return; } - if (t instanceof PlatformBlockState) { + if (t instanceof NativeBlockState) { clearDeferredPlacement(matter, x, y, z); } - Class sliceType = t instanceof PlatformBlockState ? PlatformBlockState.class : matter.getClass(t); + Class sliceType = t instanceof NativeBlockState ? NativeBlockState.class : matter.getClass(t); capturePreObjectOriginal(matter, x, y, z, sliceType); matter.slice(sliceType).set(x & 15, y & 15, z & 15, t); } @@ -380,9 +380,9 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (hasProtectedHydrology(matter, x, y, z)) { return false; } - MatterSlice blockSlice = matter.getSlice(PlatformBlockState.class); + MatterSlice blockSlice = matter.getSlice(NativeBlockState.class); if (blockSlice != null) { - capturePreObjectOriginal(matter, x, y, z, PlatformBlockState.class); + capturePreObjectOriginal(matter, x, y, z, NativeBlockState.class); blockSlice.set(x & 15, y & 15, z & 15, null); } clearDeferredPlacement(matter, x, y, z); @@ -413,9 +413,9 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (hasProtectedHydrology(matter, x, y, z)) { return; } - MatterSlice blockSlice = matter.getSlice(PlatformBlockState.class); + MatterSlice blockSlice = matter.getSlice(NativeBlockState.class); if (blockSlice != null) { - capturePreObjectOriginal(matter, x, y, z, PlatformBlockState.class); + capturePreObjectOriginal(matter, x, y, z, NativeBlockState.class); blockSlice.set(x & 15, y & 15, z & 15, null); } clearDeferredPlacement(matter, x, y, z); @@ -444,9 +444,9 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (hasProtectedHydrology(matter, x, y, z)) { return; } - MatterSlice blockSlice = matter.getSlice(PlatformBlockState.class); + MatterSlice blockSlice = matter.getSlice(NativeBlockState.class); if (blockSlice != null) { - capturePreObjectOriginal(matter, x, y, z, PlatformBlockState.class); + capturePreObjectOriginal(matter, x, y, z, NativeBlockState.class); blockSlice.set(x & 15, y & 15, z & 15, null); } clearDeferredPlacement(matter, x, y, z); @@ -522,21 +522,21 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { return complex == null ? Optional.empty() : complex.resolvedTerrainColumn(x, z); } - private PlatformBlockState resolvedBlock(TerrainBoundarySignature column, int y) { + private NativeBlockState resolvedBlock(TerrainBoundarySignature column, int y) { String stateKey = column.geometry().voxelAt(y + engineMantle.getEngine().getMinHeight()).stateKey(); - PlatformBlockState state = IrisPlatforms.get().registries().blockOrNull(stateKey); + NativeBlockState state = IrisPlatforms.get().registries().blockOrNull(stateKey); if (state == null) { throw new IllegalStateException("Saved terrain state is unavailable: " + stateKey); } return state; } - public PlatformBlockState getPrerequisiteBlock(int x, int y, int z) { + public NativeBlockState getPrerequisiteBlock(int x, int y, int z) { Optional resolved = resolvedColumn(x, z); if (resolved.isPresent()) { return resolvedBlock(resolved.get(), y); } - PlatformBlockState block = getPrerequisiteDataIfPresent(x, y, z, PlatformBlockState.class); + NativeBlockState block = getPrerequisiteDataIfPresent(x, y, z, NativeBlockState.class); return block == null ? AIR.get() : block; } @@ -675,7 +675,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { return false; } if (cell.blockCaptured()) { - restoreRaw(matter, x, y, z, PlatformBlockState.class, cell.block()); + restoreRaw(matter, x, y, z, NativeBlockState.class, cell.block()); } if (cell.stringCaptured()) { restoreRaw(matter, x, y, z, String.class, cell.string()); @@ -703,7 +703,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (matter == null || !matter.hasSlice(type)) { return; } - if ((type == PlatformBlockState.class || type == MatterCavern.class) + if ((type == NativeBlockState.class || type == MatterCavern.class) && hasProtectedHydrology(matter, x, y, z)) { return; } @@ -728,7 +728,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { } } - private void setCustomBlock(int x, int y, int z, PlatformBlockState baseState, Identifier identifier) { + private void setCustomBlock(int x, int y, int z, NativeBlockState baseState, Identifier identifier) { if (!allowsWrite(x, z) || y < 0 || y >= mantle.getWorldHeight()) { return; } @@ -746,9 +746,9 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (hasProtectedHydrology(matter, x, y, z)) { return; } - MatterSlice blockSlice = matter.slice(PlatformBlockState.class); + MatterSlice blockSlice = matter.slice(NativeBlockState.class); MatterSlice identifierSlice = matter.slice(Identifier.class); - capturePreObjectOriginal(matter, x, y, z, PlatformBlockState.class); + capturePreObjectOriginal(matter, x, y, z, NativeBlockState.class); blockSlice.set(x & 15, y & 15, z & 15, baseState); identifierSlice.set(x & 15, y & 15, z & 15, identifier); } @@ -770,10 +770,10 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { } Object original = rawValue(matter, localX, localY, localZ, type); PreObjectMatterCell updated; - if (type == PlatformBlockState.class) { + if (type == NativeBlockState.class) { updated = current == null - ? PreObjectMatterCell.block((PlatformBlockState) original) - : current.captureBlock((PlatformBlockState) original); + ? PreObjectMatterCell.block((NativeBlockState) original) + : current.captureBlock((NativeBlockState) original); } else if (type == String.class) { updated = current == null ? PreObjectMatterCell.string((String) original) @@ -787,7 +787,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { } private static boolean isPreObjectType(Class type) { - return type == PlatformBlockState.class || type == String.class || type == MatterCavern.class; + return type == NativeBlockState.class || type == String.class || type == MatterCavern.class; } private static PreObjectMatterCell preObjectCell(Matter matter, int x, int y, int z) { @@ -888,12 +888,12 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { } @Override - public void set(int x, int y, int z, PlatformBlockState s) { + public void set(int x, int y, int z, NativeBlockState s) { if (s == null) { return; } String placementKey = s.deferredPlacementKey(); - PlatformBlockState baseState = s.placementBaseState(); + NativeBlockState baseState = s.placementBaseState(); if (s.isCustom() && placementKey != null && baseState != null) { Identifier identifier = Identifier.fromString(placementKey); setCustomBlock(x, y, z, baseState, identifier); @@ -903,10 +903,10 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { // Read-only probe: getDataIfPresent returns the identical answer without materializing // a 16^3 section + slice on a miss the way getData's getOrCreate path does. - PlatformBlockState block = getDataIfPresent(x, y, z, PlatformBlockState.class); + NativeBlockState block = getDataIfPresent(x, y, z, NativeBlockState.class); Optional resolved = resolvedColumn(x, z); if (resolved.isPresent()) { PreObjectMatterCell cell = getDataIfPresent(x, y, z, PreObjectMatterCell.class); @@ -930,7 +930,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { if (resolved.isPresent()) { PreObjectMatterCell cell = getDataIfPresent(x, y, z, PreObjectMatterCell.class); if (cell != null && cell.blockCaptured()) { - PlatformBlockState block = getDataIfPresent(x, y, z, PlatformBlockState.class); + NativeBlockState block = getDataIfPresent(x, y, z, NativeBlockState.class); if (block != null) { return (block.isAir() || block.isFluid()) && resolved.get().geometry().hasSolidAbove(y + engineMantle.getEngine().getMinHeight()); @@ -973,7 +973,7 @@ public class MantleWriter implements ObjectPassPlacer, AutoCloseable { for (int y = 0; y < carved.length; y++) { PreObjectMatterCell cell = getDataIfPresent(x, y, z, PreObjectMatterCell.class); if (cell != null && cell.blockCaptured()) { - PlatformBlockState block = getDataIfPresent(x, y, z, PlatformBlockState.class); + NativeBlockState block = getDataIfPresent(x, y, z, NativeBlockState.class); if (block != null) { carved[y] = (block.isAir() || block.isFluid()) && resolved.get().geometry().hasSolidAbove(y + engineMantle.getEngine().getMinHeight()) diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/ObjectDestinationTransaction.java b/core/src/main/java/art/arcane/iris/generation/mantle/ObjectDestinationTransaction.java index 5e5ef2a14..432aebdcb 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/ObjectDestinationTransaction.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/ObjectDestinationTransaction.java @@ -6,7 +6,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.structure.object.IObjectPlacer; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.world.storage.matter.TileWrapper; import art.arcane.volmlib.util.matter.MatterCavern; @@ -66,7 +66,7 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { continue; } captureOriginal(originals, mutation.key()); - if (mutation.key().type() == PlatformBlockState.class) { + if (mutation.key().type() == NativeBlockState.class) { captureOriginal(originals, new DataKey( mutation.key().x(), mutation.key().y(), @@ -99,17 +99,17 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (state == null) { return; } String placementKey = state.deferredPlacementKey(); - PlatformBlockState baseState = state.placementBaseState(); + NativeBlockState baseState = state.placementBaseState(); if (state.isCustom() && placementKey != null && baseState != null) { if (!canSetBlock(x, y, z)) { return; } - DataKey blockKey = new DataKey(x, y, z, PlatformBlockState.class); + DataKey blockKey = new DataKey(x, y, z, NativeBlockState.class); Identifier identifier = Identifier.fromString(placementKey); overlay.put(blockKey, baseState); overlay.put(new DataKey(x, y, z, Identifier.class), identifier); @@ -120,13 +120,13 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { } @Override - public PlatformBlockState get(int x, int y, int z) { - DataKey key = new DataKey(x, y, z, PlatformBlockState.class); + public NativeBlockState get(int x, int y, int z) { + DataKey key = new DataKey(x, y, z, NativeBlockState.class); Object value = overlay.get(key); if (value == CLEARED) { return EngineMantle.AIR.get(); } - return value instanceof PlatformBlockState state + return value instanceof NativeBlockState state ? state : writer.getPrerequisiteBlock(x, y, z); } @@ -138,8 +138,8 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { @Override public boolean isCarved(int x, int y, int z) { - Object block = overlay.get(new DataKey(x, y, z, PlatformBlockState.class)); - if (block instanceof PlatformBlockState state && !state.isAir() && !state.isFluid()) { + Object block = overlay.get(new DataKey(x, y, z, NativeBlockState.class)); + if (block instanceof NativeBlockState state && !state.isAir() && !state.isFluid()) { return false; } Object hydrologyValue = overlay.get(new DataKey(x, y, z, HydrologyCaveCell.class)); @@ -187,14 +187,14 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { if (data == null || y < 0 || y >= worldHeight) { return; } - if (data instanceof PlatformBlockState && !canSetBlock(x, y, z)) { + if (data instanceof NativeBlockState && !canSetBlock(x, y, z)) { return; } if (data instanceof MatterCavern && hasProtectedHydrology(x, y, z)) { return; } - Class type = data instanceof PlatformBlockState ? PlatformBlockState.class : data.getClass(); - if (data instanceof PlatformBlockState) { + Class type = data instanceof NativeBlockState ? NativeBlockState.class : data.getClass(); + if (data instanceof NativeBlockState) { overlay.put(new DataKey(x, y, z, Identifier.class), CLEARED); } DataKey key = new DataKey(x, y, z, type); @@ -227,8 +227,8 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { carved = carved.clone(); } for (int y = 0; y < cappedHeight; y++) { - Object block = overlay.get(new DataKey(x, y, z, PlatformBlockState.class)); - if (block instanceof PlatformBlockState state && !state.isAir() && !state.isFluid()) { + Object block = overlay.get(new DataKey(x, y, z, NativeBlockState.class)); + if (block instanceof NativeBlockState state && !state.isAir() && !state.isFluid()) { carved[y] = 0; continue; } @@ -291,13 +291,13 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { private void rollback(LinkedHashMap originals, Throwable failure) { ArrayList> entries = new ArrayList<>(originals.entrySet()); entries.sort((first, second) -> Boolean.compare( - second.getKey().type() == PlatformBlockState.class, - first.getKey().type() == PlatformBlockState.class + second.getKey().type() == NativeBlockState.class, + first.getKey().type() == NativeBlockState.class )); for (Map.Entry entry : entries) { DataKey key = entry.getKey(); try { - if (key.type() == PlatformBlockState.class + if (key.type() == NativeBlockState.class && writer.restorePrerequisiteCell(key.x(), key.y(), key.z())) { continue; } @@ -349,7 +349,7 @@ final class ObjectDestinationTransaction implements ObjectPassPlacer { } } - record CustomBlockMutation(DataKey key, PlatformBlockState state) implements Mutation { + record CustomBlockMutation(DataKey key, NativeBlockState state) implements Mutation { @Override public int x() { return key.x(); diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarveEnvelope.java b/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarveEnvelope.java deleted file mode 100644 index a2763f281..000000000 --- a/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarveEnvelope.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2022 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.generation.mantle; - -import art.arcane.iris.structure.placement.IrisStructureCarveShape; -import art.arcane.volmlib.util.noise.CNG; - -/** - * Carve envelope math shared by the Iris-assembled structure path and the native (vanilla template) - * structure path so both shrinkwrap a structure footprint the same way. The lobe channel and the - * eroded floor are only sampled by the native path; the Iris-assembled path still carves a uniform - * side reach and a uniform floor cut. - */ -public final class StructureCarveEnvelope { - private static final double CEILING_LOBE_RATIO = 0.5D; - private static final double CEILING_ROLL_FREQUENCY_RATIO = 3D / 7D; - private static final double FLOOR_ROLL_AMPLITUDE = 0.35D; - private static final double FLOOR_ROLL_FREQUENCY_RATIO = 5D / 11D; - // Maps a lobe frequency onto the smooth 2D channel so the auto-derived 0.015 lands near a - // 40 block wavelength: wide enough that walls bulge and recede instead of tracking the footprint. - private static final double LOBE_FREQUENCY_SCALE = 256D; - - private StructureCarveEnvelope() { - } - - /** - * Per-column horizontal carve reach. The lobe channel only ever removes padding, so the boundary - * stays inside the footprint bounds the carve was sized against and never falls below - * {@code horizontalPadding * (1 - lobeStrength)}. - */ - public static double lobedSideReach(CNG lobe, double lobeFrequency, double lobeStrength, - int x, int z, int horizontalPadding) { - if (horizontalPadding <= 0) { - return 0D; - } - return horizontalPadding * lobeFactor(lobe, lobeFrequency, lobeStrength, x, z); - } - - /** - * Ceiling counterpart of {@link #lobedSideReach}, deliberately half as strong so the roof swells - * with the same lobes without collapsing onto the pieces. - */ - public static double lobedUpReach(CNG lobe, double lobeFrequency, double lobeStrength, - int x, int z, double upReach) { - if (upReach <= 0D) { - return upReach; - } - return Math.max(1D, upReach - * lobeFactor(lobe, lobeFrequency, lobeStrength * CEILING_LOBE_RATIO, x, z)); - } - - private static double lobeFactor(CNG lobe, double lobeFrequency, double lobeStrength, - int x, int z) { - double clampedStrength = Math.max(0D, Math.min(1D, lobeStrength)); - if (lobe == null || clampedStrength <= 0D) { - return 1D; - } - double scaledFrequency = lobeFrequency * LOBE_FREQUENCY_SCALE; - double sample = Math.max(0D, Math.min(1D, lobe.fitDouble( - 0D, 1D, x * scaledFrequency, z * scaledFrequency))); - return 1D - clampedStrength * (1D - sample); - } - - public static double erodedUpReach(CNG roll, double frequency, double strength, int x, int z, int head) { - if (head <= 0) { - return 0D; - } - double clampedStrength = Math.max(0D, Math.min(1D, strength)); - if (clampedStrength <= 0D) { - return Math.max(1D, head); - } - double rollFrequency = frequency * CEILING_ROLL_FREQUENCY_RATIO; - double sample = roll.fitDouble(0D, 1D, x * rollFrequency, z * rollFrequency) * 0.7D - + roll.fitDouble(0D, 1D, x * rollFrequency * 3D, z * rollFrequency * 3D) * 0.3D; - double contrast = Math.max(0D, Math.min(1D, (sample - 0.5D) * 2.6D + 0.5D)); - double roundedReach = Math.max(1D, head); - double erodedReach = Math.max(1D, head * (0.4D + 1.4D * contrast)); - return roundedReach + clampedStrength * (erodedReach - roundedReach); - } - - /** - * Floor counterpart of {@link #erodedUpReach}. The amplitude is deliberately small and the result - * never exceeds the configured floor padding, so a padding of zero leaves every column standing on - * its own supporting block instead of undermining the structure. - */ - public static double erodedDownReach(CNG roll, double frequency, double strength, int x, int z, int floorCut) { - if (floorCut <= 0) { - return 0D; - } - double clampedStrength = Math.max(0D, Math.min(1D, strength)); - if (clampedStrength <= 0D) { - return floorCut; - } - double rollFrequency = frequency * FLOOR_ROLL_FREQUENCY_RATIO; - double sample = roll.fitDouble(0D, 1D, x * rollFrequency, z * rollFrequency); - return floorCut * (1D - clampedStrength * FLOOR_ROLL_AMPLITUDE * (1D - sample)); - } - - public static double normalizedVerticalDistance(int y, int sourceMinY, int sourceMaxY, - double upReach, double downReach) { - if (y > sourceMaxY) { - return (y - sourceMaxY) / upReach; - } - if (y < sourceMinY) { - return (sourceMinY - y) / downReach; - } - return 0D; - } - - public static double overboreBoundaryLimit(double noise, double strength) { - double clampedNoise = Math.max(0.0, Math.min(1.0, noise)); - double clampedStrength = Math.max(0.0, Math.min(1.0, strength)); - return 1D - clampedStrength * (1D - clampedNoise); - } - - public static boolean shouldCarveOverboreCell(IrisStructureCarveShape shape, double distanceSquared, - double noise, double strength) { - if (distanceSquared <= 0.0) { - return true; - } - IrisStructureCarveShape resolvedShape = shape == null ? IrisStructureCarveShape.ERODED : shape; - return switch (resolvedShape) { - case BOX -> true; - case ROUNDED -> distanceSquared <= 1D; - case ERODED -> { - double limit = overboreBoundaryLimit(noise, strength); - yield distanceSquared <= 1D && distanceSquared <= limit * limit; - } - }; - } -} diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarvingFootprint.java b/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarvingFootprint.java deleted file mode 100644 index d5d51c4c7..000000000 --- a/core/src/main/java/art/arcane/iris/generation/mantle/StructureCarvingFootprint.java +++ /dev/null @@ -1,456 +0,0 @@ -package art.arcane.iris.generation.mantle; - -import art.arcane.iris.structure.placement.PlacedStructurePiece; -import art.arcane.iris.structure.object.IrisObject; -import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.generation.block.B; -import art.arcane.iris.generation.geometry.IrisBlockVector; -import art.arcane.volmlib.util.collection.KList; -import it.unimi.dsi.fastutil.longs.Long2LongMap; -import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; - -import java.util.Arrays; - -public final class StructureCarvingFootprint { - static final int DEFAULT_MAX_CELLS = 1_048_576; - - private final int minX; - private final int maxX; - private final int minZ; - private final int maxZ; - private final int width; - private final int depth; - private final long[] distanceSquared; - private final int[] nearestSourceIds; - private final int[] sourceMinY; - private final int[] sourceMaxY; - - private StructureCarvingFootprint(FootprintData data) { - minX = data.minX(); - maxX = data.maxX(); - minZ = data.minZ(); - maxZ = data.maxZ(); - width = data.width(); - depth = data.depth(); - distanceSquared = data.distanceSquared(); - nearestSourceIds = data.nearestSourceIds(); - sourceMinY = data.sourceMinY(); - sourceMaxY = data.sourceMaxY(); - } - - static StructureCarvingFootprint from(KList pieces, int padding) { - return from(pieces, padding, DEFAULT_MAX_CELLS); - } - - static StructureCarvingFootprint from(KList pieces, int padding, int maxCells) { - return fromColumns(sink -> emitPieceColumns(pieces, sink), padding, maxCells); - } - - /** - * Builds a footprint from any per-column occupancy source. The source reports one contribution per - * occupied column; overlapping contributions merge into a single column whose vertical extents span - * every contribution. - */ - public static StructureCarvingFootprint fromColumns(ColumnSource source, int padding, int maxCells) { - if (padding < 0) { - throw new IllegalArgumentException("padding must be non-negative"); - } - if (maxCells < 1) { - throw new IllegalArgumentException("maxCells must be positive"); - } - if (source == null) { - return null; - } - - SourceCollection sources = collectSources(source); - if (sources == null || sources.columns().isEmpty()) { - return null; - } - - Bounds bounds = paddedBounds(sources.bounds(), padding, maxCells); - if (bounds == null) { - return null; - } - - int[] nearestSourceIds = new int[bounds.cellCount()]; - Arrays.fill(nearestSourceIds, -1); - int sourceCount = sources.columns().size(); - int[] sourceX = new int[sourceCount]; - int[] sourceZ = new int[sourceCount]; - int[] sourceMinY = new int[sourceCount]; - int[] sourceMaxY = new int[sourceCount]; - int sourceId = 0; - for (Long2LongMap.Entry entry : sources.columns().long2LongEntrySet()) { - int localX = unpackX(entry.getLongKey()) - bounds.minX(); - int localZ = unpackZ(entry.getLongKey()) - bounds.minZ(); - long heights = entry.getLongValue(); - sourceX[sourceId] = localX; - sourceZ[sourceId] = localZ; - sourceMinY[sourceId] = unpackMinY(heights); - sourceMaxY[sourceId] = unpackMaxY(heights); - nearestSourceIds[localZ * bounds.width() + localX] = sourceId; - sourceId++; - } - - transformRows(nearestSourceIds, sourceX, sourceZ, bounds.width(), bounds.depth()); - long[] distanceSquared = transformColumns( - nearestSourceIds, sourceX, sourceZ, bounds.width(), bounds.depth()); - FootprintData data = new FootprintData( - bounds.minX(), bounds.maxX(), bounds.minZ(), bounds.maxZ(), - bounds.width(), bounds.depth(), distanceSquared, nearestSourceIds, - sourceMinY, sourceMaxY); - return new StructureCarvingFootprint(data); - } - - public int minX() { - return minX; - } - - public int maxX() { - return maxX; - } - - public int minZ() { - return minZ; - } - - public int maxZ() { - return maxZ; - } - - public int width() { - return width; - } - - public int depth() { - return depth; - } - - public boolean contains(int worldX, int worldZ) { - return worldX >= minX && worldX <= maxX && worldZ >= minZ && worldZ <= maxZ; - } - - public int indexAt(int worldX, int worldZ) { - if (!contains(worldX, worldZ)) { - return -1; - } - return (worldZ - minZ) * width + worldX - minX; - } - - public long distanceSquaredAt(int worldX, int worldZ) { - return distanceSquaredAt(requireIndex(worldX, worldZ)); - } - - public long distanceSquaredAt(int index) { - return distanceSquared[index]; - } - - public int sourceMinYAt(int worldX, int worldZ) { - return sourceMinYAt(requireIndex(worldX, worldZ)); - } - - public int sourceMinYAt(int index) { - return sourceMinY[nearestSourceIds[index]]; - } - - public int sourceMaxYAt(int worldX, int worldZ) { - return sourceMaxYAt(requireIndex(worldX, worldZ)); - } - - public int sourceMaxYAt(int index) { - return sourceMaxY[nearestSourceIds[index]]; - } - - Column sample(int worldX, int worldZ) { - int index = indexAt(worldX, worldZ); - if (index < 0) { - return null; - } - return new Column(distanceSquaredAt(index), sourceMinYAt(index), sourceMaxYAt(index)); - } - - private static SourceCollection collectSources(ColumnSource source) { - Long2LongOpenHashMap columns = new Long2LongOpenHashMap(); - MutableBounds bounds = new MutableBounds(); - boolean usable = source.emit((worldX, worldZ, minY, maxY) -> { - mergeColumn(columns, worldX, worldZ, minY, maxY); - bounds.include(worldX, worldZ); - }); - if (!usable) { - return null; - } - return columns.isEmpty() ? null : new SourceCollection(columns, bounds.freeze()); - } - - private static boolean emitPieceColumns(KList pieces, ColumnSink sink) { - if (pieces == null) { - return true; - } - for (PlacedStructurePiece piece : pieces) { - if (piece == null) { - continue; - } - IrisObject object = piece.getObject(); - IrisObjectRotation rotation = piece.getRotation(); - if (object == null || rotation == null || object.getBlocks() == null) { - continue; - } - for (IrisBlockVector local : object.getBlocks().keys()) { - if (local == null || B.isAir(object.getBlocks().get(local))) { - continue; - } - IrisBlockVector rotated = rotation.rotate(local.clone()); - long worldX = (long) piece.getX() + rotated.getBlockX(); - long worldY = (long) piece.getY() + rotated.getBlockY(); - long worldZ = (long) piece.getZ() + rotated.getBlockZ(); - if (!fitsInteger(worldX) || !fitsInteger(worldY) || !fitsInteger(worldZ)) { - return false; - } - sink.column((int) worldX, (int) worldZ, (int) worldY, (int) worldY); - } - } - return true; - } - - private static Bounds paddedBounds(MutableBoundsSnapshot sourceBounds, int padding, int maxCells) { - long paddedMinX = (long) sourceBounds.minX() - padding; - long paddedMaxX = (long) sourceBounds.maxX() + padding; - long paddedMinZ = (long) sourceBounds.minZ() - padding; - long paddedMaxZ = (long) sourceBounds.maxZ() + padding; - if (!fitsInteger(paddedMinX) || !fitsInteger(paddedMaxX) - || !fitsInteger(paddedMinZ) || !fitsInteger(paddedMaxZ)) { - return null; - } - long width = paddedMaxX - paddedMinX + 1L; - long depth = paddedMaxZ - paddedMinZ + 1L; - if (width < 1L || depth < 1L || width > Integer.MAX_VALUE || depth > Integer.MAX_VALUE - || width > maxCells / depth) { - return null; - } - int cellCount = (int) (width * depth); - return new Bounds( - (int) paddedMinX, (int) paddedMaxX, (int) paddedMinZ, (int) paddedMaxZ, - (int) width, (int) depth, cellCount); - } - - private static void mergeColumn(Long2LongOpenHashMap columns, int x, int z, int minY, int maxY) { - long key = pack(x, z); - if (!columns.containsKey(key)) { - columns.put(key, packHeights(minY, maxY)); - return; - } - long existing = columns.get(key); - columns.put(key, packHeights( - Math.min(minY, unpackMinY(existing)), Math.max(maxY, unpackMaxY(existing)))); - } - - private static void transformRows(int[] nearestSourceIds, int[] sourceX, int[] sourceZ, - int width, int depth) { - for (int z = 0; z < depth; z++) { - int rowStart = z * width; - int previousSource = -1; - for (int x = 0; x < width; x++) { - int index = rowStart + x; - int sourceId = nearestSourceIds[index]; - if (isExplicitSource(sourceId, x, z, sourceX, sourceZ)) { - previousSource = sourceId; - } else { - nearestSourceIds[index] = previousSource; - } - } - int nextSource = -1; - for (int x = width - 1; x >= 0; x--) { - int index = rowStart + x; - int currentSource = nearestSourceIds[index]; - if (isExplicitSource(currentSource, x, z, sourceX, sourceZ)) { - nextSource = currentSource; - } - if (isBetterHorizontalSource(nextSource, currentSource, x, sourceX)) { - nearestSourceIds[index] = nextSource; - } - } - } - } - - private static long[] transformColumns(int[] nearestSourceIds, int[] sourceX, int[] sourceZ, - int width, int depth) { - long[] distanceSquared = new long[nearestSourceIds.length]; - int[] siteRows = new int[depth]; - int[] siteSourceIds = new int[depth]; - int[] starts = new int[depth]; - long[] siteCosts = new long[depth]; - for (int x = 0; x < width; x++) { - int siteCount = buildEnvelope( - nearestSourceIds, sourceX, width, depth, x, - siteRows, siteSourceIds, starts, siteCosts); - int siteIndex = 0; - for (int z = 0; z < depth; z++) { - while (siteIndex + 1 < siteCount && starts[siteIndex + 1] <= z) { - siteIndex++; - } - int sourceId = siteSourceIds[siteIndex]; - int index = z * width + x; - long deltaX = (long) x - sourceX[sourceId]; - long deltaZ = (long) z - sourceZ[sourceId]; - nearestSourceIds[index] = sourceId; - distanceSquared[index] = deltaX * deltaX + deltaZ * deltaZ; - } - } - return distanceSquared; - } - - private static int buildEnvelope(int[] nearestSourceIds, int[] sourceX, int width, int depth, - int x, int[] siteRows, int[] siteSourceIds, - int[] starts, long[] siteCosts) { - int siteCount = 0; - for (int z = 0; z < depth; z++) { - int sourceId = nearestSourceIds[z * width + x]; - if (sourceId < 0) { - continue; - } - long deltaX = (long) x - sourceX[sourceId]; - long cost = deltaX * deltaX; - int start = Integer.MIN_VALUE; - while (siteCount > 0) { - start = firstStrictlyBetterAt( - siteRows[siteCount - 1], siteCosts[siteCount - 1], z, cost); - if (start > starts[siteCount - 1]) { - break; - } - siteCount--; - } - if (siteCount == 0) { - start = Integer.MIN_VALUE; - } - siteRows[siteCount] = z; - siteSourceIds[siteCount] = sourceId; - siteCosts[siteCount] = cost; - starts[siteCount] = start; - siteCount++; - } - return siteCount; - } - - private static int firstStrictlyBetterAt(int existingRow, long existingCost, - int candidateRow, long candidateCost) { - long numerator = candidateCost + (long) candidateRow * candidateRow - - existingCost - (long) existingRow * existingRow; - long denominator = 2L * (candidateRow - existingRow); - long start = Math.floorDiv(numerator, denominator) + 1L; - if (start < Integer.MIN_VALUE) { - return Integer.MIN_VALUE; - } - if (start > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } - return (int) start; - } - - private static boolean isExplicitSource(int sourceId, int x, int z, - int[] sourceX, int[] sourceZ) { - return sourceId >= 0 && sourceX[sourceId] == x && sourceZ[sourceId] == z; - } - - private static boolean isBetterHorizontalSource(int candidateId, int currentId, - int x, int[] sourceX) { - if (candidateId < 0) { - return false; - } - if (currentId < 0) { - return true; - } - long candidateDelta = (long) x - sourceX[candidateId]; - long currentDelta = (long) x - sourceX[currentId]; - long candidateDistance = candidateDelta * candidateDelta; - long currentDistance = currentDelta * currentDelta; - return candidateDistance < currentDistance - || candidateDistance == currentDistance && sourceX[candidateId] < sourceX[currentId]; - } - - private int requireIndex(int worldX, int worldZ) { - int index = indexAt(worldX, worldZ); - if (index < 0) { - throw new IndexOutOfBoundsException( - "coordinate outside carving footprint: " + worldX + "," + worldZ); - } - return index; - } - - private static boolean fitsInteger(long value) { - return value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE; - } - - private static long pack(int x, int z) { - return (long) x << 32 | z & 0xffffffffL; - } - - private static int unpackX(long packed) { - return (int) (packed >> 32); - } - - private static int unpackZ(long packed) { - return (int) packed; - } - - private static long packHeights(int minY, int maxY) { - return (long) minY << 32 | maxY & 0xffffffffL; - } - - private static int unpackMinY(long packed) { - return (int) (packed >> 32); - } - - private static int unpackMaxY(long packed) { - return (int) packed; - } - - @FunctionalInterface - public interface ColumnSink { - void column(int worldX, int worldZ, int minY, int maxY); - } - - @FunctionalInterface - public interface ColumnSource { - /** - * Reports every occupied column to the sink. Returning false abandons the footprint, which is how - * a source rejects occupancy it cannot address (for example coordinates outside the integer range). - */ - boolean emit(ColumnSink sink); - } - - record Column(long distanceSquared, int sourceMinY, int sourceMaxY) { - } - - private record Bounds(int minX, int maxX, int minZ, int maxZ, - int width, int depth, int cellCount) { - } - - private record FootprintData(int minX, int maxX, int minZ, int maxZ, - int width, int depth, long[] distanceSquared, - int[] nearestSourceIds, int[] sourceMinY, int[] sourceMaxY) { - } - - private record SourceCollection(Long2LongOpenHashMap columns, MutableBoundsSnapshot bounds) { - } - - private record MutableBoundsSnapshot(int minX, int maxX, int minZ, int maxZ) { - } - - private static final class MutableBounds { - private int minX = Integer.MAX_VALUE; - private int maxX = Integer.MIN_VALUE; - private int minZ = Integer.MAX_VALUE; - private int maxZ = Integer.MIN_VALUE; - - private void include(int x, int z) { - minX = Math.min(minX, x); - maxX = Math.max(maxX, x); - minZ = Math.min(minZ, z); - maxZ = Math.max(maxZ, z); - } - - private MutableBoundsSnapshot freeze() { - return new MutableBoundsSnapshot(minX, maxX, minZ, maxZ); - } - } -} diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/StructureFoundationPlanner.java b/core/src/main/java/art/arcane/iris/generation/mantle/StructureFoundationPlanner.java index a9ee61915..765c095bc 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/StructureFoundationPlanner.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/StructureFoundationPlanner.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.mantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import java.util.function.IntConsumer; @@ -13,7 +13,7 @@ final class StructureFoundationPlanner { } static void recordBaseCell(Long2IntOpenHashMap columns, int x, int y, int z, - PlatformBlockState state, boolean supportNonOccluding) { + NativeBlockState state, boolean supportNonOccluding) { if (columns == null || state == null || !(supportNonOccluding ? state.isSolid() : state.isOccluding())) { return; @@ -38,7 +38,7 @@ final class StructureFoundationPlanner { return NO_GROUND; } - static boolean isGroundSolid(PlatformBlockState overlay, boolean carved, int mantleY, int terrainHeight) { + static boolean isGroundSolid(NativeBlockState overlay, boolean carved, int mantleY, int terrainHeight) { if (mantleY <= 0) { return true; } @@ -48,7 +48,7 @@ final class StructureFoundationPlanner { return !carved && mantleY <= terrainHeight; } - static boolean isSurfaceSupportBoundary(PlatformBlockState overlay, boolean carved, + static boolean isSurfaceSupportBoundary(NativeBlockState overlay, boolean carved, int mantleY, int terrainHeight) { return carved || isGroundSolid(overlay, false, mantleY, terrainHeight); } diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/StructurePieceColumns.java b/core/src/main/java/art/arcane/iris/generation/mantle/StructurePieceColumns.java new file mode 100644 index 000000000..09453617e --- /dev/null +++ b/core/src/main/java/art/arcane/iris/generation/mantle/StructurePieceColumns.java @@ -0,0 +1,59 @@ +package art.arcane.iris.generation.mantle; + +import art.arcane.iris.structure.placement.PlacedStructurePiece; +import art.arcane.iris.structure.object.IrisObject; +import art.arcane.iris.structure.object.IrisObjectRotation; +import art.arcane.iris.generation.block.B; +import art.arcane.iris.generation.geometry.IrisBlockVector; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.structure.StructureCarvingFootprint; +import art.arcane.volmlib.util.structure.StructureCarvingFootprint.ColumnSink; + +final class StructurePieceColumns { + private static final int DEFAULT_MAX_CELLS = 1_048_576; + + private StructurePieceColumns() { + } + + static StructureCarvingFootprint from(KList pieces, int padding) { + return from(pieces, padding, DEFAULT_MAX_CELLS); + } + + static StructureCarvingFootprint from(KList pieces, int padding, int maxCells) { + return StructureCarvingFootprint.fromColumns(sink -> emitPieceColumns(pieces, sink), padding, maxCells); + } + + private static boolean emitPieceColumns(KList pieces, ColumnSink sink) { + if (pieces == null) { + return true; + } + for (PlacedStructurePiece piece : pieces) { + if (piece == null) { + continue; + } + IrisObject object = piece.getObject(); + IrisObjectRotation rotation = piece.getRotation(); + if (object == null || rotation == null || object.getBlocks() == null) { + continue; + } + for (IrisBlockVector local : object.getBlocks().keys()) { + if (local == null || B.isAir(object.getBlocks().get(local))) { + continue; + } + IrisBlockVector rotated = rotation.rotate(local.clone()); + long worldX = (long) piece.getX() + rotated.getBlockX(); + long worldY = (long) piece.getY() + rotated.getBlockY(); + long worldZ = (long) piece.getZ() + rotated.getBlockZ(); + if (!fitsInteger(worldX) || !fitsInteger(worldY) || !fitsInteger(worldZ)) { + return false; + } + sink.column((int) worldX, (int) worldZ, (int) worldY, (int) worldY); + } + } + return true; + } + + private static boolean fitsInteger(long value) { + return value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE; + } +} diff --git a/core/src/main/java/art/arcane/iris/generation/mantle/TerrainMatterView.java b/core/src/main/java/art/arcane/iris/generation/mantle/TerrainMatterView.java index 353184d71..7a5d2b416 100644 --- a/core/src/main/java/art/arcane/iris/generation/mantle/TerrainMatterView.java +++ b/core/src/main/java/art/arcane/iris/generation/mantle/TerrainMatterView.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.mantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.world.storage.matter.PreObjectMatterCell; import art.arcane.volmlib.util.function.Consumer4; @@ -103,7 +103,7 @@ public final class TerrainMatterView { } private static boolean hasJournal(Class type) { - return type == MatterCavern.class || type == PlatformBlockState.class || type == String.class + return type == MatterCavern.class || type == NativeBlockState.class || type == String.class || type == HydrologyCaveCell.class; } diff --git a/core/src/main/java/art/arcane/iris/generation/noise/IrisExpression.java b/core/src/main/java/art/arcane/iris/generation/noise/IrisExpression.java index 77476bca6..cdb5e67a1 100644 --- a/core/src/main/java/art/arcane/iris/generation/noise/IrisExpression.java +++ b/core/src/main/java/art/arcane/iris/generation/noise/IrisExpression.java @@ -23,7 +23,7 @@ import com.dfsek.paralithic.Expression; import com.dfsek.paralithic.eval.parser.Parser; import com.dfsek.paralithic.eval.parser.Scope; import art.arcane.iris.pack.loading.IrisRegistrant; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.noise.IrisExpressionFunction.FunctionContext; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/generation/noise/IrisExpressionLoad.java b/core/src/main/java/art/arcane/iris/generation/noise/IrisExpressionLoad.java index 45afd9221..d624ce293 100644 --- a/core/src/main/java/art/arcane/iris/generation/noise/IrisExpressionLoad.java +++ b/core/src/main/java/art/arcane/iris/generation/noise/IrisExpressionLoad.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.runtime.IrisEngineStreamType; import art.arcane.iris.generation.runtime.IrisEngineValueType; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.cache.LazyBoundedCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/generation/noise/IrisGenerator.java b/core/src/main/java/art/arcane/iris/generation/noise/IrisGenerator.java index 6e859ad06..8d6de0f48 100644 --- a/core/src/main/java/art/arcane/iris/generation/noise/IrisGenerator.java +++ b/core/src/main/java/art/arcane/iris/generation/noise/IrisGenerator.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.noise; import art.arcane.iris.pack.loading.IrisRegistrant; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.schema.annotation.ArrayType; diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/BlockUpdater.java b/core/src/main/java/art/arcane/iris/generation/runtime/BlockUpdater.java index 70916b3c0..a1df6c438 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/BlockUpdater.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/BlockUpdater.java @@ -18,9 +18,9 @@ package art.arcane.iris.generation.runtime; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public interface BlockUpdater { - void catchBlockUpdates(int x, int y, int z, PlatformBlockState data); + void catchBlockUpdates(int x, int y, int z, NativeBlockState data); } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/DimensionStackLayout.java b/core/src/main/java/art/arcane/iris/generation/runtime/DimensionStackLayout.java index ffcee24ed..8efbc6a90 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/DimensionStackLayout.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/DimensionStackLayout.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.generation.terrain.Terrain3DColumn; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import java.util.ArrayList; import java.util.Collections; @@ -369,9 +369,9 @@ public final class DimensionStackLayout { DimensionTerrainContext terrainContext, IrisBiome biome, IrisRegion region, - PlatformBlockState rockBlock, - PlatformBlockState fluidBlock, - PlatformBlockState surfaceBlock, + NativeBlockState rockBlock, + NativeBlockState fluidBlock, + NativeBlockState surfaceBlock, int normalTerrainHeight, int fluidHeight, Terrain3DColumn terrainColumn @@ -382,9 +382,9 @@ public final class DimensionStackLayout { DimensionTerrainContext terrainContext, IrisBiome biome, IrisRegion region, - PlatformBlockState rockBlock, - PlatformBlockState fluidBlock, - PlatformBlockState surfaceBlock, + NativeBlockState rockBlock, + NativeBlockState fluidBlock, + NativeBlockState surfaceBlock, int localBaseY, int normalTerrainHeight, int fluidHeight, diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/DimensionTerrainContext.java b/core/src/main/java/art/arcane/iris/generation/runtime/DimensionTerrainContext.java index 6287f897a..9474de2d2 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/DimensionTerrainContext.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/DimensionTerrainContext.java @@ -15,7 +15,7 @@ import art.arcane.iris.generation.noise.IrisShapedGeneratorStyle; import art.arcane.iris.generation.terrain.Terrain3DColumn; import art.arcane.iris.generation.terrain.Terrain3DRuntime; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.DataProvider; import art.arcane.volmlib.util.interpolation.NoiseBounds; import art.arcane.volmlib.util.stream.ProceduralStream; @@ -43,7 +43,7 @@ public final class DimensionTerrainContext implements DataProvider { private final ProceduralStream fluidHeightStream; private final ProceduralStream biomeStream; private final ProceduralStream regionStream; - private final ProceduralStream rockStream; + private final ProceduralStream rockStream; private final FluidBlockSampler fluidBlockSampler; private final IrisImageMapRuntime imageMapRuntime; private final boolean selfReferencing; @@ -400,12 +400,12 @@ public final class DimensionTerrainContext implements DataProvider { : GenerationStreams.cache2DDouble(ProceduralStream.ofDouble((x, z) -> shapedHeight(terrain3D, x, z)), cachePrefix + "Terrain3DHeightStream", engine, cacheSize); ProceduralStream slopeStream = GenerationStreams.cache2DDouble(shapedHeightStream.slope(3), cachePrefix + "SlopeStream", engine, cacheSize); - ProceduralStream rockStream = dimension.getRockPalette() + ProceduralStream rockStream = dimension.getRockPalette() .getLayerGenerator(rng.nextParallelRNG(45), dimensionData).stream() .select(dimension.getRockPalette().getBlockData(dimensionData)); FluidBlockSampler fluidBlockSampler; if (includeFluid) { - ProceduralStream fluidStream = dimension.getFluidPalette() + ProceduralStream fluidStream = dimension.getFluidPalette() .getLayerGenerator(rng.nextParallelRNG(78), dimensionData).stream() .select(dimension.getFluidPalette().getBlockData(dimensionData)); fluidBlockSampler = fluidStream::get; @@ -493,13 +493,13 @@ public final class DimensionTerrainContext implements DataProvider { return mappedBiome == null ? proceduralBiome : mappedBiome; } - static PlatformBlockState mappedSurfaceBlock( + static NativeBlockState mappedSurfaceBlock( IrisImageMapRuntime imageMapRuntime, - PlatformBlockState proceduralBlock, + NativeBlockState proceduralBlock, double worldX, double worldZ ) { - PlatformBlockState mappedBlock = imageMapRuntime.sampleSurfaceBlock(worldX, worldZ); + NativeBlockState mappedBlock = imageMapRuntime.sampleSurfaceBlock(worldX, worldZ); return mappedBlock == null ? proceduralBlock : mappedBlock; } @@ -722,22 +722,22 @@ public final class DimensionTerrainContext implements DataProvider { return regionStream == null ? null : regionStream.get(x, z); } - public PlatformBlockState getRockBlock(double x, double z) { + public NativeBlockState getRockBlock(double x, double z) { return rockStream.get(x, z); } - public PlatformBlockState getFluidBlock(double x, double z) { + public NativeBlockState getFluidBlock(double x, double z) { return getFluidBlock(x, z, usesNaturalFallback(x, z)); } - private PlatformBlockState getFluidBlock(double x, double z, boolean naturalFallback) { + private NativeBlockState getFluidBlock(double x, double z, boolean naturalFallback) { if (naturalFallback && selfFallback != null) { return selfFallback.fluidBlockSampler().sample(x, z); } return fluidBlockSampler.sample(x, z); } - public PlatformBlockState getSurfaceBlock(double x, double z) { + public NativeBlockState getSurfaceBlock(double x, double z) { return imageMapRuntime.sampleSurfaceBlock(x, z); } @@ -779,7 +779,7 @@ public final class DimensionTerrainContext implements DataProvider { } private interface FluidBlockSampler { - PlatformBlockState sample(double x, double z); + NativeBlockState sample(double x, double z); } private record ContextState( @@ -792,7 +792,7 @@ public final class DimensionTerrainContext implements DataProvider { ProceduralStream fluidHeightStream, ProceduralStream biomeStream, ProceduralStream regionStream, - ProceduralStream rockStream, + ProceduralStream rockStream, FluidBlockSampler fluidBlockSampler, IrisImageMapRuntime imageMapRuntime, boolean selfReferencing, @@ -815,9 +815,9 @@ public final class DimensionTerrainContext implements DataProvider { double fluidHeight, IrisBiome biome, IrisRegion region, - PlatformBlockState rockBlock, - PlatformBlockState fluidBlock, - PlatformBlockState surfaceBlock, + NativeBlockState rockBlock, + NativeBlockState fluidBlock, + NativeBlockState surfaceBlock, Terrain3DColumn terrainColumn ) { } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/Engine.java b/core/src/main/java/art/arcane/iris/generation/runtime/Engine.java index 80631fa0f..619481bc4 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/Engine.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/Engine.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.world.history.ChunkGenerationSemantics; import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.structure.placement.PlacedObject; import art.arcane.iris.structure.placement.StructurePlacementMarker; @@ -49,8 +49,8 @@ import art.arcane.iris.world.IrisWorld; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveStorage; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.iris.generation.context.ChunkContext; @@ -251,7 +251,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer, } @BlockCoordinates - void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore) throws WrongEngineBroException; + void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore) throws WrongEngineBroException; EngineMetrics getMetrics(); @@ -542,7 +542,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer, @BlockCoordinates @Override - default void catchBlockUpdates(int x, int y, int z, PlatformBlockState data) { + default void catchBlockUpdates(int x, int y, int z, NativeBlockState data) { if (data == null) { return; } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/EngineDecorator.java b/core/src/main/java/art/arcane/iris/generation/runtime/EngineDecorator.java index 893cfbcb9..fa2e8455f 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/EngineDecorator.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/EngineDecorator.java @@ -19,7 +19,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.hunk.Hunk; @@ -27,15 +27,15 @@ import art.arcane.volmlib.util.hunk.Hunk; public interface EngineDecorator extends EngineComponent { @BlockCoordinates - void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk data, IrisBiome biome, int height, int max); + void decorate(int x, int z, int realX, int realX1, int realX_1, int realZ, int realZ1, int realZ_1, Hunk data, IrisBiome biome, int height, int max); @BlockCoordinates - default void decorate(int x, int z, int realX, int realZ, Hunk data, IrisBiome biome, int height, int max) { + default void decorate(int x, int z, int realX, int realZ, Hunk data, IrisBiome biome, int height, int max) { decorate(x, z, realX, realX, realX, realZ, realZ, realZ, data, biome, height, max); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") - default boolean canGoOn(PlatformBlockState decorant, PlatformBlockState atop) { + default boolean canGoOn(NativeBlockState decorant, NativeBlockState atop) { if (atop == null || atop.isAir()) { return false; } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/EngineMode.java b/core/src/main/java/art/arcane/iris/generation/runtime/EngineMode.java index d74432cb3..5c3facd60 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/EngineMode.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/EngineMode.java @@ -27,8 +27,8 @@ import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.iris.generation.concurrent.BurstExecutor; import art.arcane.iris.generation.concurrent.MultiBurst; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; public interface EngineMode extends Staged { KList getTerrainStages(); @@ -37,7 +37,7 @@ public interface EngineMode extends Staged { EngineStage getTransitionStage(); - default void generateTerrain(int x, int z, Hunk blocks, Hunk biomes, + default void generateTerrain(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, ChunkContext context) { context.setTerrainBiomeOutput(biomes); for (EngineStage stage : getTerrainStages()) { @@ -183,7 +183,7 @@ public interface EngineMode extends Staged { } @BlockCoordinates - default void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, long generationSessionId) { + default void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, long generationSessionId) { boolean cacheContext = !getEngine().getPlatformHooks().shouldDisableChunkContextCache(getEngine()); ChunkContext.PrefillPlan prefillPlan = cacheContext ? ChunkContext.PrefillPlan.NO_CAVE : ChunkContext.PrefillPlan.NONE; ChunkContext ctx = new ChunkContext( diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/EnginePlatformHooks.java b/core/src/main/java/art/arcane/iris/generation/runtime/EnginePlatformHooks.java index 4d6de685a..44661074d 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/EnginePlatformHooks.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/EnginePlatformHooks.java @@ -18,7 +18,7 @@ package art.arcane.iris.generation.runtime; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.world.history.SavedTerrainChunk; diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/EngineStage.java b/core/src/main/java/art/arcane/iris/generation/runtime/EngineStage.java index e5c6ebe7c..c751415c4 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/EngineStage.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/EngineStage.java @@ -18,15 +18,15 @@ package art.arcane.iris.generation.runtime; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.hunk.Hunk; public interface EngineStage { @BlockCoordinates - void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, ChunkContext context); + void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, ChunkContext context); default void close() { if (this instanceof EngineComponent c) { diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/GenerationSessionLease.java b/core/src/main/java/art/arcane/iris/generation/runtime/GenerationSessionLease.java index 002adbc90..099cfc400 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/GenerationSessionLease.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/GenerationSessionLease.java @@ -1,6 +1,8 @@ package art.arcane.iris.generation.runtime; -public final class GenerationSessionLease implements AutoCloseable { +import art.arcane.volmlib.nativelib.terrain.NativeGenerationLease; + +public final class GenerationSessionLease implements NativeGenerationLease { private static final GenerationSessionLease NOOP = new GenerationSessionLease(null, null, 0L, null); private final GenerationSessionManager manager; diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java b/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java index 0249ad90e..ed39c825c 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/IrisComplex.java @@ -66,8 +66,8 @@ import art.arcane.iris.generation.terrain.Terrain3DRuntime; import art.arcane.iris.generation.terrain.HydrologyBankTerrainRuntime; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.generation.block.DataProvider; import art.arcane.volmlib.util.math.M; @@ -174,7 +174,7 @@ public class IrisComplex implements DataProvider { private ProceduralStream naturalTrueBiomeStream; private ProceduralStream unblendedNaturalTrueBiomeStream; private ProceduralStream trueBiomeStream; - private ProceduralStream trueBiomeDerivativeStream; + private ProceduralStream trueBiomeDerivativeStream; private ProceduralStream naturalHeightStream; private ProceduralStream baseTerrainHeightStream; @Getter(AccessLevel.NONE) @@ -208,9 +208,9 @@ public class IrisComplex implements DataProvider { private ProceduralStream seaSurfaceDecoration; private ProceduralStream seaFloorDecoration; private ProceduralStream shoreSurfaceDecoration; - private ProceduralStream rockStream; - private ProceduralStream fluidStream; - private Map> hydrologyFluidStreams; + private ProceduralStream rockStream; + private ProceduralStream fluidStream; + private Map> hydrologyFluidStreams; private final BiomeBuffetLayout biomeBuffet; @Getter(AccessLevel.NONE) private final Map biomeBuffetGenerators; @@ -858,11 +858,11 @@ public class IrisComplex implements DataProvider { return false; } - private Map> createHydrologyFluidStreams(IrisHydrology hydrology) { + private Map> createHydrologyFluidStreams(IrisHydrology hydrology) { if (hydrology == null) { return Map.of(); } - LinkedHashMap> streams = new LinkedHashMap<>(); + LinkedHashMap> streams = new LinkedHashMap<>(); IrisRiverHydrology rivers = Objects.requireNonNull(hydrology.getRivers(), "hydrology.rivers"); int streamIndex = 0; for (IrisRiverProfile profile : rivers.getProfiles()) { @@ -870,7 +870,7 @@ public class IrisComplex implements DataProvider { throw new IllegalArgumentException("hydrology.rivers.profiles cannot contain null entries"); } String profileKey = requireHydrologyKey(profile.getId(), "hydrology.rivers.profiles[].id"); - ProceduralStream stream = configuredFluidStream( + ProceduralStream stream = configuredFluidStream( profile.getFluidPalette(), rng.nextParallelRNG(7900 + streamIndex++), "hydrology river profile " + profileKey @@ -884,7 +884,7 @@ public class IrisComplex implements DataProvider { throw new IllegalArgumentException("hydrology.deepFluids cannot contain null entries"); } String profileKey = requireHydrologyKey(deepFluid.getId(), "hydrology.deepFluids[].id"); - ProceduralStream stream = configuredFluidStream( + ProceduralStream stream = configuredFluidStream( deepFluid.getFluidPalette(), rng.nextParallelRNG(8900 + streamIndex++), "hydrology deep-fluid profile " + profileKey @@ -898,7 +898,7 @@ public class IrisComplex implements DataProvider { throw new IllegalArgumentException("hydrology.surfacePools cannot contain null entries"); } String profileKey = requireHydrologyKey(pool.getId(), "hydrology.surfacePools[].id"); - ProceduralStream stream = configuredFluidStream( + ProceduralStream stream = configuredFluidStream( pool.getFluidPalette(), rng.nextParallelRNG(9900 + streamIndex++), "hydrology surface pool " + profileKey @@ -917,18 +917,18 @@ public class IrisComplex implements DataProvider { return key.trim(); } - private ProceduralStream configuredFluidStream( + private ProceduralStream configuredFluidStream( IrisMaterialPalette palette, RNG fluidRng, String configurationName ) { Objects.requireNonNull(palette, configurationName + " fluidPalette must be configured"); - KList blocks = palette.getBlockData(data); + KList blocks = palette.getBlockData(data); if (blocks.isEmpty()) { throw new IllegalArgumentException( configurationName + " fluidPalette must resolve at least one fluid block"); } - for (PlatformBlockState block : blocks) { + for (NativeBlockState block : blocks) { if (block == null || !block.isFluid()) { throw new IllegalArgumentException( configurationName + " fluidPalette may contain only fluid blocks"); @@ -937,16 +937,16 @@ public class IrisComplex implements DataProvider { return palette.getLayerGenerator(fluidRng, data).stream().select(blocks); } - public PlatformBlockState resolveHydrologyFluid(String profileKey, double x, double z) { + public NativeBlockState resolveHydrologyFluid(String profileKey, double x, double z) { String key = Objects.requireNonNull(profileKey, "profileKey").trim(); - ProceduralStream stream = hydrologyFluidStreams.get(key); + ProceduralStream stream = hydrologyFluidStreams.get(key); if (stream == null) { throw new IllegalArgumentException("Unknown hydrology fluid profile: " + key); } return stream.get(x, z); } - public PlatformBlockState resolveSurfaceFluid(double x, double z) { + public NativeBlockState resolveSurfaceFluid(double x, double z) { HydrologyColumnLayer layer = surfaceFluidLayer(x, z); if (layer != null) { return resolveHydrologyFluid(layer.profileKey(), x, z); @@ -1294,7 +1294,7 @@ public class IrisComplex implements DataProvider { continue; } - PlatformBlockState block = i.getBlockData(b, rngc, x, z, data); + NativeBlockState block = i.getBlockData(b, rngc, x, z, data); if (block != null) { return i; diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngine.java b/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngine.java index b1bf6f2dd..0ca8c6263 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngine.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngine.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.generation.runtime.EngineBackgroundTasks.BackgroundTaskDrain; import art.arcane.iris.generation.runtime.EngineRuntimeBuilder.RuntimeAssembly; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.structure.nativegen.NativeStructureVolumeMemo; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.structure.nativegen.NativeStructureOwnershipStore; @@ -44,8 +44,8 @@ import art.arcane.iris.structure.StructureIndexService; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.block.B; import java.util.function.Consumer; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.atomics.AtomicRollingSequence; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.IrisContext; @@ -1497,7 +1497,7 @@ public class IrisEngine implements Engine { @BlockCoordinates @Override - public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) throws WrongEngineBroException { + public void generate(int x, int z, Hunk vblocks, Hunk vbiomes, boolean multicore) throws WrongEngineBroException { awaitGenerationCacheWarm(); try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openGenerationHistoryCoordinateScope(x, z); @@ -1505,10 +1505,10 @@ public class IrisEngine implements Engine { IrisContext.Scope generationScope = IrisContext.open(this, lease.sessionId(), null)) { getEngineData().getStatistics().generatedChunk(); PrecisionStopwatch p = PrecisionStopwatch.start(); - Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t)); + Hunk blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t)); if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) { - PlatformBlockState crossSection = B.getState("CRYING_OBSIDIAN"); + NativeBlockState crossSection = B.getState("CRYING_OBSIDIAN"); for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { blocks.set(i, 0, j, crossSection); diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngineMantle.java b/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngineMantle.java index 2666a36b4..b2d7f6161 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngineMantle.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/IrisEngineMantle.java @@ -21,7 +21,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.world.WorldMaintenance; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.mantle.MantleComponent; import art.arcane.iris.generation.mantle.MantlePass; @@ -33,7 +33,7 @@ import art.arcane.iris.generation.mantle.IrisStructureComponent; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.world.storage.matter.IrisMatterContext; import art.arcane.iris.world.storage.matter.IrisMatterSupport; import art.arcane.volmlib.util.collection.KList; @@ -337,8 +337,8 @@ public class IrisEngineMantle implements EngineMantle { @Override public Class classifyValue(Object value) { - if (value instanceof PlatformBlockState) { - return PlatformBlockState.class; + if (value instanceof NativeBlockState) { + return NativeBlockState.class; } return IrisPlatforms.get().classifyMantleValue(value); } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/ResolvedTerrainProvider.java b/core/src/main/java/art/arcane/iris/generation/runtime/ResolvedTerrainProvider.java index 5fd604a1b..a975ce738 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/ResolvedTerrainProvider.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/ResolvedTerrainProvider.java @@ -9,8 +9,8 @@ import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.world.history.TransitionGenerationPlan; import art.arcane.iris.world.history.TransitionFluidContainment; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.IrisContext; @@ -54,8 +54,8 @@ public final class ResolvedTerrainProvider { return ignoreFluid ? column.oceanFloorHeight() : column.surfaceHeight(); } - public void generate(EngineMode mode, int x, int z, Hunk blocks, - Hunk biomes, boolean multicore, ChunkContext context) { + public void generate(EngineMode mode, int x, int z, Hunk blocks, + Hunk biomes, boolean multicore, ChunkContext context) { SavedTerrainChunk terrain; if (context.getComplex().getTransitionGenerationPlan() != null && context.getComplex().getTransitionGenerationPlan().hasTransitionAtChunk(x >> 4, z >> 4)) { @@ -197,8 +197,8 @@ public final class ResolvedTerrainProvider { ChunkContext context = new ChunkContext(x, z, engine.getComplex(), sessionId, true, ChunkContext.PrefillPlan.NATURAL_TERRAIN, engine.getMetrics(), engine.getDimensionStackContext()); context.beginSpeculativeTerrain(); - Hunk blocks = Hunk.newArrayHunk(16, engine.getHeight(), 16); - Hunk biomes = Hunk.newArrayHunk(16, engine.getHeight(), 16); + Hunk blocks = Hunk.newArrayHunk(16, engine.getHeight(), 16); + Hunk biomes = Hunk.newArrayHunk(16, engine.getHeight(), 16); try (IrisContext.Scope ignored = IrisContext.open(engine, sessionId, context)) { engine.getMode().generateTerrain(x, z, blocks, biomes, false, context); return new ResolvedTerrain(capture(x, z, blocks, biomes, context, false), context.getFloatingBiomes()); @@ -208,8 +208,8 @@ public final class ResolvedTerrainProvider { } } - private SavedTerrainChunk capture(int x, int z, Hunk blocks, - Hunk biomes, ChunkContext context, boolean boundaryOnly) { + private SavedTerrainChunk capture(int x, int z, Hunk blocks, + Hunk biomes, ChunkContext context, boolean boundaryOnly) { FloatingBiomeOverlay floating = context.getFloatingBiomes(); if (floating != null) { floating.retainHighestSurfaces((localX, localZ) -> highestSolid(blocks, localX, localZ)); @@ -221,9 +221,9 @@ public final class ResolvedTerrainProvider { } } - private static int highestSolid(Hunk blocks, int localX, int localZ) { + private static int highestSolid(Hunk blocks, int localX, int localZ) { for (int y = blocks.getHeight() - 1; y >= 0; y--) { - PlatformBlockState state = blocks.getRaw(localX, y, localZ); + NativeBlockState state = blocks.getRaw(localX, y, localZ); if (state != null && !state.isAir() && !state.isFluid()) { return y; } @@ -231,18 +231,18 @@ public final class ResolvedTerrainProvider { return -1; } - private static void copy(SavedTerrainChunk terrain, Hunk blocks, - Hunk biomes, ChunkContext context) { + private static void copy(SavedTerrainChunk terrain, Hunk blocks, + Hunk biomes, ChunkContext context) { PlatformRegistries registries = IrisPlatforms.get().registries(); - Map states = new HashMap<>(); - Map physicalBiomes = new HashMap<>(); + Map states = new HashMap<>(); + Map physicalBiomes = new HashMap<>(); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { TerrainBoundarySignature column = terrain.column((terrain.chunkX() << 4) + x, (terrain.chunkZ() << 4) + z); BoundaryColumnGeometry geometry = column.geometry(); for (int offset = 0; offset < blocks.getHeight(); offset++) { String stateKey = geometry.voxelAt(geometry.minimumY() + offset).stateKey(); - PlatformBlockState state = states.computeIfAbsent(stateKey, registries::blockOrNull); + NativeBlockState state = states.computeIfAbsent(stateKey, registries::blockOrNull); if (state == null) { throw new IllegalStateException("Resolved terrain requires unavailable block state " + stateKey); } diff --git a/core/src/main/java/art/arcane/iris/generation/runtime/UpperDimensionContext.java b/core/src/main/java/art/arcane/iris/generation/runtime/UpperDimensionContext.java index 3a0f670b1..b89604aee 100644 --- a/core/src/main/java/art/arcane/iris/generation/runtime/UpperDimensionContext.java +++ b/core/src/main/java/art/arcane/iris/generation/runtime/UpperDimensionContext.java @@ -24,7 +24,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.generation.terrain.Terrain3DColumn; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.DataProvider; import art.arcane.volmlib.util.stream.ProceduralStream; @@ -77,9 +77,9 @@ public class UpperDimensionContext implements DataProvider { imageMapRuntime, proceduralBiome, worldX, worldZ); } - static PlatformBlockState mappedSurfaceBlock( + static NativeBlockState mappedSurfaceBlock( IrisImageMapRuntime imageMapRuntime, - PlatformBlockState proceduralBlock, + NativeBlockState proceduralBlock, double worldX, double worldZ ) { @@ -139,11 +139,11 @@ public class UpperDimensionContext implements DataProvider { return terrainContext.getRegion(x, z); } - public PlatformBlockState getRockBlock(int x, int z) { + public NativeBlockState getRockBlock(int x, int z) { return terrainContext.getRockBlock(x, z); } - public PlatformBlockState getSurfaceBlock(int x, int z) { + public NativeBlockState getSurfaceBlock(int x, int z) { return terrainContext.getSurfaceBlock(x, z); } diff --git a/core/src/main/java/art/arcane/iris/generation/stage/HydrologyFluidLayerSelector.java b/core/src/main/java/art/arcane/iris/generation/stage/HydrologyFluidLayerSelector.java index 96f707bc4..0ca4a9446 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/HydrologyFluidLayerSelector.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/HydrologyFluidLayerSelector.java @@ -1,16 +1,16 @@ package art.arcane.iris.generation.stage; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; final class HydrologyFluidLayerSelector { private HydrologyFluidLayerSelector() { } - static PlatformBlockState select( - KList seaLayers, + static NativeBlockState select( + KList seaLayers, int depth, - PlatformBlockState fluid, + NativeBlockState fluid, boolean hydrologyOwned ) { if (!hydrologyOwned && seaLayers != null && seaLayers.hasIndex(depth)) { diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisBiomeActuator.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisBiomeActuator.java index de31ddb43..a0f13d4a6 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisBiomeActuator.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisBiomeActuator.java @@ -29,7 +29,7 @@ import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.ChunkedDataCache; import art.arcane.volmlib.util.hunk.Hunk; @@ -46,7 +46,7 @@ import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import java.util.List; import java.util.Objects; -public class IrisBiomeActuator extends EngineAssignedActuator { +public class IrisBiomeActuator extends EngineAssignedActuator { private final RNG rng; private final KMap resolvedBiomes = new KMap<>(); private final KMap resolvedPhysicalBiomes = new KMap<>(); @@ -58,7 +58,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { @BlockCoordinates @Override - public void onActuate(int x, int z, Hunk h, boolean multicore, ChunkContext context) { + public void onActuate(int x, int z, Hunk h, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); int width = h.getWidth(); int depth = h.getDepth(); @@ -82,11 +82,11 @@ public class IrisBiomeActuator extends EngineAssignedActuator { 0, worldZ )); - PlatformBiome platformBiome = resolved.biome(); + NativeBiome platformBiome = resolved.biome(); writeColumn(h, mantle, xf, zf, worldX, worldZ, height, resolved); if (dimensionStackContext != null) { - PlatformBiome bottomBiome = platformBiome == null + NativeBiome bottomBiome = platformBiome == null ? h.getRaw(xf, 0, zf) : platformBiome; applyDimensionStackBiomes( @@ -107,7 +107,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { engine.getMetrics().getBiome().put(p.getMilliseconds()); } - public static void publishNaturalMetadata(Engine engine, int x, int z, Hunk biomes, + public static void publishNaturalMetadata(Engine engine, int x, int z, Hunk biomes, ChunkContext context) { if (context.isSpeculativeTerrain() || !context.getComplex().allowsMantleChunkWrite(x >> 4, z >> 4)) { return; @@ -121,7 +121,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { for (int localX = 0; localX < biomes.getWidth(); localX += 4) { for (int localZ = 0; localZ < biomes.getDepth(); localZ += 4) { for (int y = 0; y < biomes.getHeight(); y += 4) { - PlatformBiome biome = biomes.getRaw(localX, y, localZ); + NativeBiome biome = biomes.getRaw(localX, y, localZ); if (biome != null) { mantle.set(x + localX, y, z + localZ, BiomeInjectMatter.get(biome.key())); } @@ -135,7 +135,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { } private void writeHistoricalColumn( - Hunk output, + Hunk output, Mantle mantle, int localX, int localZ, @@ -182,7 +182,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { } private void writeColumn( - Hunk output, + Hunk output, Mantle mantle, int localX, int localZ, @@ -204,7 +204,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { int localZ, int worldX, int worldZ, - Hunk output, + Hunk output, Mantle mantle, Engine engine, ResolvedBiome bottom, @@ -266,7 +266,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { int worldZ, int minimumY, int maximumY, - Hunk output, + Hunk output, Mantle mantle, ResolvedBiome resolved ) { @@ -326,7 +326,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { } IrisPlatform platform = IrisPlatforms.get(); - PlatformBiome biome = platform.registries().biome(key); + NativeBiome biome = platform.registries().biome(key); ResolvedBiome resolved = new ResolvedBiome( biome, BiomeInjectMatter.get(platform.biomeWriter().biomeIdFor(key)) @@ -342,7 +342,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator { if (cached != null) { return cached; } - PlatformBiome biome = IrisPlatforms.get().registries().biome(key); + NativeBiome biome = IrisPlatforms.get().registries().biome(key); ResolvedBiome resolved = new ResolvedBiome(biome, BiomeInjectMatter.get(key)); if (biome != null) { resolvedPhysicalBiomes.put(key, resolved); @@ -350,6 +350,6 @@ public class IrisBiomeActuator extends EngineAssignedActuator { return resolved; } - private record ResolvedBiome(PlatformBiome biome, MatterBiomeInject matter) { + private record ResolvedBiome(NativeBiome biome, MatterBiomeInject matter) { } } diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java index e6e8d1bd8..5b05d8f63 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisCarveModifier.java @@ -54,12 +54,12 @@ import art.arcane.volmlib.util.matter.slices.MarkerMatter; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Data; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import java.util.HashMap; import java.util.Map; -public class IrisCarveModifier extends EngineAssignedModifier { +public class IrisCarveModifier extends EngineAssignedModifier { private static final byte LIQUID_FLUID = 1; private static final ThreadLocal SCRATCH = ThreadLocal.withInitial(IrisCarveScratch::new); private static final int CAVE_BIOME_BLEND_RADIUS = 3; @@ -68,8 +68,8 @@ public class IrisCarveModifier extends EngineAssignedModifier output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch caveStopwatch = PrecisionStopwatch.start(); Mantle mantle = getEngine().getMantle().getMantle(); IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State(); @@ -173,7 +173,7 @@ public class IrisCarveModifier extends EngineAssignedModifier= column.minY() && y < column.topY() && !column.isSolid(y)) { @@ -375,7 +375,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, + Hunk output, ChunkContext chunkContext, IrisCarveScratch scratch, CarveColumnMask[] columnMasks, @@ -587,7 +587,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, + Hunk output, MantleChunk mc, Mantle mantle, CarveColumnMask columnMask, @@ -642,7 +642,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, + Hunk output, MantleChunk mantleChunk, CarveColumnMask boundaryMask, CarveWallBuffer walls, @@ -683,7 +683,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, + Hunk output, MantleChunk mantleChunk, CarveWallBuffer walls, int rx, @@ -710,7 +710,7 @@ public class IrisCarveModifier extends EngineAssignedModifier floorLayers = floorBiome.generateLayers( + KList floorLayers = floorBiome.generateLayers( getDimension(), worldX, worldZ, rng, 3, zoneFloor, getData(), getComplex()); for (int i = 0; i < zoneFloor - 1; i++) { if (!floorLayers.hasIndex(i)) { @@ -727,8 +727,8 @@ public class IrisCarveModifier extends EngineAssignedModifier ceilingLayers = ceilingBiome.generateCeilingLayers( + KList ceilingLayers = ceilingBiome.generateCeilingLayers( getDimension(), worldX, worldZ, rng, 3, zoneCeiling, getData(), getComplex()); for (int i = 0; i < ceilingLayers.size(); i++) { int ceilingY = zoneCeiling + i + 1; @@ -763,11 +763,11 @@ public class IrisCarveModifier extends EngineAssignedModifier output, MantleChunk mc, Mantle mantle, + private void processZone(Hunk output, MantleChunk mc, Mantle mantle, CaveZone zone, int rx, int rz, int xx, int zz, IrisDimensionCarvingResolver.State resolverState, Long2ObjectOpenHashMap caveBiomeCache, @@ -818,7 +818,7 @@ public class IrisCarveModifier extends EngineAssignedModifier floorBlocks = floorBiome.generateLayers(getDimension(), xx, zz, rng, 3, zone.floor, getData(), getComplex()); + KList floorBlocks = floorBiome.generateLayers(getDimension(), xx, zz, rng, 3, zone.floor, getData(), getComplex()); for (int i = 0; i < zone.floor - 1; i++) { if (!floorBlocks.hasIndex(i)) { break; @@ -830,13 +830,13 @@ public class IrisCarveModifier extends EngineAssignedModifier ceilingBlocks = ceilingBiome.generateCeilingLayers(getDimension(), xx, zz, rng, 3, zone.ceiling, getData(), getComplex()); + KList ceilingBlocks = ceilingBiome.generateCeilingLayers(getDimension(), xx, zz, rng, 3, zone.ceiling, getData(), getComplex()); for (int i = 0; i < ceilingBlocks.size(); i++) { int cy = zone.ceiling + i + 1; if (cy >= maxY) { @@ -863,8 +863,8 @@ public class IrisCarveModifier extends EngineAssignedModifier output) { + public void decorateNaturalCaves(int blockX, int blockZ, Hunk output) { Mantle mantle = getEngine().getMantle().getMantle(); MantleChunk chunk = mantle.getChunk(blockX >> 4, blockZ >> 4).use(); IrisDimensionCarvingResolver.State resolver = new IrisDimensionCarvingResolver.State(); @@ -895,7 +895,7 @@ public class IrisCarveModifier extends EngineAssignedModifier= 0) { CaveZone zone = new CaveZone(); @@ -929,7 +929,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, CaveZone zone, + private void decorateZone(Hunk output, CaveZone zone, int rx, int rz, int xx, int zz, IrisBiome floorBiome, IrisBiome ceilingBiome) { int maxY = output.getHeight(); IrisDecorator[] surfaceDecorators = floorBiome == null @@ -949,7 +949,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, + Hunk output, MantleChunk mantleChunk, CaveZone zone, int localX, @@ -967,13 +967,13 @@ public class IrisCarveModifier extends EngineAssignedModifier output, int x, int y, int z, PlatformBlockState layer) { + static boolean canReplaceCaveFloorLayer(Hunk output, int x, int y, int z, NativeBlockState layer) { return !isGravityAffected(layer) || y > 0 && B.isSolid(output.getRaw(x, y - 1, z)); } - static PlatformBlockState resolveSubmergedCaveFloorLayer( - Hunk output, + static NativeBlockState resolveSubmergedCaveFloorLayer( + Hunk output, int x, int y, int z, - PlatformBlockState layer, + NativeBlockState layer, HydrologyCaveCell hydrologyAbove ) { if (hydrologyAbove == null || !hydrologyAbove.isWet() || !isVegetatedHydrologyBed(layer)) { @@ -1071,7 +1071,7 @@ public class IrisCarveModifier extends EngineAssignedModifier= minimumY; substrateY--) { - PlatformBlockState substrate = output.getRaw(x, substrateY, z); + NativeBlockState substrate = output.getRaw(x, substrateY, z); if (B.isSolid(substrate) && !B.isFluid(substrate) && !isVegetatedHydrologyBed(substrate) @@ -1082,7 +1082,7 @@ public class IrisCarveModifier extends EngineAssignedModifier output, int x, int floorY, int z) { + static boolean hasStableCaveFloorSupport(Hunk output, int x, int floorY, int z) { if (floorY <= 0) { return false; } - PlatformBlockState support = output.getRaw(x, floorY - 1, z); + NativeBlockState support = output.getRaw(x, floorY - 1, z); if (!B.isSolid(support)) { return false; } return !isGravityAffected(support) || floorY > 1 && B.isSolid(output.getRaw(x, floorY - 2, z)); } - static boolean isGravityAffected(PlatformBlockState state) { + static boolean isGravityAffected(NativeBlockState state) { return IrisProceduralBlocks.isGravityAffected(state); } diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisCustomModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisCustomModifier.java index c386ddfe6..e7b148d2f 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisCustomModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisCustomModifier.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.stage; import art.arcane.iris.integration.Identifier; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineAssignedModifier; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.concurrent.BurstExecutor; import art.arcane.iris.generation.concurrent.MultiBurst; import art.arcane.iris.generation.context.ChunkContext; @@ -12,13 +12,13 @@ import art.arcane.volmlib.util.mantle.flag.MantleFlag; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; import art.arcane.volmlib.util.matter.Matter; -public class IrisCustomModifier extends EngineAssignedModifier { +public class IrisCustomModifier extends EngineAssignedModifier { public IrisCustomModifier(Engine engine) { super(engine, "Custom"); } @Override - public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { MantleChunk mc = getEngine().getMantle().getMantle().getChunk(x >> 4, z >> 4); if (!mc.isFlagged(MantleFlag.CUSTOM_ACTIVE)) { return; @@ -35,12 +35,12 @@ public class IrisCustomModifier extends EngineAssignedModifier { for (int rX = 0; rX < output.getWidth(); rX++) { for (int rZ = 0; rZ < output.getDepth(); rZ++) { - PlatformBlockState b = output.get(rX, finalY, rZ); + NativeBlockState b = output.get(rX, finalY, rZ); if (b == null || !b.isCustom()) { continue; } String placementKey = b.deferredPlacementKey(); - PlatformBlockState baseState = b.placementBaseState(); + NativeBlockState baseState = b.placementBaseState(); if (placementKey == null || baseState == null) { continue; } diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisDecorantActuator.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisDecorantActuator.java index d6d196ea2..657b04dfb 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisDecorantActuator.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisDecorantActuator.java @@ -37,13 +37,13 @@ import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.Getter; import java.util.function.Predicate; -public class IrisDecorantActuator extends EngineAssignedActuator { - private static final Predicate PREDICATE_SOLID = (s) -> s != null && !B.isAirOrFluid(s); +public class IrisDecorantActuator extends EngineAssignedActuator { + private static final Predicate PREDICATE_SOLID = (s) -> s != null && !B.isAirOrFluid(s); private final RNG rng; @Getter private final IrisSurfaceDecorator surfaceDecorator; @@ -87,7 +87,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator output, + Hunk output, int x, int z, int height, @@ -99,7 +99,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator output, + Hunk output, int x, int z, int lowerY, @@ -119,13 +119,13 @@ public class IrisDecorantActuator extends EngineAssignedActuator output, + Hunk output, int x, int y, int z, - PlatformBlockState original + NativeBlockState original ) { - PlatformBlockState placed = output.get(x, y, z); + NativeBlockState placed = output.get(x, y, z); if (placed != original && IrisSurfaceDecorator.isAquaticPlacement(placed)) { output.set(x, y, z, original); } @@ -148,7 +148,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator output, boolean multicore, ChunkContext context) { + public void onActuate(int x, int z, Hunk output, boolean multicore, ChunkContext context) { if (!getEngine().getDimension().isDecorate()) { return; } @@ -184,7 +184,7 @@ public class IrisDecorantActuator extends EngineAssignedActuator output, Terrain3DColumn column, + private void decorateTerrainLedges(Hunk output, Terrain3DColumn column, IrisBiome biome, int localX, int localZ, int worldX, int worldZ) { for (int span = 0; span + 1 < column.spanCount(); span++) { int floorY = column.floor(span); diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisDepositModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisDepositModifier.java index b4b212d07..a317b9c57 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisDepositModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisDepositModifier.java @@ -42,14 +42,14 @@ import art.arcane.volmlib.util.matter.MatterCavern; import art.arcane.iris.generation.concurrent.BurstExecutor; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.VectorMap; import java.util.ArrayList; import java.util.List; -public class IrisDepositModifier extends EngineAssignedModifier { +public class IrisDepositModifier extends EngineAssignedModifier { private static final int CLUMPS_PER_BATCH = 8; private static final int PREPARATION_BATCH_COUNT = 4; @@ -61,13 +61,13 @@ public class IrisDepositModifier extends EngineAssignedModifier output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); generateDeposits(output, Math.floorDiv(x, 16), Math.floorDiv(z, 16), multicore, context); getEngine().getMetrics().getDeposit().put(p.getMilliseconds()); } - public void generateDeposits(Hunk terrain, int x, int z, boolean multicore, ChunkContext context) { + public void generateDeposits(Hunk terrain, int x, int z, boolean multicore, ChunkContext context) { IrisRegion region = context.getRegion().get(7, 7); IrisBiome biome = context.getBiome().get(7, 7); List generators = new ArrayList<>( @@ -123,7 +123,7 @@ public class IrisDepositModifier extends EngineAssignedModifier terrain, int x, int z, PreparationContext preparation) { + Hunk terrain, int x, int z, PreparationContext preparation) { burst.complete(); Throwable failure = preparation.takeFailure(null); if (failure instanceof Error error) { @@ -158,11 +158,11 @@ public class IrisDepositModifier extends EngineAssignedModifier data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) { + public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk data, RNG rng, int cx, int cz, boolean safe, ChunkContext context) { generate(k, chunk, data, rng, cx, cz, safe, null, context); } - public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) { + public void generate(IrisDepositGenerator k, MantleChunk chunk, Hunk data, RNG rng, int cx, int cz, boolean safe, HeightMap he, ChunkContext context) { DepositPlan plan = plan(k, rng); for (int first = 0; first < plan.attempts(); first += CLUMPS_PER_BATCH) { int limit = Math.min(plan.attempts(), first + CLUMPS_PER_BATCH); @@ -262,7 +262,7 @@ public class IrisDepositModifier extends EngineAssignedModifier data, + private void place(PreparedDeposit deposit, MantleChunk chunk, Hunk data, int cx, int cz, HeightMap he, ChunkContext context) { if (deposit.clumps().isEmpty()) { return; @@ -278,7 +278,7 @@ public class IrisDepositModifier extends EngineAssignedModifier.Cursor cursor = clump.getBlocks().cursor(); + VectorMap.Cursor cursor = clump.getBlocks().cursor(); while (cursor.next()) { IrisBlockVector j = cursor.key(); int nx = j.getBlockX() + x; @@ -299,7 +299,7 @@ public class IrisDepositModifier extends EngineAssignedModifier data, int x, int y, int z, int columnSurface) { + Hunk data, int x, int y, int z, int columnSurface) { return y == columnSurface || isExteriorAirAt(data, x - 1, y, z) || isExteriorAirAt(data, x + 1, y, z) @@ -419,7 +419,7 @@ public class IrisDepositModifier extends EngineAssignedModifier data, int x, int y, int z) { + static boolean isAdjacentToAir(Hunk data, int x, int y, int z) { return isAirAt(data, x - 1, y, z) || isAirAt(data, x + 1, y, z) || isAirAt(data, x, y - 1, z) @@ -428,23 +428,23 @@ public class IrisDepositModifier extends EngineAssignedModifier data, int x, int y, int z) { + private static boolean isAirAt(Hunk data, int x, int y, int z) { if (x < 0 || x >= data.getWidth() || y < 0 || y >= data.getHeight() || z < 0 || z >= data.getDepth()) { return false; } - PlatformBlockState state = data.getRaw(x, y, z); + NativeBlockState state = data.getRaw(x, y, z); return state == null || state.isAir(); } - private static boolean isExteriorAirAt(Hunk data, int x, int y, int z) { + private static boolean isExteriorAirAt(Hunk data, int x, int y, int z) { if (x < 0 || x >= data.getWidth() || y < 0 || y >= data.getHeight() || z < 0 || z >= data.getDepth()) { return false; } - PlatformBlockState state = data.getRaw(x, y, z); + NativeBlockState state = data.getRaw(x, y, z); return state == null || (state.isAir() && !"minecraft:cave_air".equals(IrisProceduralBlocks.materialKey(state))); @@ -460,14 +460,14 @@ public class IrisDepositModifier extends EngineAssignedModifier variants, PlatformBlockState ore, int y) { + private NativeBlockState matchDepositVariant(java.util.List variants, NativeBlockState ore, int y) { if (variants == null || variants.isEmpty()) { return null; } @@ -501,7 +501,7 @@ public class IrisDepositModifier extends EngineAssignedModifier { +public final class IrisDimensionStackActuator extends EngineAssignedActuator { private static final BoundBlockState AIR = BoundBlockState.of("AIR"); private static final BoundBlockState BEDROCK = BoundBlockState.of("BEDROCK"); private static final List> REPLACED_METADATA = List.of( @@ -75,7 +75,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator blocks, + Hunk blocks, boolean multicore, ChunkContext context ) { @@ -129,7 +129,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator blocks, + Hunk blocks, DimensionStackLayout layout, MetadataCleaner metadata ) { @@ -149,7 +149,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator output, + Hunk output, DimensionStackLayout.Layer lower, DimensionStackLayout.Layer upper, MetadataCleaner metadata @@ -170,7 +170,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator output, + Hunk output, DimensionStackLayout.Layer layer, MetadataCleaner metadata ) { @@ -180,7 +180,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator surfaceBlocks = biome == null || layer.terrainColumn() != null + KList surfaceBlocks = biome == null || layer.terrainColumn() != null ? null : biome.generateLayersWithSlope( dimension, @@ -193,7 +193,7 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator seaBlocks = biome == null || fluidDepth == 0 + KList seaBlocks = biome == null || fluidDepth == 0 ? null : biome.generateSeaLayers(worldX, worldZ, rng, fluidDepth, data); @@ -241,12 +241,12 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator output, + Hunk output, MetadataCleaner metadata, int x, int y, int z, - PlatformBlockState block + NativeBlockState block ) { metadata.clear(x, y, z); output.setRaw(x, y, z, block); diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisFloatingChildBiomeModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisFloatingChildBiomeModifier.java index 3c49392d1..41c648e0f 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisFloatingChildBiomeModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisFloatingChildBiomeModifier.java @@ -47,30 +47,30 @@ import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.matter.slices.BiomeInjectMatter; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.world.history.TransitionGenerationPlan; import art.arcane.iris.world.history.FloatingBiomeOverlay; import java.util.Optional; import java.util.IdentityHashMap; -public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier { +public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier { public static final long FLOATING_BASE_SEED_SALT = 0x5EED_F107_00F1B10CL; private static final Runnable NOOP_DECORATION_MISS = () -> { }; private final RNG rng; private final EngineDecorator seaSurfaceDecorator; - private static KList generateBottomPaletteLayers(IrisFloatingChildBiomes entry, IrisDimension dimension, double wx, double wz, RNG random, int paletteDepth, IrisData data, IrisComplex complex) { + private static KList generateBottomPaletteLayers(IrisFloatingChildBiomes entry, IrisDimension dimension, double wx, double wz, RNG random, int paletteDepth, IrisData data, IrisComplex complex) { if (entry == null || entry.getBottomPaletteMode() != FloatingBottomPaletteMode.CUSTOM || entry.getBottomPalette() == null || entry.getBottomPalette().isEmpty()) { return null; } return generatePaletteLayers(dimension, entry.getBottomPalette(), wx, wz, random.nextParallelRNG(0xB0770B), paletteDepth, data, complex); } - private static KList generatePaletteLayers(IrisDimension dimension, KList layers, double wx, double wz, RNG random, int maxDepth, IrisData data, IrisComplex complex) { - KList generated = new KList<>(); + private static KList generatePaletteLayers(IrisDimension dimension, KList layers, double wx, double wz, RNG random, int maxDepth, IrisData data, IrisComplex complex) { + KList generated = new KList<>(); if (layers == null || layers.isEmpty() || maxDepth <= 0) { return generated; } @@ -110,7 +110,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier topBlocks, KList bottomBlocks, int topDepth, int bottomDepth, PlatformBlockState fallbackSolid) { + private static NativeBlockState selectPaletteBlock(IrisFloatingChildBiomes entry, KList topBlocks, KList bottomBlocks, int topDepth, int bottomDepth, NativeBlockState fallbackSolid) { FloatingBottomPaletteMode mode = entry == null || entry.getBottomPaletteMode() == null ? FloatingBottomPaletteMode.DEPTH : entry.getBottomPaletteMode(); if (mode == FloatingBottomPaletteMode.MIRROR_TOP) { return paletteBlock(topBlocks, Math.min(topDepth, bottomDepth), fallbackSolid); @@ -168,11 +168,11 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier blocks, int depth, PlatformBlockState fallbackSolid) { + private static NativeBlockState paletteBlock(KList blocks, int depth, NativeBlockState fallbackSolid) { if (blocks == null || blocks.isEmpty()) { return fallbackSolid; } - PlatformBlockState block = blocks.hasIndex(depth) ? blocks.get(depth) : blocks.getLast(); + NativeBlockState block = blocks.hasIndex(depth) ? blocks.get(depth) : blocks.getLast(); return block == null ? fallbackSolid : block; } @@ -180,20 +180,20 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier topBlocks = target == null ? null : target.generateLayers(dimension, wx, wz, layerRng, paletteDepth, paletteDepth, data, complex); + KList topBlocks = target == null ? null : target.generateLayers(dimension, wx, wz, layerRng, paletteDepth, paletteDepth, data, complex); if (topBlocks == null || topBlocks.isEmpty()) { topBlocks = parent.generateLayers(dimension, wx, wz, layerRng, paletteDepth, paletteDepth, data, complex); } - KList bottomBlocks = generateBottomPaletteLayers(entry, dimension, wx, wz, layerRng, paletteDepth, data, complex); + KList bottomBlocks = generateBottomPaletteLayers(entry, dimension, wx, wz, layerRng, paletteDepth, data, complex); return new PaletteContext(topBlocks, bottomBlocks, B.getState("minecraft:stone")); } private static final class PaletteContext { - private final KList topBlocks; - private final KList bottomBlocks; - private final PlatformBlockState fallbackSolid; + private final KList topBlocks; + private final KList bottomBlocks; + private final NativeBlockState fallbackSolid; - private PaletteContext(KList topBlocks, KList bottomBlocks, PlatformBlockState fallbackSolid) { + private PaletteContext(KList topBlocks, KList bottomBlocks, NativeBlockState fallbackSolid) { this.topBlocks = topBlocks; this.bottomBlocks = bottomBlocks; this.fallbackSolid = fallbackSolid; @@ -207,7 +207,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); int chunkHeight = output.getHeight(); IrisData data = getData(); @@ -251,7 +251,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier 0) { - PlatformBlockState fluid = B.getStateOrNull(entry.getFluidBlock()); + NativeBlockState fluid = B.getStateOrNull(entry.getFluidBlock()); if (fluid == null) { fluid = B.getState("minecraft:water"); } @@ -305,7 +305,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier output, boolean multicore, ChunkContext context) { + public void decorateColumns(int x, int z, Hunk output, boolean multicore, ChunkContext context) { int chunkHeight = output.getHeight(); IrisData data = getData(); IrisComplex complex = getComplex(); @@ -337,7 +337,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier historical = transition.historicalPhysicalBiomeKeyAt(wx, y + getEngine().getMinHeight(), wz); @@ -441,7 +441,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier { +public class IrisPerfectionModifier extends EngineAssignedModifier { private static final BoundBlockState AIR = BoundBlockState.of("AIR"); private static final BoundBlockState WATER = BoundBlockState.of("WATER"); private static final Map ORE_BASES = buildOreBases(); @@ -74,14 +74,14 @@ public class IrisPerfectionModifier extends EngineAssignedModifier output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); if (getDimension().isHideOresForHiddenOre()) { hideOres(output, multicore); @@ -103,7 +103,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier= 0; k--) { - PlatformBlockState b = output.get(finalI, k, j); + NativeBlockState b = output.get(finalI, k, j); if (IrisSpeleothems.isSpike(b)) { b = normalizeSpike(b, output, finalI, j, k, AIR.get(), WATER.get()); } @@ -121,7 +121,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier output, - int x, int z, int y, PlatformBlockState air, PlatformBlockState water) { + static NativeBlockState normalizeSpike(NativeBlockState state, Hunk output, + int x, int z, int y, NativeBlockState air, NativeBlockState water) { if (IrisSpeleothems.isSupported(state, output, x, z, y)) { IrisSpeleothems.finishAtTip(output, x, z, y); return output.get(x, y, z); @@ -177,7 +177,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier= 0 && nextY < output.getHeight()) { - PlatformBlockState current = output.get(x, nextY, z); + NativeBlockState current = output.get(x, nextY, z); if (!IrisSpeleothems.isSpike(current) || !material.equals(IrisProceduralBlocks.materialKey(current)) || !direction.equals(IrisProceduralBlocks.propertyValue(current, "vertical_direction"))) { @@ -188,7 +188,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier= 0 && nextY < output.getHeight()) { - PlatformBlockState retained = output.get(x, nextY, z); + NativeBlockState retained = output.get(x, nextY, z); if (IrisSpeleothems.isSpike(retained) && material.equals(IrisProceduralBlocks.materialKey(retained)) && !direction.equals(IrisProceduralBlocks.propertyValue(retained, "vertical_direction"))) { @@ -198,7 +198,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier output, boolean multicore) { + private void hideOres(Hunk output, boolean multicore) { BurstExecutor burst = burst().burst(multicore); int height = output.getHeight(); for (int i = 0; i < 16; i++) { @@ -206,7 +206,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier { for (int j = 0; j < 16; j++) { for (int k = height - 1; k >= 0; k--) { - PlatformBlockState block = output.get(finalI, k, j); + NativeBlockState block = output.get(finalI, k, j); if (block == null) { continue; } @@ -221,9 +221,9 @@ public class IrisPerfectionModifier extends EngineAssignedModifier output, int x, int z) { + private int getHeight(Hunk output, int x, int z) { for (int i = output.getHeight() - 1; i >= 0; i--) { - PlatformBlockState b = output.get(x, i, z); + NativeBlockState b = output.get(x, i, z); if (b != null) { if (!B.isAir(b) && !B.isFluid(b)) { diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisPostModifier.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisPostModifier.java index 80efafdfa..a78507822 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisPostModifier.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisPostModifier.java @@ -32,9 +32,9 @@ import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; -public class IrisPostModifier extends EngineAssignedModifier { +public class IrisPostModifier extends EngineAssignedModifier { private static final BoundBlockState AIR = BoundBlockState.of("AIR"); private final RNG rng; @@ -45,11 +45,11 @@ public class IrisPostModifier extends EngineAssignedModifier } @Override - public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { + public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); // The post stage runs sequentially on production (multicore false); an uncontended // monitor per probe is still a monitor times ~10k probes per chunk. - Hunk sync = multicore ? output.synchronize() : output; + Hunk sync = multicore ? output.synchronize() : output; int width = output.getWidth(); int depth = output.getDepth(); int planeWidth = width + 2; @@ -109,7 +109,7 @@ public class IrisPostModifier extends EngineAssignedModifier return heights; } - private void post(int currentPostX, int currentPostZ, Hunk currentData, int x, int z, ChunkContext context, int[] heights, int planeWidth, boolean walls, boolean slabs) { + private void post(int currentPostX, int currentPostZ, Hunk currentData, int x, int z, ChunkContext context, int[] heights, int planeWidth, boolean walls, boolean slabs) { // x/z are world coordinates, the hunk is indexed relative to this chunk origin. int originX = x - currentPostX; int originZ = z - currentPostZ; @@ -157,8 +157,8 @@ public class IrisPostModifier extends EngineAssignedModifier } if (g >= 4) { - PlatformBlockState bcState = getPostBlock(x, h, z, originX, originZ, currentData); - PlatformBlockState bState = getPostBlock(x, h + 1, z, originX, originZ, currentData); + NativeBlockState bcState = getPostBlock(x, h, z, originX, originZ, currentData); + NativeBlockState bState = getPostBlock(x, h + 1, z, originX, originZ, currentData); if (bState.isOccluding() && bState.isSolid()) { if (bcState.isSolid()) { @@ -175,10 +175,10 @@ public class IrisPostModifier extends EngineAssignedModifier g += hd == h + 1 ? 1 : 0; if (g >= 4) { - PlatformBlockState ba = getPostBlock(x, ha, z, originX, originZ, currentData); - PlatformBlockState bb = getPostBlock(x, hb, z, originX, originZ, currentData); - PlatformBlockState bc = getPostBlock(x, hc, z, originX, originZ, currentData); - PlatformBlockState bd = getPostBlock(x, hd, z, originX, originZ, currentData); + NativeBlockState ba = getPostBlock(x, ha, z, originX, originZ, currentData); + NativeBlockState bb = getPostBlock(x, hb, z, originX, originZ, currentData); + NativeBlockState bc = getPostBlock(x, hc, z, originX, originZ, currentData); + NativeBlockState bd = getPostBlock(x, hd, z, originX, originZ, currentData); g = 0; g = B.isSolid(ba) ? g + 1 : g; g = B.isSolid(bb) ? g + 1 : g; @@ -204,7 +204,7 @@ public class IrisPostModifier extends EngineAssignedModifier int max = Math.abs(Math.max(h - ha, Math.max(h - hb, Math.max(h - hc, h - hd)))); for (int i = h; i > h - max; i--) { - PlatformBlockState d = biome.getWall().get(rng, x + i, i + h, z + i, getData()); + NativeBlockState d = biome.getWall().get(rng, x + i, i + h, z + i, getData()); if (d != null) { if (isAirOrWater(x, i, z, originX, originZ, currentData)) { @@ -233,7 +233,7 @@ public class IrisPostModifier extends EngineAssignedModifier //@done { IrisSlopeClip sc = biome.getSlab().getSlopeCondition(); - PlatformBlockState d = sc.isValid(getComplex().getSlopeStream().get(x, z)) ? biome.getSlab().get(rng, x, h, z, getData()) : null; + NativeBlockState d = sc.isValid(getComplex().getSlopeStream().get(x, z)) ? biome.getSlab().get(rng, x, h, z, getData()) : null; if (d != null) { boolean cancel = B.isAir(d); @@ -255,7 +255,7 @@ public class IrisPostModifier extends EngineAssignedModifier } // Waterlogging - PlatformBlockState b = getPostBlock(x, h, z, originX, originZ, currentData); + NativeBlockState b = getPostBlock(x, h, z, originX, originZ, currentData); if (IrisProceduralBlocks.hasProperty(b, "waterlogged")) { boolean w = false; @@ -277,7 +277,7 @@ public class IrisPostModifier extends EngineAssignedModifier b = getPostBlock(x, h + 1, z, originX, originZ, currentData); if (B.isVineBlock(b)) { - PlatformBlockState result = b; + NativeBlockState result = b; int finalH = h + 1; for (String face : IrisProceduralBlocks.FACE_PROPERTIES) { @@ -285,7 +285,7 @@ public class IrisPostModifier extends EngineAssignedModifier continue; } int[] mod = IrisProceduralBlocks.faceOffset(face); - PlatformBlockState d = getPostBlock(x + mod[0], finalH + mod[1], z + mod[2], originX, originZ, currentData); + NativeBlockState d = getPostBlock(x + mod[0], finalH + mod[1], z + mod[2], originX, originZ, currentData); result = result.withProperty(face, String.valueOf(!B.isAir(d) && !B.isVineBlock(d))); } if (!result.equals(b)) { @@ -294,7 +294,7 @@ public class IrisPostModifier extends EngineAssignedModifier } if (B.isFoliage(b) || IrisProceduralBlocks.materialKey(b).equals("minecraft:dead_bush")) { - PlatformBlockState onto = getPostBlock(x, h, z, originX, originZ, currentData); + NativeBlockState onto = getPostBlock(x, h, z, originX, originZ, currentData); if (!B.canPlaceOnto(b, onto) && !B.isDecorant(b)) { setPostBlock(x, h + 1, z, AIR.get(), originX, originZ, currentData); @@ -302,53 +302,53 @@ public class IrisPostModifier extends EngineAssignedModifier } } - public boolean isAir(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isAir(int x, int y, int z, int originX, int originZ, Hunk currentData) { String material = IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)); return material.equals("minecraft:air") || material.equals("minecraft:cave_air"); } - public boolean hasGravity(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean hasGravity(int x, int y, int z, int originX, int originZ, Hunk currentData) { String material = IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)); return material.equals("minecraft:sand") || material.equals("minecraft:red_sand") || material.endsWith("_concrete_powder"); } - public boolean isSolid(int x, int y, int z, int originX, int originZ, Hunk currentData) { - PlatformBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); + public boolean isSolid(int x, int y, int z, int originX, int originZ, Hunk currentData) { + NativeBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); return B.isSolid(d) && !B.isVineBlock(d); } - public boolean isSolidNonSlab(int x, int y, int z, int originX, int originZ, Hunk currentData) { - PlatformBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); + public boolean isSolidNonSlab(int x, int y, int z, int originX, int originZ, Hunk currentData) { + NativeBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); return B.isSolid(d) && !IrisProceduralBlocks.materialKey(d).endsWith("_slab"); } - public boolean isAirOrWater(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isAirOrWater(int x, int y, int z, int originX, int originZ, Hunk currentData) { String material = IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)); return material.equals("minecraft:water") || material.equals("minecraft:air") || material.equals("minecraft:cave_air"); } - public boolean isSlab(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isSlab(int x, int y, int z, int originX, int originZ, Hunk currentData) { return IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)).endsWith("_slab"); } - public boolean isSnowLayer(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isSnowLayer(int x, int y, int z, int originX, int originZ, Hunk currentData) { return IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)).equals("minecraft:snow"); } - public boolean isWater(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isWater(int x, int y, int z, int originX, int originZ, Hunk currentData) { return IrisProceduralBlocks.materialKey(getPostBlock(x, y, z, originX, originZ, currentData)).equals("minecraft:water"); } - public boolean isWaterOrWaterlogged(int x, int y, int z, int originX, int originZ, Hunk currentData) { - PlatformBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); + public boolean isWaterOrWaterlogged(int x, int y, int z, int originX, int originZ, Hunk currentData) { + NativeBlockState d = getPostBlock(x, y, z, originX, originZ, currentData); return IrisProceduralBlocks.materialKey(d).equals("minecraft:water") || "true".equals(IrisProceduralBlocks.propertyValue(d, "waterlogged")); } - public boolean isLiquid(int x, int y, int z, int originX, int originZ, Hunk currentData) { + public boolean isLiquid(int x, int y, int z, int originX, int originZ, Hunk currentData) { return IrisProceduralBlocks.hasProperty(getPostBlock(x, y, z, originX, originZ, currentData), "level"); } - public void setPostBlock(int x, int y, int z, PlatformBlockState d, int originX, int originZ, Hunk currentData) { + public void setPostBlock(int x, int y, int z, NativeBlockState d, int originX, int originZ, Hunk currentData) { int lx = x - originX; int lz = z - originZ; @@ -364,8 +364,8 @@ public class IrisPostModifier extends EngineAssignedModifier * available while generating this one. Resolve the hunk index relative to the chunk origin and let * getClosest clamp to the nearest in-chunk column instead of wrapping to the opposite chunk edge. */ - public PlatformBlockState getPostBlock(int x, int y, int z, int originX, int originZ, Hunk h) { - PlatformBlockState b = h.getClosest(x - originX, y, z - originZ); + public NativeBlockState getPostBlock(int x, int y, int z, int originX, int originZ, Hunk h) { + NativeBlockState b = h.getClosest(x - originX, y, z - originZ); return b == null ? AIR.get() : b; } diff --git a/core/src/main/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuator.java b/core/src/main/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuator.java index 5bad65943..73da30d98 100644 --- a/core/src/main/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuator.java +++ b/core/src/main/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuator.java @@ -44,10 +44,10 @@ import art.arcane.volmlib.util.documentation.BlockCoordinates; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.scheduling.PrecisionStopwatch; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.Getter; -public class IrisTerrainNormalActuator extends EngineAssignedActuator { +public class IrisTerrainNormalActuator extends EngineAssignedActuator { private static final BoundBlockState BEDROCK = BoundBlockState.of("BEDROCK"); private static final BoundBlockState AIR = BoundBlockState.of("AIR"); @Getter @@ -62,7 +62,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator h, boolean multicore, ChunkContext context) { + public void onActuate(int x, int z, Hunk h, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); for (int xf = 0; xf < h.getWidth(); xf++) { @@ -81,7 +81,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator h, ChunkContext context) { + public void terrainSliver(int x, int z, int xf, Hunk h, ChunkContext context) { int chunkHeight = h.getHeight(); int chunkDepth = h.getDepth(); IrisDimension dimension = getDimension(); @@ -93,7 +93,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator biomeCache = context.getBiome(); ChunkedDataCache regionCache = context.getRegion(); - ChunkedDataCache rockCache = context.getRock(); + ChunkedDataCache rockCache = context.getRock(); int realX = xf + x; UpperDimensionContext upperContext = getEngine().getUpperContext(); // Dimension-level ore lookups are chunk-invariant; resolving them per column paid @@ -149,11 +149,11 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator biomeSurfaceOres = hideOres ? null : biome.getSurfaceOreGenerators(); KList regionSurfaceOres = hideOres ? null : region.getSurfaceOreGenerators(); KList biomeUndergroundOres = hideOres ? null : biome.getUndergroundOreGenerators(); @@ -164,9 +164,9 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator blocks = null; - KList ceilingBlocks = null; - KList fblocks = null; + KList blocks = null; + KList ceilingBlocks = null; + KList fblocks = null; for (int i = topY; i >= 0; i--) { if (i == 0 && bedrockEnabled) { @@ -189,7 +189,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator upperBlocks = null; + NativeBlockState upperRock = upperContext.getRockBlock(realX, realZ); + NativeBlockState upperMappedSurface = upperContext.getSurfaceBlock(realX, realZ); + KList upperBlocks = null; int paletteSourceY = -1; for (int y = chunkHeight - 1; y >= upperSurfaceY; y--) { @@ -367,8 +367,8 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator oreGenerators, int x, int y, int z, RNG rng, IrisData data) { + private NativeBlockState generateOres(KList oreGenerators, int x, int y, int z, RNG rng, IrisData data) { if (oreGenerators == null || oreGenerators.isEmpty()) { return null; } @@ -393,7 +393,7 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator blocks, Hunk biomes, + public void generate(int x, int z, Hunk blocks, Hunk biomes, boolean multicore, ChunkContext context) { TransitionGenerationPlan plan = context.getComplex().getTransitionGenerationPlan(); if (plan == null || !plan.hasTransitionAtChunk(x >> 4, z >> 4)) { @@ -45,8 +45,8 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen blendGeometry(x, z, blocks, context, source, plan); } - public static SavedTerrainChunk capture(int x, int z, Hunk blocks, - Hunk biomes, int minimumY, + public static SavedTerrainChunk capture(int x, int z, Hunk blocks, + Hunk biomes, int minimumY, ChunkContext context, boolean boundaryOnly) throws IOException { GeometrySource source = new GeometrySource(blocks, biomes, minimumY, context); return boundaryOnly @@ -54,10 +54,10 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen : SavedTerrainChunk.capture(x >> 4, z >> 4, minimumY, blocks.getHeight(), "minecraft:noise", source); } - private void blendGeometry(int x, int z, Hunk blocks, ChunkContext context, + private void blendGeometry(int x, int z, Hunk blocks, ChunkContext context, GeometrySource source, TransitionGenerationPlan plan) { PlatformRegistries registries = IrisPlatforms.get().registries(); - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); for (int localX = 0; localX < blocks.getWidth(); localX++) { for (int localZ = 0; localZ < blocks.getDepth(); localZ++) { BoundaryGeometryInfluence influence = plan.geometryAt(x + localX, z + localZ); @@ -77,11 +77,11 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen List voxels = blended.voxels(); for (int offset = 0; offset < voxels.size(); offset++) { BoundaryColumnGeometry.Voxel voxel = voxels.get(offset); - PlatformBlockState existing = blocks.getRaw(localX, offset, localZ); + NativeBlockState existing = blocks.getRaw(localX, offset, localZ); if (existing != null && existing.key().equals(voxel.stateKey())) { continue; } - PlatformBlockState replacement = resolved.computeIfAbsent(voxel.stateKey(), registries::blockOrNull); + NativeBlockState replacement = resolved.computeIfAbsent(voxel.stateKey(), registries::blockOrNull); if (replacement == null) { throw new IllegalStateException("Terrain boundary requires unavailable block state " + voxel.stateKey()); } @@ -93,13 +93,13 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen } private static final class GeometrySource implements SavedTerrainChunk.VoxelSource { - private final Hunk blocks; - private final Hunk biomes; + private final Hunk blocks; + private final Hunk biomes; private final int minimumY; private final ChunkContext context; - private final Map voxels = new IdentityHashMap<>(); + private final Map voxels = new IdentityHashMap<>(); - private GeometrySource(Hunk blocks, Hunk biomes, int minimumY, ChunkContext context) { + private GeometrySource(Hunk blocks, Hunk biomes, int minimumY, ChunkContext context) { this.blocks = blocks; this.biomes = biomes; this.minimumY = minimumY; @@ -108,7 +108,7 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen @Override public BoundaryColumnGeometry.Voxel voxel(int localX, int worldY, int localZ) { - PlatformBlockState state = blocks.getRaw(localX, worldY - minimumY, localZ); + NativeBlockState state = blocks.getRaw(localX, worldY - minimumY, localZ); if (state == null) { state = IrisPlatforms.get().registries().air(); } @@ -122,7 +122,7 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen @Override public String biome(int localX, int worldY, int localZ) throws IOException { - PlatformBiome biome = biomes.getRaw(localX, worldY - minimumY, localZ); + NativeBiome biome = biomes.getRaw(localX, worldY - minimumY, localZ); if (biome == null) { throw new IOException("Natural terrain has no physical biome at " + localX + "," + worldY + "," + localZ); } @@ -137,10 +137,10 @@ public final class IrisTransitionGeometryActuator extends EngineAssignedComponen return BoundaryColumnGeometry.fromVoxels(minimumY, values); } - private static BoundaryColumnGeometry.Voxel encode(PlatformBlockState state) { + private static BoundaryColumnGeometry.Voxel encode(NativeBlockState state) { String stateKey = state.key(); boolean custom = state.isCustom(); - PlatformBlockState nativeState = state.placementBaseState(); + NativeBlockState nativeState = state.placementBaseState(); if (nativeState != null) { state = nativeState; } diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimension.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimension.java index 4304e18e6..60adc504e 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimension.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimension.java @@ -60,12 +60,12 @@ import art.arcane.iris.pack.validation.CompatStatus; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.pack.loading.IrisRegistrant; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer.Dimension; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer.Dimension; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.world.history.GenerationRegistryContractFactory; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; @@ -427,16 +427,16 @@ public class IrisDimension extends IrisRegistrant { carvingEntryIndex.reset(); } - public PlatformBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { + public NativeBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { KList localOres = surface ? getSurfaceOres() : getUndergroundOres(); return generateOres(localOres, x, y, z, rng, data); } - public PlatformBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { return generateOres(getSurfaceOres(), x, y, z, rng, data); } - public PlatformBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { return generateOres(getUndergroundOres(), x, y, z, rng, data); } @@ -448,7 +448,7 @@ public class IrisDimension extends IrisRegistrant { return !getUndergroundOres().isEmpty(); } - private PlatformBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { + private NativeBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { if (localOres.isEmpty()) { return null; } @@ -456,7 +456,7 @@ public class IrisDimension extends IrisRegistrant { int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - PlatformBlockState ore = oreGenerator.generate(x, y, z, rng, data); + NativeBlockState ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; } diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionCarvingEntry.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionCarvingEntry.java index 6dd847744..6c7ff23e5 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionCarvingEntry.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionCarvingEntry.java @@ -7,7 +7,7 @@ import art.arcane.iris.pack.validation.CompatPools; import art.arcane.iris.pack.value.IrisRange; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.DependsOn; diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionRuntimeContract.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionRuntimeContract.java index 8be6d5b60..412dfa311 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionRuntimeContract.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionRuntimeContract.java @@ -18,7 +18,7 @@ package art.arcane.iris.generation.terrain; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.volmlib.util.json.JSONObject; import java.util.Objects; diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionType.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionType.java index 749fd31ce..814e9381d 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionType.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisDimensionType.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.terrain; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.volmlib.util.data.Varint; import art.arcane.volmlib.util.io.IO; import lombok.EqualsAndHashCode; diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisMaterialPalette.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisMaterialPalette.java index 7d7ca2aa4..b11222afb 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisMaterialPalette.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisMaterialPalette.java @@ -18,13 +18,15 @@ package art.arcane.iris.generation.terrain; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePalette; + import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.noise.IrisGeneratorStyle; import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.cache.LazyBoundedCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; @@ -32,7 +34,7 @@ import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MinNumber; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -53,11 +55,11 @@ import java.util.concurrent.atomic.AtomicReference; @AllArgsConstructor @Description("A palette of materials") @Data -public class IrisMaterialPalette { +public class IrisMaterialPalette implements StructurePalette { private static final int LAYER_GENERATOR_CACHE_SIZE = 32; private static final int LAYER_GENERATOR_SALT = -23_498_896; - private final transient AtomicCache> blockData = new AtomicCache<>(); + private final transient AtomicCache> blockData = new AtomicCache<>(); @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) private final transient LazyBoundedCache layerGenerators = @@ -76,8 +78,8 @@ public class IrisMaterialPalette { @Description("The palette of blocks to be used in this layer") private KList palette = new KList().qadd(new IrisBlockData("STONE")); - public PlatformBlockState get(RNG rng, double x, double y, double z, IrisData rdata) { - KList localBlockData = getBlockData(rdata); + public NativeBlockState get(RNG rng, double x, double y, double z, IrisData rdata) { + KList localBlockData = getBlockData(rdata); int blockDataSize = localBlockData.size(); if (blockDataSize == 0) { return null; @@ -136,12 +138,12 @@ public class IrisMaterialPalette { return this; } - public KList getBlockData(IrisData rdata) { + public KList getBlockData(IrisData rdata) { return blockData.aquire(() -> { - KList blockData = new KList<>(); + KList blockData = new KList<>(); for (IrisBlockData ix : palette) { - PlatformBlockState bx = ix.getBlockData(rdata); + NativeBlockState bx = ix.getBlockData(rdata); if (bx != null) { for (int i = 0; i < ix.getWeight(); i++) { blockData.add(bx); diff --git a/core/src/main/java/art/arcane/iris/generation/terrain/IrisRegion.java b/core/src/main/java/art/arcane/iris/generation/terrain/IrisRegion.java index e8528dbed..e37ef3dfd 100644 --- a/core/src/main/java/art/arcane/iris/generation/terrain/IrisRegion.java +++ b/core/src/main/java/art/arcane/iris/generation/terrain/IrisRegion.java @@ -42,9 +42,9 @@ import art.arcane.iris.pack.validation.CompatStatus; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.pack.loading.IrisRegistrant; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; @@ -176,16 +176,16 @@ public class IrisRegion extends IrisRegistrant implements Rarity { @ArrayType(type = IrisOreGenerator.class, min = 1) private KList ores = new KList<>(); - public PlatformBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { + public NativeBlockState generateOres(int x, int y, int z, RNG rng, IrisData data, boolean surface) { KList localOres = surface ? getSurfaceOres() : getUndergroundOres(); return generateOres(localOres, x, y, z, rng, data); } - public PlatformBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateSurfaceOres(int x, int y, int z, RNG rng, IrisData data) { return generateOres(getSurfaceOres(), x, y, z, rng, data); } - public PlatformBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { + public NativeBlockState generateUndergroundOres(int x, int y, int z, RNG rng, IrisData data) { return generateOres(getUndergroundOres(), x, y, z, rng, data); } @@ -197,7 +197,7 @@ public class IrisRegion extends IrisRegistrant implements Rarity { return !getUndergroundOres().isEmpty(); } - private PlatformBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { + private NativeBlockState generateOres(KList localOres, int x, int y, int z, RNG rng, IrisData data) { if (localOres.isEmpty()) { return null; } @@ -205,7 +205,7 @@ public class IrisRegion extends IrisRegistrant implements Rarity { int oreCount = localOres.size(); for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) { IrisOreGenerator oreGenerator = localOres.get(oreIndex); - PlatformBlockState ore = oreGenerator.generate(x, y, z, rng, data); + NativeBlockState ore = oreGenerator.generate(x, y, z, rng, data); if (ore != null) { return ore; } diff --git a/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashEngine.java b/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashEngine.java index 56d653423..57e15b6f1 100644 --- a/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashEngine.java +++ b/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashEngine.java @@ -23,8 +23,8 @@ import art.arcane.iris.localization.RuntimeProgressMessages; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.ChunkSpiral; import art.arcane.iris.generation.concurrent.MultiBurst; import art.arcane.volmlib.util.localization.MessageArgument; @@ -62,9 +62,9 @@ public final class GoldenHashEngine { int maxY(); - PlatformBlockState block(int x, int y, int z); + NativeBlockState block(int x, int y, int z); - PlatformBiome biome(int x, int y, int z); + NativeBiome biome(int x, int y, int z); } public interface ChunkSource { @@ -249,15 +249,15 @@ public final class GoldenHashEngine { MessageDigest biomeDigest = sha256(); int minY = snapshot.minY(); int maxY = snapshot.maxY(); - IdentityHashMap blockCache = new IdentityHashMap<>(); - Map biomeCache = new HashMap<>(); + IdentityHashMap blockCache = new IdentityHashMap<>(); + Map biomeCache = new HashMap<>(); byte[] nullBiome = (request.nullBiomeKey() + "\n").getBytes(StandardCharsets.UTF_8); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = minY; y < maxY; y++) { - PlatformBlockState data = snapshot.block(x, y, z); - byte[] bytes = blockCache.computeIfAbsent(data, (PlatformBlockState d) -> (d.key() + "\n").getBytes(StandardCharsets.UTF_8)); + NativeBlockState data = snapshot.block(x, y, z); + byte[] bytes = blockCache.computeIfAbsent(data, (NativeBlockState d) -> (d.key() + "\n").getBytes(StandardCharsets.UTF_8)); blockDigest.update(bytes); } } @@ -266,10 +266,10 @@ public final class GoldenHashEngine { for (int x = 0; x < 16; x += BIOME_STEP) { for (int z = 0; z < 16; z += BIOME_STEP) { for (int y = minY; y < maxY; y += BIOME_STEP) { - PlatformBiome biome = snapshot.biome(x, y, z); + NativeBiome biome = snapshot.biome(x, y, z); byte[] bytes = biome == null ? nullBiome - : biomeCache.computeIfAbsent(biome, (PlatformBiome b) -> (b.key() + "\n").getBytes(StandardCharsets.UTF_8)); + : biomeCache.computeIfAbsent(biome, (NativeBiome b) -> (b.key() + "\n").getBytes(StandardCharsets.UTF_8)); biomeDigest.update(bytes); } } diff --git a/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashScanner.java b/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashScanner.java index 350fa5512..7083e3e7b 100644 --- a/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashScanner.java +++ b/core/src/main/java/art/arcane/iris/generation/validation/GoldenHashScanner.java @@ -27,8 +27,8 @@ import art.arcane.iris.generation.chunk.ChunkDataHunkView; import art.arcane.iris.generation.chunk.TerrainChunkBiomeHunkView; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.localization.C; import art.arcane.iris.platform.bukkit.plugin.VolmitSender; import org.bukkit.Bukkit; @@ -99,12 +99,12 @@ public final class GoldenHashScanner { } @Override - public PlatformBlockState block(int x, int y, int z) { + public NativeBlockState block(int x, int y, int z) { return buffer.getBlockData(x, y, z); } @Override - public PlatformBiome biome(int x, int y, int z) { + public NativeBiome biome(int x, int y, int z) { return buffer.getBiome(x, y, z); } }; diff --git a/core/src/main/java/art/arcane/iris/integration/ExternalDataProvider.java b/core/src/main/java/art/arcane/iris/integration/ExternalDataProvider.java index e0341f2a1..b05dd4e35 100644 --- a/core/src/main/java/art/arcane/iris/integration/ExternalDataProvider.java +++ b/core/src/main/java/art/arcane/iris/integration/ExternalDataProvider.java @@ -1,9 +1,9 @@ package art.arcane.iris.integration; import art.arcane.iris.integration.data.DataType; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BiomeColor; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.generation.cache.Cache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/main/java/art/arcane/iris/integration/ExternalDataSVC.java b/core/src/main/java/art/arcane/iris/integration/ExternalDataSVC.java index 71056ec91..185fa395e 100644 --- a/core/src/main/java/art/arcane/iris/integration/ExternalDataSVC.java +++ b/core/src/main/java/art/arcane/iris/integration/ExternalDataSVC.java @@ -21,8 +21,8 @@ package art.arcane.iris.integration; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.integration.data.DataType; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/main/java/art/arcane/iris/integration/WorldEditLink.java b/core/src/main/java/art/arcane/iris/integration/WorldEditLink.java index e8c778c57..77fd69240 100644 --- a/core/src/main/java/art/arcane/iris/integration/WorldEditLink.java +++ b/core/src/main/java/art/arcane/iris/integration/WorldEditLink.java @@ -2,7 +2,7 @@ package art.arcane.iris.integration; import art.arcane.iris.spi.CapabilityProbe; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.data.Cuboid; import art.arcane.volmlib.util.scheduling.ChronoLatch; import org.bukkit.Bukkit; diff --git a/core/src/main/java/art/arcane/iris/integration/data/CraftEngineDataProvider.java b/core/src/main/java/art/arcane/iris/integration/data/CraftEngineDataProvider.java index 52ea0b157..41a656d61 100644 --- a/core/src/main/java/art/arcane/iris/integration/data/CraftEngineDataProvider.java +++ b/core/src/main/java/art/arcane/iris/integration/data/CraftEngineDataProvider.java @@ -2,8 +2,8 @@ package art.arcane.iris.integration.data; import art.arcane.iris.integration.ExternalDataProvider; import art.arcane.iris.integration.Identifier; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.generation.cache.Cache; import art.arcane.iris.generation.runtime.Engine; diff --git a/core/src/main/java/art/arcane/iris/integration/data/MythicCrucibleDataProvider.java b/core/src/main/java/art/arcane/iris/integration/data/MythicCrucibleDataProvider.java index 1e7a3cb42..5a085628c 100644 --- a/core/src/main/java/art/arcane/iris/integration/data/MythicCrucibleDataProvider.java +++ b/core/src/main/java/art/arcane/iris/integration/data/MythicCrucibleDataProvider.java @@ -24,9 +24,9 @@ import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.integration.ExternalDataProvider; import art.arcane.iris.integration.Identifier; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BiomeColor; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/main/java/art/arcane/iris/integration/data/NexoDataProvider.java b/core/src/main/java/art/arcane/iris/integration/data/NexoDataProvider.java index 0a56e05a5..a57d99393 100644 --- a/core/src/main/java/art/arcane/iris/integration/data/NexoDataProvider.java +++ b/core/src/main/java/art/arcane/iris/integration/data/NexoDataProvider.java @@ -9,9 +9,9 @@ import com.nexomc.nexo.items.ItemBuilder; import art.arcane.iris.integration.ExternalDataProvider; import art.arcane.iris.integration.Identifier; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BiomeColor; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.spi.IrisLogging; diff --git a/core/src/main/java/art/arcane/iris/pack/ContentKeyValidator.java b/core/src/main/java/art/arcane/iris/pack/ContentKeyValidator.java index 666a7c090..3d2e37ad4 100644 --- a/core/src/main/java/art/arcane/iris/pack/ContentKeyValidator.java +++ b/core/src/main/java/art/arcane/iris/pack/ContentKeyValidator.java @@ -22,8 +22,8 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.structure.object.IrisObjectIO; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformNumericRange; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; @@ -122,7 +122,7 @@ public final class ContentKeyValidator { if (registries == null || referencedBlockStates == null || referencedBlockStates.isEmpty()) { return List.of(); } - Map> declared = registries.blockStateProperties(); + Map> declared = registries.blockStateProperties(); if (declared == null || declared.isEmpty()) { return List.of(); } @@ -138,7 +138,7 @@ public final class ContentKeyValidator { if (base == null) { continue; } - List known = declared.get(base); + List known = declared.get(base); if (known == null || known.isEmpty()) { continue; } @@ -161,10 +161,10 @@ public final class ContentKeyValidator { return List.copyOf(messages); } - private static String describeProperty(String base, String name, String value, List known) { - PlatformBlockProperty match = null; + private static String describeProperty(String base, String name, String value, List known) { + NativeBlockProperty match = null; List names = new ArrayList<>(known.size()); - for (PlatformBlockProperty property : known) { + for (NativeBlockProperty property : known) { names.add(property.name()); if (property.name().equalsIgnoreCase(name)) { match = property; @@ -206,11 +206,11 @@ public final class ContentKeyValidator { * Range check for a property that declares bounds but cannot enumerate its values. A value that is not a number * at all is reported too - a numeric property never accepts one. */ - private static String describeNumericRange(String base, PlatformBlockProperty match, String value) { + private static String describeNumericRange(String base, NativeBlockProperty match, String value) { if (!match.hasNumericRange()) { return null; } - PlatformNumericRange range = match.numericRange(); + NativeNumericRange range = match.numericRange(); String bounds = "expected " + (range.exclusiveMinimum() ? "greater than " : "at least ") + describeBound(range.minimum()) + " and " + (range.exclusiveMaximum() ? "less than " : "at most ") + describeBound(range.maximum()); diff --git a/core/src/main/java/art/arcane/iris/pack/DefaultPackBootstrapProvisioner.java b/core/src/main/java/art/arcane/iris/pack/DefaultPackBootstrapProvisioner.java index 1baf001c5..1976d0f29 100644 --- a/core/src/main/java/art/arcane/iris/pack/DefaultPackBootstrapProvisioner.java +++ b/core/src/main/java/art/arcane/iris/pack/DefaultPackBootstrapProvisioner.java @@ -4,8 +4,8 @@ import art.arcane.iris.pack.datapack.IrisDatapackCompiler; import art.arcane.iris.world.lifecycle.BukkitStartupPaths; import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration; import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.volmlib.util.collection.KList; import java.io.File; diff --git a/core/src/main/java/art/arcane/iris/pack/PackImageMapValidator.java b/core/src/main/java/art/arcane/iris/pack/PackImageMapValidator.java index 50f754b77..67a6a4fb1 100644 --- a/core/src/main/java/art/arcane/iris/pack/PackImageMapValidator.java +++ b/core/src/main/java/art/arcane/iris/pack/PackImageMapValidator.java @@ -22,7 +22,7 @@ import art.arcane.iris.pack.value.IrisRange; import art.arcane.iris.world.IrisWorldBoundary; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; @@ -495,7 +495,7 @@ final class PackImageMapValidator { } else if (application == IrisImageMapApplication.SURFACE_BLOCK && registries != null) { for (String target : targets) { try { - PlatformBlockState block = registries.blockOrNull(target, false); + NativeBlockState block = registries.blockOrNull(target, false); if (block == null) { blockingErrors.add(context + " references unknown surface block target '" + target + "'."); } diff --git a/core/src/main/java/art/arcane/iris/pack/PackSpawnValidator.java b/core/src/main/java/art/arcane/iris/pack/PackSpawnValidator.java index 4d040e7b9..8f1698260 100644 --- a/core/src/main/java/art/arcane/iris/pack/PackSpawnValidator.java +++ b/core/src/main/java/art/arcane/iris/pack/PackSpawnValidator.java @@ -21,7 +21,7 @@ package art.arcane.iris.pack; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawnType; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformEntityType; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; @@ -325,7 +325,7 @@ final class PackSpawnValidator { if (registries == null) { return null; } - PlatformEntityType entityType = registries.entity(typeKey); + NativeEntityType entityType = registries.entity(typeKey); return entityType == null ? SpawnCategoryResolution.unknown() : SpawnCategoryResolution.known(entityType.spawnCategory()); diff --git a/core/src/main/java/art/arcane/iris/pack/PackStructurePlacementValidator.java b/core/src/main/java/art/arcane/iris/pack/PackStructurePlacementValidator.java index e007d62bf..c2fee8dfd 100644 --- a/core/src/main/java/art/arcane/iris/pack/PackStructurePlacementValidator.java +++ b/core/src/main/java/art/arcane/iris/pack/PackStructurePlacementValidator.java @@ -21,7 +21,7 @@ package art.arcane.iris.pack; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersion.java b/core/src/main/java/art/arcane/iris/pack/datapack/DataVersion.java similarity index 70% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersion.java rename to core/src/main/java/art/arcane/iris/pack/datapack/DataVersion.java index bcfc985f7..db9a5b9e7 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersion.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/DataVersion.java @@ -1,15 +1,13 @@ -package art.arcane.iris.platform.bukkit.nms.datapack; +package art.arcane.iris.pack.datapack; -import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217; +import art.arcane.iris.pack.datapack.v1217.DataFixerV1217; import art.arcane.volmlib.util.collection.KMap; import lombok.AccessLevel; import lombok.Getter; import java.util.function.Supplier; -import art.arcane.iris.platform.bukkit.nms.datapack.v263.DataFixerV263; +import art.arcane.iris.pack.datapack.v263.DataFixerV263; //https://minecraft.wiki/w/Pack_format @Getter @@ -35,21 +33,7 @@ public enum DataVersion { } public static IDataFixer getDefault() { - DataVersion version = INMS.get().getDataVersion(); - if (version == null || version == UNSUPPORTED) { - DataVersion fallback = getLatest(); - IrisLogging.warn("Unsupported datapack version mapping detected, falling back to latest fixer: " + fallback.getVersion()); - return fallback.get(); - } - - IDataFixer fixer = version.get(); - if (fixer == null) { - DataVersion fallback = getLatest(); - IrisLogging.warn("Null datapack fixer for " + version.getVersion() + ", falling back to latest fixer: " + fallback.getVersion()); - return fallback.get(); - } - - return fixer; + return getRuntime().get(); } public static DataVersion forMinecraftVersion(String version) { diff --git a/core/src/main/java/art/arcane/iris/pack/datapack/DatapackStructureScopeIndex.java b/core/src/main/java/art/arcane/iris/pack/datapack/DatapackStructureScopeIndex.java index 0aa1e3162..831da8239 100644 --- a/core/src/main/java/art/arcane/iris/pack/datapack/DatapackStructureScopeIndex.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/DatapackStructureScopeIndex.java @@ -1,5 +1,7 @@ package art.arcane.iris.pack.datapack; +import art.arcane.volmlib.nativelib.terrain.StructureScope; + import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -7,7 +9,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; -public final class DatapackStructureScopeIndex { +public final class DatapackStructureScopeIndex implements StructureScope { private final Map> sourcesByStructure; private final Map> sourcesByStructureSet; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/IDataFixer.java b/core/src/main/java/art/arcane/iris/pack/datapack/IDataFixer.java similarity index 94% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/IDataFixer.java rename to core/src/main/java/art/arcane/iris/pack/datapack/IDataFixer.java index 25f431ac5..b9a74e085 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/IDataFixer.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/IDataFixer.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.nms.datapack; +package art.arcane.iris.pack.datapack; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; diff --git a/core/src/main/java/art/arcane/iris/pack/datapack/IrisDatapackCompiler.java b/core/src/main/java/art/arcane/iris/pack/datapack/IrisDatapackCompiler.java index 235648cc8..09697b9c0 100644 --- a/core/src/main/java/art/arcane/iris/pack/datapack/IrisDatapackCompiler.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/IrisDatapackCompiler.java @@ -7,8 +7,8 @@ import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration.IrisGeneratorBin import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.ResourceLoader; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.AtomicDirectoryPublisher; import art.arcane.iris.pack.PackDirectoryResolver; import art.arcane.iris.world.history.GenerationEpoch; diff --git a/core/src/main/java/art/arcane/iris/pack/datapack/ServerConfigurator.java b/core/src/main/java/art/arcane/iris/pack/datapack/ServerConfigurator.java index 465178825..62740ea9d 100644 --- a/core/src/main/java/art/arcane/iris/pack/datapack/ServerConfigurator.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/ServerConfigurator.java @@ -32,8 +32,8 @@ import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration; import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding; import art.arcane.iris.world.lifecycle.LifecycleOperationCoordinator; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.AtomicDirectoryPublisher; import art.arcane.iris.pack.DefaultPackBootstrapProvisioner; import art.arcane.iris.pack.PackDirectoryResolver; @@ -1154,9 +1154,7 @@ public class ServerConfigurator { } for (String i : keys) { - Object o = INMS.get().getCustomBiomeBaseFor(i); - - if (o == null) { + if (!INMS.get().hasBiome(i)) { if (reportRuntimeFailure) { IrisLogging.warn("The Biome " + i + " is not registered on the server."); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1192/DataFixerV1192.java b/core/src/main/java/art/arcane/iris/pack/datapack/v1192/DataFixerV1192.java similarity index 96% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1192/DataFixerV1192.java rename to core/src/main/java/art/arcane/iris/pack/datapack/v1192/DataFixerV1192.java index 6c215a1a2..d8636cf13 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1192/DataFixerV1192.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v1192/DataFixerV1192.java @@ -1,8 +1,8 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1192; +package art.arcane.iris.pack.datapack.v1192; import static art.arcane.iris.generation.terrain.IrisDimensionTypeOptions.TriState.FALSE; import static art.arcane.iris.generation.terrain.IrisDimensionTypeOptions.TriState.TRUE; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; import art.arcane.volmlib.util.json.JSONObject; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1206/DataFixerV1206.java b/core/src/main/java/art/arcane/iris/pack/datapack/v1206/DataFixerV1206.java similarity index 95% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1206/DataFixerV1206.java rename to core/src/main/java/art/arcane/iris/pack/datapack/v1206/DataFixerV1206.java index 964cb3fc9..aad334c8a 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1206/DataFixerV1206.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v1206/DataFixerV1206.java @@ -1,7 +1,7 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1206; +package art.arcane.iris.pack.datapack.v1206; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.platform.bukkit.nms.datapack.v1192.DataFixerV1192; +import art.arcane.iris.pack.datapack.v1192.DataFixerV1192; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawn; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawnType; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1213/DataFixerV1213.java b/core/src/main/java/art/arcane/iris/pack/datapack/v1213/DataFixerV1213.java similarity index 76% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1213/DataFixerV1213.java rename to core/src/main/java/art/arcane/iris/pack/datapack/v1213/DataFixerV1213.java index f39d4e29a..38938b115 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1213/DataFixerV1213.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v1213/DataFixerV1213.java @@ -1,6 +1,6 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1213; +package art.arcane.iris.pack.datapack.v1213; -import art.arcane.iris.platform.bukkit.nms.datapack.v1206.DataFixerV1206; +import art.arcane.iris.pack.datapack.v1206.DataFixerV1206; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217.java b/core/src/main/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217.java similarity index 98% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217.java rename to core/src/main/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217.java index e9da2dcc3..c1edff8b5 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217.java @@ -1,6 +1,6 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1217; +package art.arcane.iris.pack.datapack.v1217; -import art.arcane.iris.platform.bukkit.nms.datapack.v1213.DataFixerV1213; +import art.arcane.iris.pack.datapack.v1213.DataFixerV1213; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v263/DataFixerV263.java b/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java similarity index 94% rename from core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v263/DataFixerV263.java rename to core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java index 77512dda0..8c3228f12 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/datapack/v263/DataFixerV263.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java @@ -1,7 +1,7 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v263; +package art.arcane.iris.pack.datapack.v263; import art.arcane.iris.generation.biome.IrisBiomeCustom; -import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217; +import art.arcane.iris.pack.datapack.v1217.DataFixerV1217; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; diff --git a/core/src/main/java/art/arcane/iris/pack/loading/IrisData.java b/core/src/main/java/art/arcane/iris/pack/loading/IrisData.java index 6ddd3aabc..6f72897f3 100644 --- a/core/src/main/java/art/arcane/iris/pack/loading/IrisData.java +++ b/core/src/main/java/art/arcane/iris/pack/loading/IrisData.java @@ -35,11 +35,11 @@ import com.google.gson.stream.JsonWriter; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.pack.PackDirectoryResolver; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.world.loot.IrisBlockDrops; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.structure.placement.IrisStructureLocator; import art.arcane.iris.structure.graph.StructureGraphCatalog; import art.arcane.iris.world.history.GenerationPackFingerprint; @@ -809,7 +809,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { private void collectBlockDropMaterials(Set materials, KList rules) { for (IrisBlockDrops rule : rules) { for (IrisBlockData block : rule.getBlocks()) { - PlatformBlockState state = block.getBlockData(this); + NativeBlockState state = block.getBlockData(this); if (state != null) { materials.add(IrisProceduralBlocks.materialKey(state.placementBaseState())); } diff --git a/core/src/main/java/art/arcane/iris/pack/loading/ResourceLoader.java b/core/src/main/java/art/arcane/iris/pack/loading/ResourceLoader.java index 61173a8d1..cc78f09a1 100644 --- a/core/src/main/java/art/arcane/iris/pack/loading/ResourceLoader.java +++ b/core/src/main/java/art/arcane/iris/pack/loading/ResourceLoader.java @@ -26,7 +26,7 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.pack.schema.SchemaBuilder; import art.arcane.iris.pack.PackValidator; import art.arcane.iris.generation.runtime.PreservationRegistry; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.MeteredCache; import art.arcane.iris.generation.terrain.IrisDimension; diff --git a/core/src/main/java/art/arcane/iris/pack/schema/SchemaBlockStates.java b/core/src/main/java/art/arcane/iris/pack/schema/SchemaBlockStates.java index 7164224c3..dea2f4ec1 100644 --- a/core/src/main/java/art/arcane/iris/pack/schema/SchemaBlockStates.java +++ b/core/src/main/java/art/arcane/iris/pack/schema/SchemaBlockStates.java @@ -19,8 +19,8 @@ package art.arcane.iris.pack.schema; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformNumericRange; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; import art.arcane.volmlib.util.json.JSONArray; import art.arcane.volmlib.util.json.JSONObject; @@ -75,7 +75,7 @@ final class SchemaBlockStates { if (!builder.definitions.containsKey(propertiesKey)) { JSONObject props = new JSONObject(); - for (PlatformBlockProperty property : group.properties()) { + for (NativeBlockProperty property : group.properties()) { props.put(property.name(), buildBlockPropertyJson(property)); } @@ -90,7 +90,7 @@ final class SchemaBlockStates { return "#/definitions/" + key.replace("~", "~0").replace("/", "~1"); } - private static JSONObject buildBlockPropertyJson(PlatformBlockProperty property) { + private static JSONObject buildBlockPropertyJson(NativeBlockProperty property) { JSONObject json = new JSONObject(); json.put("type", property.jsonType()); json.put("default", property.defaultValue()); @@ -99,7 +99,7 @@ final class SchemaBlockStates { json.put("enum", new JSONArray(allowed)); } if (property.hasNumericRange()) { - PlatformNumericRange range = property.numericRange(); + NativeNumericRange range = property.numericRange(); if ("integer".equals(property.jsonType())) { json.put("minimum", (long) range.minimum()); json.put("maximum", (long) range.maximum()); @@ -115,10 +115,10 @@ final class SchemaBlockStates { private static List reconstructBlockStateGroups() { List groups = new ArrayList<>(); - List currentProperties = null; + List currentProperties = null; List currentBlocks = null; - for (Map.Entry> entry : IrisPlatforms.get().registries().blockStateProperties().entrySet()) { - List value = entry.getValue(); + for (Map.Entry> entry : IrisPlatforms.get().registries().blockStateProperties().entrySet()) { + List value = entry.getValue(); if (currentProperties != null && value == currentProperties) { currentBlocks.add(entry.getKey()); } else { @@ -131,6 +131,6 @@ final class SchemaBlockStates { return groups; } - private record BlockStateGroup(List blocks, List properties) { + private record BlockStateGroup(List blocks, List properties) { } } diff --git a/core/src/main/java/art/arcane/iris/pack/validation/ContentGate.java b/core/src/main/java/art/arcane/iris/pack/validation/ContentGate.java index c4618752d..f1ce501b3 100644 --- a/core/src/main/java/art/arcane/iris/pack/validation/ContentGate.java +++ b/core/src/main/java/art/arcane/iris/pack/validation/ContentGate.java @@ -23,7 +23,7 @@ import art.arcane.iris.pack.loading.ResourceLoader; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.io.IO; import art.arcane.volmlib.util.json.JSONObject; @@ -224,7 +224,7 @@ public final class ContentGate { * object palettes and {@code edit} find lists keep the key an edit rule can still rewrite. Null only when the * registry is not ready (callers keep their plain lookup) or the state is blank. */ - public PlatformBlockState resolveBlockOrPlaceholder(String state) { + public NativeBlockState resolveBlockOrPlaceholder(String state) { String normalized = normalizeState(state); if (normalized == null || !ready()) { return null; @@ -235,7 +235,7 @@ public final class ContentGate { } MissingBlockState placeholder = placeholders.get(normalized); if (placeholder == null) { - PlatformBlockState air; + NativeBlockState air; try { air = registries.air(); } catch (Throwable e) { @@ -254,7 +254,7 @@ public final class ContentGate { } private BlockResolution resolveUncached(String normalized) { - PlatformBlockState direct = lookup(normalized); + NativeBlockState direct = lookup(normalized); if (direct != null) { return new BlockResolution(direct, normalized, false, null, BlockResolution.Source.REGISTRY); } @@ -265,7 +265,7 @@ public final class ContentGate { String fallback = fallbacks().get(baseKey(normalized)); if (fallback != null) { String normalizedFallback = normalizeState(fallback); - PlatformBlockState substitute = normalizedFallback == null ? null : lookup(normalizedFallback); + NativeBlockState substitute = normalizedFallback == null ? null : lookup(normalizedFallback); if (substitute != null) { return new BlockResolution(substitute, normalizedFallback, true, normalized, BlockResolution.Source.FALLBACK); } @@ -280,7 +280,7 @@ public final class ContentGate { if (supplement == null) { return null; } - PlatformBlockState state = lookup(supplement); + NativeBlockState state = lookup(supplement); if (state != null) { return new BlockResolution(state, supplement, false, null, BlockResolution.Source.RENAME); } @@ -417,7 +417,7 @@ public final class ContentGate { return namespace < 0 ? baseKey : baseKey.substring(namespace + 1); } - private PlatformBlockState lookup(String normalized) { + private NativeBlockState lookup(String normalized) { try { return registries.blockOrNull(normalized, false); } catch (Throwable e) { @@ -483,7 +483,7 @@ public final class ContentGate { * @param substitutedFrom the missing key the fallback replaced, or null * @param source which step of the chain answered */ - public record BlockResolution(PlatformBlockState state, String resolvedKey, boolean substituted, String substitutedFrom, + public record BlockResolution(NativeBlockState state, String resolvedKey, boolean substituted, String substitutedFrom, Source source) { public enum Source { REGISTRY, @@ -496,7 +496,7 @@ public final class ContentGate { Objects.requireNonNull(source, "source"); } - public BlockResolution(PlatformBlockState state, String resolvedKey, boolean substituted, String substitutedFrom) { + public BlockResolution(NativeBlockState state, String resolvedKey, boolean substituted, String substitutedFrom) { this(state, resolvedKey, substituted, substitutedFrom, substituted ? Source.FALLBACK : Source.REGISTRY); } } diff --git a/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityBlockFilter.java b/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityBlockFilter.java index 4a14c779e..6677e0518 100644 --- a/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityBlockFilter.java +++ b/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityBlockFilter.java @@ -20,7 +20,7 @@ package art.arcane.iris.pack.validation; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.spi.IrisLogging; diff --git a/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityItemFilter.java b/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityItemFilter.java index 001bcc8a1..1ef8e8311 100644 --- a/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityItemFilter.java +++ b/core/src/main/java/art/arcane/iris/pack/validation/IrisCompatabilityItemFilter.java @@ -21,7 +21,7 @@ package art.arcane.iris.pack.validation; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.Required; import lombok.Data; diff --git a/core/src/main/java/art/arcane/iris/pack/validation/MissingBlockState.java b/core/src/main/java/art/arcane/iris/pack/validation/MissingBlockState.java index 650aeb27e..81d89ea1a 100644 --- a/core/src/main/java/art/arcane/iris/pack/validation/MissingBlockState.java +++ b/core/src/main/java/art/arcane/iris/pack/validation/MissingBlockState.java @@ -17,7 +17,7 @@ */ package art.arcane.iris.pack.validation; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import java.util.LinkedHashMap; import java.util.Map; @@ -29,14 +29,14 @@ import java.util.Objects; * everywhere else: every physical predicate and {@link #nativeHandle()} come from the real air state, so a placeholder * that does reach the world places air and never throws. */ -public final class MissingBlockState implements PlatformBlockState { +public final class MissingBlockState implements NativeBlockState { private final String key; private final String materialKey; private final String namespace; private final Map properties; - private final PlatformBlockState air; + private final NativeBlockState air; - private MissingBlockState(String key, PlatformBlockState air) { + private MissingBlockState(String key, NativeBlockState air) { this.key = key; this.materialKey = ContentGate.baseKey(key); int colon = materialKey.indexOf(':'); @@ -46,11 +46,11 @@ public final class MissingBlockState implements PlatformBlockState { } /** @param normalizedKey the missing key as {@link ContentGate#normalizeState(String)} returns it */ - public static MissingBlockState of(String normalizedKey, PlatformBlockState air) { + public static MissingBlockState of(String normalizedKey, NativeBlockState air) { return new MissingBlockState(Objects.requireNonNull(normalizedKey, "key"), Objects.requireNonNull(air, "air")); } - public static boolean isPlaceholder(PlatformBlockState state) { + public static boolean isPlaceholder(NativeBlockState state) { return state instanceof MissingBlockState; } @@ -165,7 +165,7 @@ public final class MissingBlockState implements PlatformBlockState { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return air.canPlaceOnto(onto); } @@ -174,7 +174,7 @@ public final class MissingBlockState implements PlatformBlockState { * the partial-match contract the adapters use for exact {@code find} entries. */ @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { if (!(state instanceof MissingBlockState other) || !materialKey.equals(other.materialKey)) { return false; } @@ -192,7 +192,7 @@ public final class MissingBlockState implements PlatformBlockState { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { Map merged = new LinkedHashMap<>(properties); merged.put(name, value); StringBuilder rebuilt = new StringBuilder(materialKey).append('['); diff --git a/core/src/main/java/art/arcane/iris/pack/value/IrisColor.java b/core/src/main/java/art/arcane/iris/pack/value/IrisColor.java index 8212a9647..e125d1340 100644 --- a/core/src/main/java/art/arcane/iris/pack/value/IrisColor.java +++ b/core/src/main/java/art/arcane/iris/pack/value/IrisColor.java @@ -18,7 +18,7 @@ package art.arcane.iris.pack.value; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/platform/BlockStateKey.java b/core/src/main/java/art/arcane/iris/platform/BlockStateKey.java deleted file mode 100644 index 80bfa70c1..000000000 --- a/core/src/main/java/art/arcane/iris/platform/BlockStateKey.java +++ /dev/null @@ -1,47 +0,0 @@ -package art.arcane.iris.platform; - -import java.util.LinkedHashMap; -import java.util.Map; - -public final class BlockStateKey { - private BlockStateKey() { - } - - public static String namespace(String key) { - String base = key; - int bracket = base.indexOf('['); - if (bracket >= 0) { - base = base.substring(0, bracket); - } - int colon = base.indexOf(':'); - return colon >= 0 ? base.substring(0, colon) : "minecraft"; - } - - public static String withProperty(String key, String name, String value) { - int bracket = key.indexOf('['); - if (bracket < 0) { - return key + "[" + name + "=" + value + "]"; - } - String base = key.substring(0, bracket); - String body = key.substring(bracket + 1, key.lastIndexOf(']')); - LinkedHashMap properties = new LinkedHashMap<>(); - for (String entry : body.split(",")) { - int equals = entry.indexOf('='); - if (equals < 0) { - continue; - } - properties.put(entry.substring(0, equals).trim(), entry.substring(equals + 1).trim()); - } - properties.put(name, value); - StringBuilder merged = new StringBuilder(base).append('['); - boolean first = true; - for (Map.Entry property : properties.entrySet()) { - if (!first) { - merged.append(','); - } - merged.append(property.getKey()).append('=').append(property.getValue()); - first = false; - } - return merged.append(']').toString(); - } -} diff --git a/core/src/main/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibraries.java b/core/src/main/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibraries.java new file mode 100644 index 000000000..fa7d151f1 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibraries.java @@ -0,0 +1,152 @@ +package art.arcane.iris.platform.bootstrap; + +import art.arcane.volmlib.nativelib.NativeVersion; +import art.arcane.volmlib.nativelib.NativeAdapters; +import io.github.slimjar.app.builder.ApplicationBuilder; +import io.github.slimjar.downloader.DependencyDownloader; +import io.github.slimjar.downloader.URLDependencyDownloader; +import io.github.slimjar.downloader.strategy.FilePathStrategy; +import io.github.slimjar.downloader.verify.FileChecksumCalculator; +import io.github.slimjar.relocation.helper.RelocationHelper; +import io.github.slimjar.relocation.helper.VerifyingRelocationHelperFactory; +import io.github.slimjar.relocation.meta.FlatFileMetaMediatorFactory; +import io.github.slimjar.exceptions.DownloaderException; +import io.github.slimjar.resolver.data.Dependency; +import io.github.slimjar.resolver.data.DependencyData; +import io.github.slimjar.resolver.data.Repository; +import io.github.slimjar.resolver.reader.dependency.DependencyDataProvider; +import io.github.slimjar.resolver.reader.dependency.DependencyReader; +import io.github.slimjar.resolver.reader.dependency.WrappingDependencyDataProviderFactory; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HexFormat; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +public final class NativeRuntimeLibraries { + private static final String MANIFEST = "META-INF/iris/native-runtime.properties"; + private final Map dependencies; + private final Repository repository; + + private NativeRuntimeLibraries(Map dependencies, Repository repository) { + this.dependencies = dependencies; + this.repository = repository; + } + + public static NativeRuntimeLibraries load(String minecraftVersion) { + try (InputStream input = NativeRuntimeLibraries.class.getClassLoader().getResourceAsStream(MANIFEST)) { + if (input == null) { + throw new IllegalStateException("Missing native runtime dependency manifest"); + } + Properties manifest = new Properties(); + manifest.load(input); + return from(manifest, minecraftVersion); + } catch (IOException exception) { + throw new IllegalStateException("Cannot read native runtime dependency manifest", exception); + } + } + + static NativeRuntimeLibraries from(Properties manifest, String minecraftVersion) throws IOException { + NativeVersion version = NativeVersion.resolve(minecraftVersion).orElseThrow( + () -> new UnsupportedOperationException("No native provider for Minecraft " + minecraftVersion)); + List modules = List.of(manifest.getProperty("modules", "").split(",")); + Map selected = new LinkedHashMap<>(); + for (String module : List.of("native-common", "native-" + version.packageName())) { + if (!modules.contains(module)) { + throw new UnsupportedOperationException("Native provider is not distributed: " + module); + } + String[] coordinate = manifest.getProperty(module + ".coordinate", "").split(":"); + String digest = manifest.getProperty(module + ".sha256", ""); + if (coordinate.length != 3 || !coordinate[0].equals("com.github.VolmitSoftware.VolmLib") + || !coordinate[1].equals(module) || coordinate[2].isBlank() || !digest.matches("[0-9a-f]{64}")) { + throw new IllegalStateException("Invalid native dependency manifest entry: " + module); + } + selected.put(new Dependency(coordinate[0], coordinate[1], coordinate[2], null, List.of()), digest); + } + return new NativeRuntimeLibraries(Map.copyOf(selected), + new Repository(URI.create(manifest.getProperty("repository")).toURL())); + } + + public > T configure(T builder, Path downloads) { + WrappingDependencyDataProviderFactory defaults = new WrappingDependencyDataProviderFactory(DependencyReader.DEFAULT); + builder.dataProviderFactory(url -> { + DependencyDataProvider provider = defaults.create(url); + return () -> merge(provider.get()); + }); + VerifyingRelocationHelperFactory relocation = new VerifyingRelocationHelperFactory( + new FileChecksumCalculator("SHA-256"), + FilePathStrategy.createRelocationStrategy(downloads.toFile(), "Iris"), + new FlatFileMetaMediatorFactory()); + builder.relocationHelperFactory(relocator -> { + RelocationHelper helper = relocation.create(relocator); + return (dependency, file) -> dependencies.containsKey(dependency) ? file : helper.relocate(dependency, file); + }); + builder.downloaderFactory((output, resolver, verifier) -> { + DependencyDownloader delegate = new URLDependencyDownloader(output, resolver, verifier); + return dependency -> { + Optional result = delegate.download(dependency); + String expected = dependencies.get(dependency); + if (expected != null) { + File file = result.orElseThrow(() -> new DownloaderException("Native dependency has no jar: " + dependency)); + verify(file, expected); + } + return result; + }; + }); + return builder; + } + + public URLClassLoader openProviderLoader(Path downloads) { + try { + List urls = new ArrayList<>(dependencies.size()); + for (Map.Entry entry : dependencies.entrySet()) { + Dependency dependency = entry.getKey(); + Path artifact = downloads.resolve(dependency.groupId().replace('.', '/')) + .resolve(dependency.artifactId()).resolve(dependency.version()) + .resolve(dependency.artifactId() + "-" + dependency.version() + ".jar"); + verify(artifact.toFile(), entry.getValue()); + urls.add(artifact.toUri().toURL()); + } + URLClassLoader loader = new URLClassLoader("Iris native providers", urls.toArray(URL[]::new), + NativeAdapters.class.getClassLoader()); + NativeAdapters.registerProviderLoader(loader); + return loader; + } catch (IOException exception) { + throw new IllegalStateException("Cannot open native provider loader", exception); + } + } + + DependencyData merge(DependencyData original) { + List combined = new ArrayList<>(original.dependencies()); + combined.addAll(dependencies.keySet()); + List repositories = new ArrayList<>(original.repositories()); + if (!repositories.contains(repository)) { + repositories.add(repository); + } + return new DependencyData(original.mirrors(), repositories, combined, original.relocations()); + } + + static void verify(File file, String expected) { + try { + String actual = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(file.toPath()))); + if (!actual.equals(expected)) { + throw new DownloaderException("Native dependency does not match this Iris build: " + file.getName()); + } + } catch (IOException | NoSuchAlgorithmException exception) { + throw new DownloaderException("Cannot verify native dependency: " + file, exception); + } + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bootstrap/SlimJar.java b/core/src/main/java/art/arcane/iris/platform/bootstrap/SlimJar.java index 2fd046ba4..437d4ed75 100644 --- a/core/src/main/java/art/arcane/iris/platform/bootstrap/SlimJar.java +++ b/core/src/main/java/art/arcane/iris/platform/bootstrap/SlimJar.java @@ -1,15 +1,18 @@ package art.arcane.iris.platform.bootstrap; import art.arcane.iris.platform.bukkit.BukkitPlatform; +import art.arcane.volmlib.nativelib.NativeAdapters; import art.arcane.iris.platform.bukkit.plugin.VolmitPlugin; import io.github.slimjar.app.builder.ApplicationBuilder; -import io.github.slimjar.app.builder.SpigotApplicationBuilder; import io.github.slimjar.injector.loader.factory.InjectableFactory; import io.github.slimjar.logging.ProcessLogger; +import org.bukkit.Bukkit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.nio.file.Path; +import java.net.URLClassLoader; +import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReentrantLock; @@ -18,8 +21,9 @@ public class SlimJar { private static final ReentrantLock lock = new ReentrantLock(); private static final AtomicBoolean loaded = new AtomicBoolean(); + private static URLClassLoader nativeProviders; - public static void loadBootstrap(Path downloadPath, BootstrapLogger logger) { + public static void loadBootstrap(Path downloadPath, String minecraftVersion, BootstrapLogger logger) { if (loaded.get()) { return; } @@ -28,7 +32,8 @@ public class SlimJar { if (loaded.get()) { return; } - ApplicationBuilder.appending("Iris") + NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(minecraftVersion); + libraries.configure(ApplicationBuilder.appending("Iris"), downloadPath) .injectableFactory(InjectableFactory.selecting( InjectableFactory.ERROR, InjectableFactory.INJECTABLE, @@ -52,6 +57,7 @@ public class SlimJar { } }) .build(); + nativeProviders = libraries.openProviderLoader(downloadPath); loaded.set(true); } finally { lock.unlock(); @@ -71,36 +77,29 @@ public class SlimJar { VolmitPlugin plugin = BukkitPlatform.volmitPlugin(); Path downloadPath = plugin.getDataFolder("cache", "libraries").toPath(); debug(plugin, "Loading libraries..."); - try { - new SpigotApplicationBuilder(plugin) - .downloadDirectoryPath(downloadPath) - .debug(DEBUG) - .build(); - } catch (Throwable e) { - // The Spigot builder is a probe: not every server exposes it, and the fallback is the - // supported path on the ones that do not. - debug(plugin, "Failed to inject the library loader, falling back to application builder"); - ApplicationBuilder.appending(plugin.getName()) - .injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, InjectableFactory.WRAPPED, InjectableFactory.UNSAFE)) - .downloadDirectoryPath(downloadPath) - .logger(new ProcessLogger() { - @Override - public void info(@NotNull String message, @Nullable Object... args) { - SlimJar.debug(plugin, message.formatted(args)); - } + NativeRuntimeLibraries libraries = NativeRuntimeLibraries.load(Bukkit.getBukkitVersion()); + libraries.configure(ApplicationBuilder.appending(plugin.getName()), downloadPath) + .injectableFactory(InjectableFactory.selecting(InjectableFactory.ERROR, InjectableFactory.INJECTABLE, + InjectableFactory.WRAPPED, InjectableFactory.UNSAFE)) + .downloadDirectoryPath(downloadPath) + .logger(new ProcessLogger() { + @Override + public void info(@NotNull String message, @Nullable Object... args) { + SlimJar.debug(plugin, message.formatted(args)); + } - @Override - public void error(@NotNull String message, @Nullable Object... args) { - plugin.getLogger().severe(message.formatted(args)); - } + @Override + public void error(@NotNull String message, @Nullable Object... args) { + plugin.getLogger().severe(message.formatted(args)); + } - @Override - public void debug(@NotNull String message, @Nullable Object... args) { - SlimJar.debug(plugin, message.formatted(args)); - } - }) - .build(); - } + @Override + public void debug(@NotNull String message, @Nullable Object... args) { + SlimJar.debug(plugin, message.formatted(args)); + } + }) + .build(); + nativeProviders = libraries.openProviderLoader(downloadPath); loaded.set(true); debug(plugin, "Libraries loaded successfully!"); } finally { @@ -108,6 +107,19 @@ public class SlimJar { } } + public static void closeNativeProviders() throws IOException { + lock.lock(); + try { + if (nativeProviders != null) { + NativeAdapters.releaseProviderLoader(nativeProviders); + nativeProviders.close(); + nativeProviders = null; + } + } finally { + lock.unlock(); + } + } + private static void debug(VolmitPlugin plugin, String message) { if (DEBUG) { plugin.getLogger().info("[DEBUG] " + message); diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiome.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiome.java index 89800c39d..f1ed8543c 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiome.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiome.java @@ -18,7 +18,7 @@ package art.arcane.iris.platform.bukkit; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; import org.bukkit.NamespacedKey; @@ -27,7 +27,7 @@ import org.bukkit.block.Biome; /** * Interned Bukkit adapter for a neutral biome handle. */ -public final class BukkitBiome implements PlatformBiome { +public final class BukkitBiome implements NativeBiome { private static final Cache CACHE = Caffeine.newBuilder() .weakKeys() .maximumSize(4_096) diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiomeWriter.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiomeWriter.java index 1ef5df81f..cbf2690c7 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiomeWriter.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBiomeWriter.java @@ -19,7 +19,7 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; import java.util.ArrayList; @@ -31,13 +31,13 @@ import java.util.List; public final class BukkitBiomeWriter implements PlatformBiomeWriter { @Override public int biomeIdFor(String key) { - return INMS.get().getBiomeBaseIdForKey(key); + return INMS.get().getBiomeId(key); } @Override - public List allBiomes() { + public List allBiomes() { List natives = INMS.get().getBiomes(); - List biomes = new ArrayList<>(natives.size()); + List biomes = new ArrayList<>(natives.size()); for (Object biome : natives) { biomes.add(BukkitPlatform.wrapBiome(biome)); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockResolution.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockResolution.java index 57e19d7fd..48b5736d9 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockResolution.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockResolution.java @@ -22,12 +22,12 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.data.DataType; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.pack.validation.IrisCompat; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.platform.bukkit.registry.Materials; +import art.arcane.iris.platform.registry.Materials; import art.arcane.iris.platform.reflect.KeyedType; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java index 34a164136..9ccbda4f5 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitBlockState.java @@ -20,11 +20,11 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.integration.Identifier; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.platform.BlockStateKey; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.BlockStateKey; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KMap; @@ -42,7 +42,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * Interned Bukkit adapter for a neutral block state backed by BlockData. */ -public final class BukkitBlockState implements PlatformBlockState { +public final class BukkitBlockState implements NativeBlockState { private static final Map CUSTOM_NAMED_FACES = Map.of( "north", BlockFace.NORTH, "south", BlockFace.SOUTH, "east", BlockFace.EAST, "west", BlockFace.WEST, @@ -237,7 +237,12 @@ public final class BukkitBlockState implements PlatformBlockState { } @Override - public PlatformBlockState placementBaseState() { + public Object placementHandle() { + return data instanceof IrisCustomData custom ? custom.getBase() : data; + } + + @Override + public NativeBlockState placementBaseState() { return data instanceof IrisCustomData custom ? of(custom.getBase()) : this; } @@ -382,12 +387,12 @@ public final class BukkitBlockState implements PlatformBlockState { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return BukkitBlockResolution.canPlaceOnto(data.getMaterial(), ((BlockData) onto.nativeHandle()).getMaterial()); } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return data.matches((BlockData) state.nativeHandle()); } @@ -407,7 +412,7 @@ public final class BukkitBlockState implements PlatformBlockState { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { if (data instanceof IrisCustomData custom) { if (ExternalDataSVC.parseState(custom.getCustom()).getB().containsKey(name)) { String merged = BlockStateKey.withProperty(key, name, value); diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitEntityType.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitEntityType.java index e3f7043bd..580bbac70 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitEntityType.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitEntityType.java @@ -19,7 +19,7 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.spi.PlatformEntityType; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; import org.bukkit.entity.EntityType; import java.util.concurrent.ConcurrentHashMap; @@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * Interned Bukkit adapter for a neutral entity type handle. */ -public final class BukkitEntityType implements PlatformEntityType { +public final class BukkitEntityType implements NativeEntityType { private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); private final EntityType type; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitItem.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitItem.java index f98b0ed1b..71af5b658 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitItem.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitItem.java @@ -18,7 +18,7 @@ package art.arcane.iris.platform.bukkit; -import art.arcane.iris.spi.PlatformItem; +import art.arcane.volmlib.nativelib.item.NativeItem; import org.bukkit.Material; import java.util.concurrent.ConcurrentHashMap; @@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap; /** * Interned Bukkit adapter for a neutral item handle backed by Material. */ -public final class BukkitItem implements PlatformItem { +public final class BukkitItem implements NativeItem { private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); private final Material material; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java index 2d40c6bd5..d6fb5f1bc 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitPlatform.java @@ -24,12 +24,12 @@ import art.arcane.iris.platform.bukkit.nms.MinecraftVersion; import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.LogLevel; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.platform.bukkit.plugin.VolmitPlugin; import art.arcane.iris.platform.bukkit.plugin.VolmitSender; import art.arcane.volmlib.util.collection.KMap; @@ -175,11 +175,11 @@ public final class BukkitPlatform implements IrisPlatform { return supplier.get(); } - public static World unwrapWorld(PlatformWorld world) { + public static World unwrapWorld(NativeWorld world) { return (World) world.nativeHandle(); } - public static PlatformBiome wrapBiome(Object biome) { + public static NativeBiome wrapBiome(Object biome) { return BukkitBiome.of((Biome) biome); } @@ -335,7 +335,7 @@ public final class BukkitPlatform implements IrisPlatform { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { if (world == null || entityKey == null || !(world.nativeHandle() instanceof World bukkitWorld)) { return false; } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitRegistries.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitRegistries.java index aad7e9933..d8db630f0 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitRegistries.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitRegistries.java @@ -21,16 +21,16 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.data.DataType; import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; import art.arcane.iris.spi.PlatformGenerationRegistry; -import art.arcane.iris.spi.PlatformItem; -import art.arcane.iris.spi.PlatformNumericRange; +import art.arcane.volmlib.nativelib.item.NativeItem; +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.platform.reflect.KeyedType; import art.arcane.volmlib.util.json.JSONArray; @@ -61,7 +61,7 @@ public final class BukkitRegistries implements PlatformRegistries { } @Override - public PlatformBlockState block(String key) { + public NativeBlockState block(String key) { BlockData data = BukkitBlockResolution.get(key); return data == null ? null : BukkitBlockState.of(data); } @@ -70,29 +70,29 @@ public final class BukkitRegistries implements PlatformRegistries { // BukkitBlockResolution.getOrNull, which substitutes air for an unregistered key and feeds the Bukkit-only // IrisCompat rewrite table used by block(). @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { BlockData data = BukkitBlockResolution.resolveOrNull(key); return data == null ? null : BukkitBlockState.of(data); } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { BlockData data = BukkitBlockResolution.resolveOrNull(key, warn); return data == null ? null : BukkitBlockState.of(data); } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return BukkitBlockState.of(BukkitBlockResolution.getAir()); } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return BukkitBlockState.of(BukkitBlockResolution.toDeepSlateOre((BlockData) block.nativeHandle(), (BlockData) ore.nativeHandle())); } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { NamespacedKey namespacedKey = NamespacedKey.fromString(key); if (namespacedKey == null) { return null; @@ -102,13 +102,13 @@ public final class BukkitRegistries implements PlatformRegistries { } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { Material material = Material.matchMaterial(key); return material == null ? null : BukkitItem.of(material); } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { NamespacedKey namespacedKey = NamespacedKey.fromString(key); if (namespacedKey == null) { return null; @@ -223,17 +223,17 @@ public final class BukkitRegistries implements PlatformRegistries { } @Override - public Map> blockStateProperties() { - Map> properties = new LinkedHashMap<>(); + public Map> blockStateProperties() { + Map> properties = new LinkedHashMap<>(); BukkitBlockResolution.getBlockStates().forEach((blocks, blockProperties) -> { if (blocks.isEmpty()) { return; } - List converted = new ArrayList<>(blockProperties.size()); + List converted = new ArrayList<>(blockProperties.size()); for (BlockProperty blockProperty : blockProperties) { converted.add(toPlatformProperty(blockProperty)); } - List shared = List.copyOf(converted); + List shared = List.copyOf(converted); for (String block : blocks) { properties.put(block, shared); } @@ -241,7 +241,7 @@ public final class BukkitRegistries implements PlatformRegistries { return properties; } - private static PlatformBlockProperty toPlatformProperty(BlockProperty property) { + private static NativeBlockProperty toPlatformProperty(BlockProperty property) { JSONObject json = property.buildJson(); Object defaultValue = json.has("default") ? json.get("default") : null; List allowedValues = new ArrayList<>(); @@ -251,10 +251,10 @@ public final class BukkitRegistries implements PlatformRegistries { allowedValues.add(values.get(index)); } } - PlatformNumericRange range = null; + NativeNumericRange range = null; if (json.has("minimum")) { - range = new PlatformNumericRange(json.getDouble("minimum"), json.getDouble("maximum"), json.getBoolean("exclusiveMinimum"), json.getBoolean("exclusiveMaximum")); + range = new NativeNumericRange(json.getDouble("minimum"), json.getDouble("maximum"), json.getBoolean("exclusiveMinimum"), json.getBoolean("exclusiveMaximum")); } - return new PlatformBlockProperty(property.name(), json.getString("type"), defaultValue, List.copyOf(allowedValues), range); + return new NativeBlockProperty(property.name(), json.getString("type"), defaultValue, List.copyOf(allowedValues), range); } } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitScheduler.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitScheduler.java index acf173f97..6d83e2ab3 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitScheduler.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitScheduler.java @@ -19,7 +19,7 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.spi.PlatformScheduler; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.world.task.J; import org.bukkit.World; @@ -33,7 +33,7 @@ public final class BukkitScheduler implements PlatformScheduler { } @Override - public void region(PlatformWorld world, int chunkX, int chunkZ, Runnable task) { + public void region(NativeWorld world, int chunkX, int chunkZ, Runnable task) { World bukkitWorld = (World) world.nativeHandle(); if (!J.runRegion(bukkitWorld, chunkX, chunkZ, task)) { J.s(task); @@ -51,7 +51,7 @@ public final class BukkitScheduler implements PlatformScheduler { } @Override - public void laterRegion(PlatformWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { + public void laterRegion(NativeWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { World bukkitWorld = (World) world.nativeHandle(); if (!J.runRegion(bukkitWorld, chunkX, chunkZ, task, ticks)) { J.s(task, ticks); diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitStructureHooks.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitStructureHooks.java index 4bf0d0d29..f35802e4d 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitStructureHooks.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitStructureHooks.java @@ -18,9 +18,11 @@ package art.arcane.iris.platform.bukkit; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; + import art.arcane.iris.platform.bukkit.nms.INMS; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.lang.reflect.Method; import java.util.ArrayList; @@ -76,22 +78,22 @@ public final class BukkitStructureHooks implements PlatformStructureHooks { } @Override - public List reachableStructureKeys(PlatformWorld world) { + public List reachableStructureKeys(NativeWorld world) { return new ArrayList<>(INMS.get().getReachableStructureKeys(BukkitPlatform.unwrapWorld(world))); } @Override - public List possibleBiomeKeys(PlatformWorld world) { + public List possibleBiomeKeys(NativeWorld world) { return new ArrayList<>(INMS.get().getPossibleBiomeKeys(BukkitPlatform.unwrapWorld(world))); } @Override - public boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed) { + public boolean placeFeature(NativeWorld world, int x, int y, int z, String featureKey, long seed) { return INMS.get().placeFeature(BukkitPlatform.unwrapWorld(world), x, y, z, featureKey, seed); } @Override - public int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { + public int[] placeStructure(NativeWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { return INMS.get().placeStructure(BukkitPlatform.unwrapWorld(world), chunkX, chunkZ, structureKey, seed, maxSpan); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitWorld.java b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitWorld.java index 0a570ae54..ff2f550f0 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitWorld.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/BukkitWorld.java @@ -18,16 +18,16 @@ package art.arcane.iris.platform.bukkit; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import org.bukkit.World; import org.bukkit.block.data.BlockData; /** * Bukkit adapter for a neutral world view backed by org.bukkit.World. */ -public final class BukkitWorld implements PlatformWorld { +public final class BukkitWorld implements NativeWorld { private final World world; public BukkitWorld(World world) { @@ -55,17 +55,17 @@ public final class BukkitWorld implements PlatformWorld { } @Override - public PlatformBlockState getBlock(int x, int y, int z) { + public NativeBlockState getBlock(int x, int y, int z) { return BukkitBlockState.of(world.getBlockAt(x, y, z).getBlockData()); } @Override - public void setBlock(int x, int y, int z, PlatformBlockState block, int flags) { + public void setBlock(int x, int y, int z, NativeBlockState block, int flags) { world.getBlockAt(x, y, z).setBlockData((BlockData) block.nativeHandle(), (flags & 1) != 0); } @Override - public PlatformBiome getBiome(int x, int y, int z) { + public NativeBiome getBiome(int x, int y, int z) { return BukkitBiome.of(world.getBiome(x, y, z)); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBinding.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBinding.java new file mode 100644 index 000000000..2f55a4685 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBinding.java @@ -0,0 +1,161 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.terrain.IrisDimensionRuntimeContract; +import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.platform.generation.BukkitChunkGenerator; +import art.arcane.iris.spi.PlatformGenerationRegistry; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.world.history.SavedTerrainChunk; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainAccess; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainSnapshots; +import art.arcane.volmlib.nativelib.terrain.NativeWorldGeneration; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeWorldLifecycleFactory; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; +import art.arcane.volmlib.util.collection.KList; +import art.arcane.volmlib.util.nbt.tag.CompoundTag; +import lombok.experimental.Delegate; +import org.bukkit.Bukkit; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import java.util.Arrays; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public final class BukkitBinding implements INMSBinding { + @Delegate + private final NativeTerrainAccess terrain; + @Delegate + private final NativeWorldLifecycleFactory.Controller worldLifecycle; + private final NativeWorldGeneration generation; + private final NativeTerrainSnapshots snapshots; + private final NativeGenerationRegistry registry; + private volatile DataVersion dataVersion; + + public BukkitBinding() { + this(new Capabilities(NativeAdapters.require(NativeTerrainAccess.class), + NativeAdapters.require(NativeWorldGeneration.class), NativeAdapters.require(NativeTerrainSnapshots.class), + NativeAdapters.require(NativeGenerationRegistry.class))); + } + + public BukkitBinding(Capabilities capabilities) { + terrain = capabilities.terrain(); + generation = capabilities.generation(); + snapshots = capabilities.snapshots(); + registry = capabilities.registry(); + worldLifecycle = generation.lifecycle(BukkitWorldLifecyclePolicy.INSTANCE); + } + + @Override + public PlatformGenerationRegistry generationRegistry() { + return new BukkitGenerationRegistry(registry, "bukkit-generation-registry-v1", generation.generationRendererIdentity()); + } + + @Override + public CompoundTag serializeEntity(Entity entity) { return null; } + + @Override + public Entity deserializeEntity(CompoundTag tag, Location position) { return null; } + + @Override + public boolean supportsCustomHeight() { return true; } + + @Override + public int getMinHeight(World world) { return world.getMinHeight(); } + + @Override + public boolean supportsCustomBiomes() { return true; } + + @Override + public boolean supportsIrisWorldGeneration() { return true; } + + @Override + public boolean supportsStructureCapture() { return true; } + + @Override + public boolean supportsDataPacks() { return true; } + + @Override + public KList nativeStructureVolumes(Engine engine, int minX, int minZ, int maxX, int maxZ) { + return NativeStructureVolumeIndex.volumes(engine, minX, minZ, maxX, maxZ); + } + + @Override + public void invalidateNativeStructureVolumeIndex(Engine engine) { NativeStructureVolumeIndex.invalidate(engine); } + + @Override + public CompletableFuture flushSavedTerrainCapture(World world) { + return snapshots.flush(world, SavedTerrainSnapshotPolicy.INSTANCE); + } + + @Override + public CompletableFuture captureSavedTerrainChunk(World world, int chunkX, int chunkZ, int minimumY, int height) { + return snapshots.capture(new NativeTerrainSnapshots.CaptureRequest(world, chunkX, chunkZ, minimumY, height), SavedTerrainSnapshotPolicy.INSTANCE); + } + + @Override + public void inject(long seed, Engine engine, World world) throws NoSuchFieldException, IllegalAccessException { + validateDimension(engine, world, generation.dimension(world)); + generation.inject(world, new BukkitGeneratorContext(new BukkitGeneratorContext.Options(seed, engine, world))); + } + + @Override + public DatapackStructureScopeResult scopeDatapackStructures(World world, DatapackStructureScopeIndex scope, + Set declaredSources, IrisImportedStructureControl control) + throws NoSuchFieldException, IllegalAccessException { + BukkitChunkGenerator generator = world.getGenerator() instanceof BukkitChunkGenerator current ? current : null; + NativeWorldGeneration.ScopeMode mode = generator != null && generator.isAuthoringStudio() + ? NativeWorldGeneration.ScopeMode.AUTHORING + : generator != null && generator.isStudioEntryBootstrapActive() + ? NativeWorldGeneration.ScopeMode.RETAINED : NativeWorldGeneration.ScopeMode.IMMEDIATE; + NativeWorldGeneration.ScopeResult result = generation.scope(new NativeWorldGeneration.ScopeRequest(world, scope, declaredSources, control, mode)); + return new DatapackStructureScopeResult(result.retainedManagedSets(), result.excludedManagedSets()); + } + + @Override + public CompletableFuture completeStudioStructureBootstrap(World world) throws NoSuchFieldException, IllegalAccessException { + return generation.completeBootstrap(world); + } + + @Override + public void abandonStudioStructureBootstrap(World world) { generation.abandonBootstrap(world); } + + @Override + public boolean missingDimensionTypes(String... keys) { + return generation.missingDimensionTypes(Arrays.stream(keys).map(key -> new NamespacedKey("iris", key)).toList()); + } + + @Override + public DataVersion getDataVersion() { + DataVersion cached = dataVersion; + if (cached == null) { + MinecraftVersion version = MinecraftVersion.detect(Bukkit.getServer()); + cached = DataVersion.forMinecraftVersion(version == null ? null : version.value()); + dataVersion = cached; + } + return cached; + } + + @Override + public int getSpawnChunkCount(World world) { return 0; } + + private static void validateDimension(Engine engine, World world, NativeWorldGeneration.Dimension dimension) { + IrisDimensionRuntimeContract expected = IrisDimensionRuntimeContract.expected(engine.getDimension(), "iris"); + IrisDimensionRuntimeContract actual = new IrisDimensionRuntimeContract(dimension.key(), dimension.minimumY(), dimension.height(), dimension.logicalHeight()); + String runtimeName = "Bukkit world '" + world.getName() + "'"; + expected.requireExact(runtimeName, actual); + expected.requireHeight(runtimeName, dimension.worldMinimumY(), dimension.worldHeight()); + expected.requireHeight(runtimeName, world.getMinHeight(), world.getMaxHeight() - world.getMinHeight()); + IrisLogging.debug("Loaded world " + world.getName() + " with exact Iris dimension type " + dimension.key()); + } + + public record Capabilities(NativeTerrainAccess terrain, NativeWorldGeneration generation, + NativeTerrainSnapshots snapshots, NativeGenerationRegistry registry) { } +} diff --git a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSource.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBiomePolicy.java similarity index 65% rename from adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSource.java rename to core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBiomePolicy.java index 4e4d5ea8a..314455cc3 100644 --- a/adapters/bukkit/nms/v26_2_R1/src/main/java/art/arcane/iris/platform/bukkit/nms/v26_2_R1/CustomBiomeSource.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitBiomePolicy.java @@ -1,13 +1,14 @@ -package art.arcane.iris.platform.bukkit.nms.v26_2_R1; +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.volmlib.nativelib.terrain.NativeBiomeRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy; +import java.util.function.IntUnaryOperator; import art.arcane.iris.platform.bukkit.BukkitWorldBinding; -import com.mojang.datafixers.util.Pair; -import com.mojang.serialization.MapCodec; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.generation.runtime.DimensionStackContext; import art.arcane.iris.generation.runtime.DimensionStackLayout; import art.arcane.iris.generation.runtime.DimensionTerrainContext; -import art.arcane.iris.generation.cache.AtomicCache; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.GenerationSessionException; @@ -21,28 +22,9 @@ import art.arcane.iris.platform.generation.BukkitChunkGenerator; import art.arcane.iris.generation.context.IrisContext; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.math.RNG; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; -import net.minecraft.core.Holder; -import net.minecraft.core.QuartPos; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.util.RandomSource; -import net.minecraft.util.Mth; -import net.minecraft.world.level.LevelReader; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.biome.Climate; -import org.bukkit.Bukkit; import org.bukkit.World; -import org.bukkit.craftbukkit.CraftServer; -import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.block.Biome; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -55,9 +37,8 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; -import java.util.stream.Stream; -public class CustomBiomeSource extends BiomeSource { +public final class BukkitBiomePolicy implements NativeBiomeSourcePolicy { private static final int NOISE_BIOME_CACHE_MAX = 262144; private static final int STRONGHOLD_RING_SEARCH_Y = 0; private static final int STRONGHOLD_RING_SEARCH_RADIUS = 112; @@ -66,24 +47,25 @@ public class CustomBiomeSource extends BiomeSource { private final long seed; private final Engine engine; private final BukkitChunkGenerator platformGenerator; - private final Registry biomeCustomRegistry; - private final Registry biomeRegistry; - private final AtomicCache registryAccess = new AtomicCache<>(); - private final Holder fallbackBiome; - private final ConcurrentHashMap> noiseBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> structureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> surfaceStructureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap> naturalSurfaceStructureBiomeCache = new ConcurrentHashMap<>(); - private final ConcurrentHashMap runtimeBiomeStates = new ConcurrentHashMap<>(); + private final NativeBiomeRegistry biomeCustomRegistry; + private final NativeBiomeRegistry biomeRegistry; + private final H fallbackBiome; + private final ConcurrentHashMap noiseBiomeCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap structureBiomeCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap surfaceStructureBiomeCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap naturalSurfaceStructureBiomeCache = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> runtimeBiomeStates = new ConcurrentHashMap<>(); - public CustomBiomeSource(long seed, Engine engine, World world) { + public BukkitBiomePolicy(RuntimeOptions options, NativeBiomeRegistry registry) { + Engine engine = options.engine(); + World world = options.world(); this.engine = engine; this.platformGenerator = world.getGenerator() instanceof BukkitChunkGenerator generator ? generator : null; - this.seed = seed; - this.biomeCustomRegistry = registry().lookup(Registries.BIOME).orElse(null); - this.biomeRegistry = ((RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())).lookup(Registries.BIOME).orElse(null); + this.seed = options.seed(); + this.biomeCustomRegistry = registry; + this.biomeRegistry = biomeCustomRegistry; this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry); if (engine instanceof IrisEngine irisEngine) { irisEngine.addGenerationRuntimeRetirementListener(this::evictRuntimeCaches); @@ -91,17 +73,17 @@ public class CustomBiomeSource extends BiomeSource { runtimeBiomeState(); } - private static List> getAllBiomes( - Registry customRegistry, - Registry registry, + private static List getAllBiomes( + NativeBiomeRegistry customRegistry, + NativeBiomeRegistry registry, Engine engine, boolean includeDimensionStack ) { - LinkedHashSet> biomes = new LinkedHashSet<>(); + LinkedHashSet biomes = new LinkedHashSet<>(); for (OwnedBiome ownedBiome : getHostOwnedBiomes(engine)) { IrisBiome i = ownedBiome.biome(); - Holder vanillaHolder = resolveBiomeHolder(registry, i.getStructureDerivativeKey()); + H vanillaHolder = resolveBiomeHolder(registry, i.getStructureDerivativeKey()); if (vanillaHolder == null) { throw new IllegalStateException("Iris structure biome derivative '" + i.getStructureDerivativeKey() + "' is not registered for biome '" + i.getLoadKey() + "'"); @@ -125,19 +107,19 @@ public class CustomBiomeSource extends BiomeSource { return new ArrayList<>(biomes); } - private static List> getVisibleBiomes( - Registry customRegistry, - Registry registry, + private static List getVisibleBiomes( + NativeBiomeRegistry customRegistry, + NativeBiomeRegistry registry, Engine engine, - Holder fallback + H fallback ) { - LinkedHashSet> biomes = new LinkedHashSet<>(); + LinkedHashSet biomes = new LinkedHashSet<>(); boolean fallbackPossible = false; for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) { IrisBiome irisBiome = ownedBiome.biome(); if (irisBiome.isCustom()) { for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - Holder customHolder = resolveCustomBiomeHolder( + H customHolder = resolveCustomBiomeHolder( customRegistry, engine, ownedBiome.dimension(), customBiome.getId()); if (customHolder == null) { throw new IllegalStateException("Iris custom visible biome '" @@ -163,12 +145,12 @@ public class CustomBiomeSource extends BiomeSource { return new ArrayList<>(biomes); } - private static boolean addVisibleBiomeHolder( - Set> biomes, - Registry registry, + private static boolean addVisibleBiomeHolder( + Set biomes, + NativeBiomeRegistry registry, String biomeKey ) { - Holder holder = resolveBiomeHolder(registry, biomeKey); + H holder = resolveBiomeHolder(registry, biomeKey); if (holder == null) { return false; } @@ -176,84 +158,18 @@ public class CustomBiomeSource extends BiomeSource { return true; } - private static Object getFor(Class type, Object source) { - Object o = fieldFor(type, source); - - if (o != null) { - return o; - } - - o = invokeFor(type, source); - - if (o != null) { - return o; - } - - throw new IllegalStateException("Iris cannot resolve a " + type.getName() - + " from " + source.getClass().getName() + " on this server version"); - } - - private static Object fieldFor(Class returns, Object in) { - for (Class sourceType = in.getClass(); sourceType != null; sourceType = sourceType.getSuperclass()) { - Object o = fieldForClass(returns, sourceType, in); - - if (o != null) { - return o; - } - } - - return null; - } - - private static Object invokeFor(Class returns, Object in) { - for (Method i : in.getClass().getMethods()) { - if (i.getReturnType().equals(returns)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returns.getSimpleName() + " in " + in.getClass().getSimpleName() + "." + i.getName() + "()"); - return i.invoke(in); - } catch (ReflectiveOperationException | RuntimeException e) { - throw new IllegalStateException("Iris failed to invoke " + in.getClass().getName() + "." - + i.getName() + "() for " + returns.getName(), e); - } - } - } - - return null; - } - @SuppressWarnings("unchecked") - private static T fieldForClass(Class returnType, Class sourceType, Object in) { - for (Field i : sourceType.getDeclaredFields()) { - if (i.getType().equals(returnType)) { - i.setAccessible(true); - try { - IrisLogging.debug("[NMS] Found " + returnType.getSimpleName() + " in " + sourceType.getSimpleName() + "." + i.getName()); - return (T) i.get(in); - } catch (IllegalAccessException | RuntimeException e) { - throw new IllegalStateException("Iris failed to read " + sourceType.getName() + "." - + i.getName() + " for " + returnType.getName(), e); - } - } - } - return null; - } @Override - protected Stream> collectPossibleBiomes() { - return possibleBiomes(true).stream(); - } - - @Override - public Set> possibleBiomes() { + public Set possibleBiomes() { return possibleBiomes(true); } - Set> possibleStructureBiomes() { + public Set possibleStructureBiomes() { return possibleBiomes(false); } - private Set> possibleBiomes(boolean includeDimensionStack) { + private Set possibleBiomes(boolean includeDimensionStack) { GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_possible_biomes"); if (lease == null) { throw new IllegalStateException("Iris possible biome lookup was rejected during an engine transition"); @@ -267,18 +183,15 @@ public class CustomBiomeSource extends BiomeSource { if (world == null) { throw new IllegalStateException("Iris biome source has no bound Bukkit world"); } - Registry customRegistry = ((RegistryAccess) getFor( - RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())) - .lookup(Registries.BIOME).orElse(null); - Registry worldRegistry = ((CraftWorld) world).getHandle().registryAccess() - .lookup(Registries.BIOME).orElse(null); + NativeBiomeRegistry customRegistry = biomeCustomRegistry; + NativeBiomeRegistry worldRegistry = biomeRegistry.forWorld(world); return Set.copyOf(getAllBiomes( customRegistry, worldRegistry, engine, includeDimensionStack)); } } - private KMap> fillCustomBiomes(Registry customRegistry, Engine engine, Holder fallback) { - KMap> m = new KMap<>(); + private KMap fillCustomBiomes(NativeBiomeRegistry customRegistry, Engine engine, H fallback) { + KMap m = new KMap<>(); if (customRegistry == null) { return m; } @@ -288,7 +201,7 @@ public class CustomBiomeSource extends BiomeSource { if (i.isCustom()) { for (IrisBiomeCustom j : i.getCustomDerivitives()) { String key = customBiomeKey(ownedBiome.dimension(), j.getId()); - Holder holder = resolveCustomBiomeHolder( + H holder = resolveCustomBiomeHolder( customRegistry, engine, ownedBiome.dimension(), j.getId()); if (holder == null) { if (fallback != null) { @@ -306,8 +219,8 @@ public class CustomBiomeSource extends BiomeSource { return m; } - private Map> fillVanillaSpawnBiomes(Registry customRegistry, Registry registry, Engine engine) { - IdentityHashMap> spawnBiomes = new IdentityHashMap<>(); + private Map fillVanillaSpawnBiomes(NativeBiomeRegistry customRegistry, NativeBiomeRegistry registry, Engine engine) { + IdentityHashMap spawnBiomes = new IdentityHashMap<>(); if (customRegistry == null || registry == null) { return Collections.unmodifiableMap(spawnBiomes); } @@ -317,15 +230,15 @@ public class CustomBiomeSource extends BiomeSource { if (!irisBiome.isCustom()) { continue; } - Holder vanillaHolder = NMSBinding.biomeToBiomeBase(registry, irisBiome.getVanillaDerivative()); + H vanillaHolder = registry.lookup(irisBiome.getVanillaDerivative()); if (vanillaHolder == null) { continue; } for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) { - Holder customHolder = resolveCustomBiomeHolder( + H customHolder = resolveCustomBiomeHolder( customRegistry, engine, ownedBiome.dimension(), customBiome.getId()); if (customHolder != null) { - spawnBiomes.putIfAbsent(customHolder.value(), vanillaHolder); + spawnBiomes.putIfAbsent(customRegistry.value(customHolder), vanillaHolder); } } } @@ -333,11 +246,11 @@ public class CustomBiomeSource extends BiomeSource { return Collections.unmodifiableMap(spawnBiomes); } - Holder getRetainedVanillaSpawnBiome(String derivativeKey) { + public H getRetainedVanillaSpawnBiome(String derivativeKey) { return resolveBiomeHolder(biomeRegistry, derivativeKey); } - Holder getVanillaSpawnBiome(Holder biome) { + public H getVanillaSpawnBiome(H biome) { if (biome == null) { return null; } @@ -349,11 +262,11 @@ public class CustomBiomeSource extends BiomeSource { if (!isRuntimeAvailable()) { return null; } - return runtimeBiomeState().vanillaSpawnBiomes().get(biome.value()); + return runtimeBiomeState().vanillaSpawnBiomes().get(biomeRegistry.value(biome)); } } - Holder getVisibleSurfaceBiome(int blockX, int blockZ) { + public H getVisibleSurfaceBiome(int blockX, int blockZ) { GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_surface_spawn_biome"); if (lease == null) { return null; @@ -366,10 +279,10 @@ public class CustomBiomeSource extends BiomeSource { return fallbackBiome; } runtimeBiomeState(); - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - int sampleX = QuartPos.toBlock(quartX); - int sampleZ = QuartPos.toBlock(quartZ); + int quartX = ((blockX) >> 2); + int quartZ = ((blockZ) >> 2); + int sampleX = ((quartX) << 2); + int sampleZ = ((quartZ) << 2); boolean cacheable = isBiomeCacheable(engine, sampleX, sampleZ); DimensionStackContext stackContext = engine.getDimensionStackContext(); DimensionStackLayout.Layer layer = stackContext == null @@ -378,29 +291,14 @@ public class CustomBiomeSource extends BiomeSource { int internalY = layer == null ? Engine.hostHeight(engine, sampleX, sampleZ, true) : layer.clippedSurfaceY(); - int quartY = QuartPos.fromBlock(internalY + engine.getWorld().minHeight()); + int quartY = ((internalY + engine.getWorld().minHeight()) >> 2); return getVisibleNoiseBiomeWithActiveGenerationLease( - quartX, quartY, quartZ, null, null, cacheable); + quartX, quartY, quartZ, null, cacheable); } } - private RegistryAccess registry() { - RegistryAccess access = registryAccess.aquire(() -> (RegistryAccess) getFor(RegistryAccess.Frozen.class, ((CraftServer) Bukkit.getServer()).getHandle().getServer())); - - if (access == null) { - throw new IllegalStateException("Iris cannot resolve the Minecraft registry access on this server version"); - } - - return access; - } - @Override - protected MapCodec codec() { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + public H getNoiseBiome(int x, int y, int z) { int blockX = x << 2; int blockZ = z << 2; try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = @@ -416,7 +314,7 @@ public class CustomBiomeSource extends BiomeSource { } } - private Holder getStructureNoiseBiomeWithActiveGenerationLease(int x, int y, int z) { + private H getStructureNoiseBiomeWithActiveGenerationLease(int x, int y, int z) { if (platformGenerator != null && platformGenerator.usesFlatStudioTerrain()) { return fallbackBiome; } @@ -428,17 +326,17 @@ public class CustomBiomeSource extends BiomeSource { RuntimeNoiseKey cacheKey = new RuntimeNoiseKey( engine.getCacheID(), packNoiseKey(x, y, z)); if (cacheable) { - Holder cachedHolder = structureBiomeCache.get(cacheKey); + H cachedHolder = structureBiomeCache.get(cacheKey); if (cachedHolder != null) { return cachedHolder; } } - Holder resolvedHolder = resolveStructureBiomeHolder(x, y, z); + H resolvedHolder = resolveStructureBiomeHolder(x, y, z); if (!cacheable) { return resolvedHolder; } - Holder existingHolder = structureBiomeCache.putIfAbsent(cacheKey, resolvedHolder); + H existingHolder = structureBiomeCache.putIfAbsent(cacheKey, resolvedHolder); if (existingHolder != null) { return existingHolder; } @@ -451,18 +349,16 @@ public class CustomBiomeSource extends BiomeSource { } @Override - public Pair> findBiomeHorizontal( - int x, - int y, - int z, - int searchRadius, - Predicate> allowed, - RandomSource random, - Climate.Sampler sampler - ) { + public BiomeLocation findBiomeHorizontal(HorizontalQuery query) { + int x = query.x(); + int y = query.y(); + int z = query.z(); + int searchRadius = query.radius(); + Predicate allowed = query.allowed(); + IntUnaryOperator random = query.random(); int quartStep = horizontalBiomeSearchQuartStep(y, searchRadius); if (quartStep == 1) { - return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler); + return query.fallback().get(); } GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_structure_ring_biome"); if (lease == null) { @@ -478,15 +374,7 @@ public class CustomBiomeSource extends BiomeSource { } @Override - public Pair> findClosestBiome3d( - BlockPos origin, - int searchRadius, - int sampleResolutionHorizontal, - int sampleResolutionVertical, - Predicate> allowed, - Climate.Sampler sampler, - LevelReader level - ) { + public BiomeLocation findClosestBiome3d(ClosestQuery query) { GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_locate_visible_biome"); if (lease == null) { throw new IllegalStateException("Iris visible biome search was rejected during an engine transition"); @@ -495,52 +383,41 @@ public class CustomBiomeSource extends BiomeSource { prepareVisibleBiomeBatch(); DimensionStackContext stackContext = engine.getDimensionStackContext(); if (stackContext == null) { - return super.findClosestBiome3d( - origin, searchRadius, sampleResolutionHorizontal, sampleResolutionVertical, - allowed, sampler, level); + return query.fallback().get(); } - Set> candidates = new HashSet<>(); - for (Holder biome : getVisibleBiomes( + Set candidates = new HashSet<>(); + for (H biome : getVisibleBiomes( biomeCustomRegistry, biomeRegistry, engine, fallbackBiome)) { - if (allowed.test(biome)) { + if (query.allowed().test(biome)) { candidates.add(biome); } } if (candidates.isEmpty()) { return null; } - int sampleRadius = Math.floorDiv(searchRadius, sampleResolutionHorizontal); - int[] sampleYs = Mth.outFromOrigin( - origin.getY(), level.getMinY() + 1, level.getMaxY() + 1, sampleResolutionVertical).toArray(); - for (BlockPos.MutableBlockPos sampleColumn : BlockPos.spiralAround( - BlockPos.ZERO, sampleRadius, Direction.EAST, Direction.SOUTH)) { - int blockX = origin.getX() + sampleColumn.getX() * sampleResolutionHorizontal; - int blockZ = origin.getZ() + sampleColumn.getZ() * sampleResolutionHorizontal; - int quartX = QuartPos.fromBlock(blockX); - int quartZ = QuartPos.fromBlock(blockZ); - DimensionStackLayout stackLayout = stackContext.sample( - QuartPos.toBlock(quartX), QuartPos.toBlock(quartZ)); - for (int blockY : sampleYs) { - int quartY = QuartPos.fromBlock(blockY); - Holder visibleBiome = resolveVisibleBiomeHolder( - quartX, quartY, quartZ, null, stackLayout); - if (candidates.contains(visibleBiome)) { - return Pair.of(new BlockPos(blockX, blockY, blockZ), visibleBiome); - } - } - } - return null; + return query.search().search(candidates, (blockX, blockZ) -> visibleColumn(stackContext, blockX, blockZ)); } } - static int horizontalBiomeSearchQuartStep(int blockY, int searchRadius) { + private Column visibleColumn(DimensionStackContext context, int blockX, int blockZ) { + int quartX = blockX >> 2; + int quartZ = blockZ >> 2; + DimensionStackLayout stackLayout = context.sample(quartX << 2, quartZ << 2); + return blockY -> resolveVisibleBiomeHolder(quartX, blockY >> 2, quartZ, null, stackLayout); + } + + public static int horizontalBiomeSearchQuartStep(int blockY, int searchRadius) { return blockY == STRONGHOLD_RING_SEARCH_Y && searchRadius == STRONGHOLD_RING_SEARCH_RADIUS ? STRONGHOLD_RING_SEARCH_QUART_STEP : 1; } @Override - public Set> getBiomesWithin(int x, int y, int z, int radius, Climate.Sampler sampler) { + public Set getBiomesWithin(NearbyQuery query) { + int x = query.x(); + int y = query.y(); + int z = query.z(); + int radius = query.radius(); GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_biomes_within"); if (lease == null) { throw new IllegalStateException("Iris biome radius lookup was rejected during an engine transition"); @@ -549,18 +426,18 @@ public class CustomBiomeSource extends BiomeSource { if (!isRuntimeAvailable()) { throw new IllegalStateException("Iris biome radius lookup has no active engine runtime"); } - int minQuartY = QuartPos.fromBlock(y - radius); + int minQuartY = ((y - radius) >> 2); boolean monumentQuery = radius == 29 && y == engine.getMinHeight() + engine.getDimension().getFluidHeight(); if (!monumentQuery && !isGuaranteedSurfaceBiome(minQuartY)) { - return super.getBiomesWithin(x, y, z, radius, sampler); + return query.fallback().get(); } - int minQuartX = QuartPos.fromBlock(x - radius); - int maxQuartX = QuartPos.fromBlock(x + radius); - int minQuartZ = QuartPos.fromBlock(z - radius); - int maxQuartZ = QuartPos.fromBlock(z + radius); + int minQuartX = ((x - radius) >> 2); + int maxQuartX = ((x + radius) >> 2); + int minQuartZ = ((z - radius) >> 2); + int maxQuartZ = ((z + radius) >> 2); int columns = (maxQuartX - minQuartX + 1) * (maxQuartZ - minQuartZ + 1); - Set> biomes = new HashSet<>(columns); + Set biomes = new HashSet<>(columns); for (int quartZ = minQuartZ; quartZ <= maxQuartZ; quartZ++) { for (int quartX = minQuartX; quartX <= maxQuartX; quartX++) { biomes.add(getSurfaceStructureBiomeHolder(quartX, quartZ)); @@ -570,19 +447,19 @@ public class CustomBiomeSource extends BiomeSource { } } - private Holder getSurfaceStructureBiomeHolder(int x, int z) { + private H getSurfaceStructureBiomeHolder(int x, int z) { int blockX = x << 2; int blockZ = z << 2; try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope(blockX, blockZ, "bukkit_surface_structure_biome")) { RuntimeColumnKey columnKey = new RuntimeColumnKey( engine.getCacheID(), packColumnKey(x, z)); - Holder surfaceHolder = surfaceStructureBiomeCache.get(columnKey); + H surfaceHolder = surfaceStructureBiomeCache.get(columnKey); if (surfaceHolder != null) { return surfaceHolder; } - Holder resolvedSurfaceHolder = resolveSurfaceStructureBiomeHolder(x, z); - Holder existingSurfaceHolder = surfaceStructureBiomeCache.putIfAbsent( + H resolvedSurfaceHolder = resolveSurfaceStructureBiomeHolder(x, z); + H existingSurfaceHolder = surfaceStructureBiomeCache.putIfAbsent( columnKey, resolvedSurfaceHolder); if (existingSurfaceHolder != null) { return existingSurfaceHolder; @@ -594,34 +471,31 @@ public class CustomBiomeSource extends BiomeSource { } } - private Pair> findNaturalSurfaceBiomeHorizontal( + private BiomeLocation findNaturalSurfaceBiomeHorizontal( int x, int y, int z, int searchRadius, int quartStep, - Predicate> allowed, - RandomSource random + Predicate allowed, + IntUnaryOperator random ) { - int centerQuartX = QuartPos.fromBlock(x); - int centerQuartZ = QuartPos.fromBlock(z); - int quartRadius = QuartPos.fromBlock(searchRadius); - Pair> selected = null; + int centerQuartX = ((x) >> 2); + int centerQuartZ = ((z) >> 2); + int quartRadius = ((searchRadius) >> 2); + BiomeLocation selected = null; int matches = 0; for (int radius = 0; radius <= quartRadius; radius += quartStep) { for (int offsetZ = -radius; offsetZ <= radius; offsetZ += quartStep) { for (int offsetX = -radius; offsetX <= radius; offsetX += quartStep) { int quartX = centerQuartX + offsetX; int quartZ = centerQuartZ + offsetZ; - Holder holder = getNaturalSurfaceStructureBiomeHolder(quartX, quartZ); + H holder = getNaturalSurfaceStructureBiomeHolder(quartX, quartZ); if (!allowed.test(holder)) { continue; } - if (selected == null || random.nextInt(matches + 1) == 0) { - selected = Pair.of(new BlockPos( - QuartPos.toBlock(quartX), - y, - QuartPos.toBlock(quartZ)), holder); + if (selected == null || random.applyAsInt(matches + 1) == 0) { + selected = new BiomeLocation<>(quartX << 2, y, quartZ << 2, holder); } matches++; } @@ -630,19 +504,19 @@ public class CustomBiomeSource extends BiomeSource { return selected; } - private Holder getNaturalSurfaceStructureBiomeHolder(int x, int z) { + private H getNaturalSurfaceStructureBiomeHolder(int x, int z) { int blockX = x << 2; int blockZ = z << 2; try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope(blockX, blockZ, "bukkit_natural_structure_biome")) { RuntimeColumnKey columnKey = new RuntimeColumnKey( engine.getCacheID(), packColumnKey(x, z)); - Holder cachedHolder = naturalSurfaceStructureBiomeCache.get(columnKey); + H cachedHolder = naturalSurfaceStructureBiomeCache.get(columnKey); if (cachedHolder != null) { return cachedHolder; } - Holder resolvedHolder = resolveNaturalSurfaceStructureBiomeHolder(x, z); - Holder existingHolder = naturalSurfaceStructureBiomeCache.putIfAbsent( + H resolvedHolder = resolveNaturalSurfaceStructureBiomeHolder(x, z); + H existingHolder = naturalSurfaceStructureBiomeCache.putIfAbsent( columnKey, resolvedHolder); if (existingHolder != null) { return existingHolder; @@ -664,7 +538,7 @@ public class CustomBiomeSource extends BiomeSource { return internalY > caveSwitchInternalY; } - private Holder resolveSurfaceStructureBiomeHolder(int x, int z) { + private H resolveSurfaceStructureBiomeHolder(int x, int z) { int blockX = x << 2; int blockZ = z << 2; IrisBiome irisBiome = resolveSurfaceStructureBiome(engine, blockX, blockZ); @@ -672,7 +546,7 @@ public class CustomBiomeSource extends BiomeSource { throw new IllegalStateException("Iris returned no surface structure biome at block " + blockX + "," + blockZ); } - Holder holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); + H holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); if (holder == null) { throw new IllegalStateException("Iris structure biome derivative '" + irisBiome.getStructureDerivativeKey() + "' is not registered at block " @@ -681,7 +555,7 @@ public class CustomBiomeSource extends BiomeSource { return holder; } - private Holder resolveNaturalSurfaceStructureBiomeHolder(int x, int z) { + private H resolveNaturalSurfaceStructureBiomeHolder(int x, int z) { int blockX = x << 2; int blockZ = z << 2; IrisBiome irisBiome = engine.getComplex().getNaturalTrueBiomeStream().get(blockX, blockZ); @@ -689,7 +563,7 @@ public class CustomBiomeSource extends BiomeSource { throw new IllegalStateException("Iris returned no natural structure biome at block " + blockX + "," + blockZ); } - Holder holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); + H holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey()); if (holder == null) { throw new IllegalStateException("Iris natural structure biome derivative '" + irisBiome.getStructureDerivativeKey() + "' is not registered at block " @@ -698,7 +572,7 @@ public class CustomBiomeSource extends BiomeSource { return holder; } - public Holder getVisibleNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { + public H getVisibleNoiseBiome(int x, int y, int z) { try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openHistoryCoordinateScope( x << 2, z << 2, "bukkit_visible_biome"); GenerationSessionLease lease = requireGenerationLease( @@ -706,43 +580,40 @@ public class CustomBiomeSource extends BiomeSource { "Iris visible biome lookup was rejected during an engine transition"); IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) { prepareVisibleBiomeBatch(); - return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, sampler); + return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z); } } - void prepareVisibleBiomeBatch() { + public void prepareVisibleBiomeBatch() { if (!isRuntimeAvailable()) { throw new IllegalStateException("Iris visible biome lookup has no active engine runtime"); } runtimeBiomeState(); } - Holder getVisibleNoiseBiomeWithActiveGenerationLease( + public H getVisibleNoiseBiomeWithActiveGenerationLease( int x, int y, - int z, - Climate.Sampler sampler + int z ) { - return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, sampler, null); + return getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, null); } - Holder getVisibleNoiseBiomeWithActiveGenerationLease( + public H getVisibleNoiseBiomeWithActiveGenerationLease( int x, int y, int z, - Climate.Sampler sampler, IrisDimensionCarvingResolver.State resolverState ) { boolean cacheable = isBiomeCacheable(x, z); return getVisibleNoiseBiomeWithActiveGenerationLease( - x, y, z, sampler, resolverState, cacheable); + x, y, z, resolverState, cacheable); } - private Holder getVisibleNoiseBiomeWithActiveGenerationLease( + private H getVisibleNoiseBiomeWithActiveGenerationLease( int x, int y, int z, - Climate.Sampler sampler, IrisDimensionCarvingResolver.State resolverState, boolean cacheable ) { @@ -752,17 +623,17 @@ public class CustomBiomeSource extends BiomeSource { RuntimeNoiseKey cacheKey = new RuntimeNoiseKey( engine.getCacheID(), packNoiseKey(x, y, z)); if (cacheable) { - Holder cachedHolder = noiseBiomeCache.get(cacheKey); + H cachedHolder = noiseBiomeCache.get(cacheKey); if (cachedHolder != null) { return cachedHolder; } } - Holder resolvedHolder = resolveVisibleBiomeHolder(x, y, z, resolverState); + H resolvedHolder = resolveVisibleBiomeHolder(x, y, z, resolverState); if (!cacheable) { return resolvedHolder; } - Holder existingHolder = noiseBiomeCache.putIfAbsent(cacheKey, resolvedHolder); + H existingHolder = noiseBiomeCache.putIfAbsent(cacheKey, resolvedHolder); if (existingHolder != null) { return existingHolder; } @@ -777,12 +648,12 @@ public class CustomBiomeSource extends BiomeSource { private boolean isBiomeCacheable(int quartX, int quartZ) { return isBiomeCacheable( engine, - QuartPos.toBlock(quartX), - QuartPos.toBlock(quartZ) + ((quartX) << 2), + ((quartZ) << 2) ); } - static boolean isBiomeCacheable(Engine engine, int blockX, int blockZ) { + public static boolean isBiomeCacheable(Engine engine, int blockX, int blockZ) { return !engine.answersFromNaturalTerrain(blockX, blockZ); } @@ -838,10 +709,10 @@ public class CustomBiomeSource extends BiomeSource { naturalSurfaceStructureBiomeCache.keySet().removeIf(key -> key.runtimeId() == runtimeId); } - private RuntimeBiomeState runtimeBiomeState() { + private RuntimeBiomeState runtimeBiomeState() { IrisDimension dimension = engine.getDimension(); int runtimeId = engine.getCacheID(); - RuntimeBiomeState existing = runtimeBiomeStates.get(runtimeId); + RuntimeBiomeState existing = runtimeBiomeStates.get(runtimeId); if (existing != null) { if (existing.dimension() != dimension) { throw new IllegalStateException("Iris biome runtime cache ID is shared by different dimensions."); @@ -856,11 +727,11 @@ public class CustomBiomeSource extends BiomeSource { } return existing; } - KMap> refreshedCustomBiomes = fillCustomBiomes( + KMap refreshedCustomBiomes = fillCustomBiomes( biomeCustomRegistry, engine, fallbackBiome); - Map> refreshedSpawnBiomes = fillVanillaSpawnBiomes( + Map refreshedSpawnBiomes = fillVanillaSpawnBiomes( biomeCustomRegistry, biomeRegistry, engine); - RuntimeBiomeState created = new RuntimeBiomeState( + RuntimeBiomeState created = new RuntimeBiomeState<>( dimension, refreshedCustomBiomes, refreshedSpawnBiomes); @@ -869,14 +740,14 @@ public class CustomBiomeSource extends BiomeSource { } } - private Holder resolveStructureBiomeHolder(int x, int y, int z) { + private H resolveStructureBiomeHolder(int x, int y, int z) { BiomeResolution resolution = resolveStructureBiomeResolution(x, y, z); if (resolution == null) { throw new IllegalStateException("Iris returned no structure biome at quart " + x + "," + y + "," + z); } - Holder holder = resolveBiomeHolder( + H holder = resolveBiomeHolder( biomeRegistry, resolution.irisBiome.getStructureDerivativeKey()); if (holder == null) { throw new IllegalStateException("Iris structure biome derivative '" @@ -886,7 +757,7 @@ public class CustomBiomeSource extends BiomeSource { return holder; } - private Holder resolveVisibleBiomeHolder( + private H resolveVisibleBiomeHolder( int x, int y, int z, @@ -895,7 +766,7 @@ public class CustomBiomeSource extends BiomeSource { return resolveVisibleBiomeHolder(x, y, z, resolverState, null); } - private Holder resolveVisibleBiomeHolder( + private H resolveVisibleBiomeHolder( int x, int y, int z, @@ -919,15 +790,15 @@ public class CustomBiomeSource extends BiomeSource { return resolveVisibleBiomeHolder(resolution); } - private Holder resolveVisibleBiomeHolder(BiomeResolution resolution) { + private H resolveVisibleBiomeHolder(BiomeResolution resolution) { if (resolution.irisBiome.isCustom()) { return resolveCustomHolder(resolution); } - org.bukkit.block.Biome vanillaBiome = resolution.underground + Biome vanillaBiome = resolution.underground ? resolution.irisBiome.getGroundBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ) : resolution.irisBiome.getSkyBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ); - Holder holder = NMSBinding.biomeToBiomeBase(biomeRegistry, vanillaBiome); + H holder = biomeRegistry.lookup(vanillaBiome); if (holder != null) { return holder; } @@ -935,10 +806,10 @@ public class CustomBiomeSource extends BiomeSource { return getFallbackBiome(); } - private Holder resolveCustomHolder(BiomeResolution resolution) { + private H resolveCustomHolder(BiomeResolution resolution) { IrisBiomeCustom customBiome = resolution.irisBiome.getCustomBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ); if (customBiome != null) { - Holder holder = runtimeBiomeState().customBiomes().get(customBiomeKey( + H holder = runtimeBiomeState().customBiomes().get(customBiomeKey( resolution.dimension, customBiome.getId())); if (holder != null) { return holder; @@ -952,8 +823,8 @@ public class CustomBiomeSource extends BiomeSource { return resolveBiomeResolution(x, y, z, null, false); } - private Holder resolvePhysicalBiomeHolder(String physicalKey) { - Holder holder = resolveBiomeHolder(biomeCustomRegistry, physicalKey); + private H resolvePhysicalBiomeHolder(String physicalKey) { + H holder = resolveBiomeHolder(biomeCustomRegistry, physicalKey); if (holder == null) { holder = resolveBiomeHolder(biomeRegistry, physicalKey); } @@ -1075,12 +946,12 @@ public class CustomBiomeSource extends BiomeSource { irisBiome, underground, dimension, blockX, blockY, blockZ, noiseRng); } - private Holder getFallbackBiome() { + private H getFallbackBiome() { if (fallbackBiome != null) { return fallbackBiome; } - Holder holder = resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); + H holder = resolveFallbackBiome(biomeRegistry, biomeCustomRegistry); if (holder != null) { return holder; } @@ -1098,8 +969,8 @@ public class CustomBiomeSource extends BiomeSource { return ((long) x << 32) ^ ((long) z & 4294967295L); } - private static Holder resolveCustomBiomeHolder( - Registry customRegistry, + private static H resolveCustomBiomeHolder( + NativeBiomeRegistry customRegistry, Engine engine, IrisDimension dimension, String customBiomeId @@ -1109,30 +980,10 @@ public class CustomBiomeSource extends BiomeSource { return null; } - String physicalKey = engine.getData().customBiomeResourceKey(dimension, customBiomeId); - Identifier resourceLocation = Identifier.tryParse(physicalKey); - if (resourceLocation == null) { - throw new IllegalStateException("Invalid Iris custom biome resource key '" + physicalKey + "'."); - } - Biome biome = customRegistry.getValue(resourceLocation); - if (biome == null) { - return null; - } - - Optional> optionalBiomeKey = customRegistry.getResourceKey(biome); - if (optionalBiomeKey.isEmpty()) { - return null; - } - - Optional> optionalReferenceHolder = customRegistry.get(optionalBiomeKey.get()); - if (optionalReferenceHolder.isEmpty()) { - return null; - } - - return optionalReferenceHolder.get(); + return customRegistry.lookupCustom(engine.getData().customBiomeResourceKey(dimension, customBiomeId)); } - static String customBiomeKey(IrisDimension dimension, String customBiomeId) { + public static String customBiomeKey(IrisDimension dimension, String customBiomeId) { return dimension.getCustomBiomeKey(customBiomeId); } @@ -1189,24 +1040,17 @@ public class CustomBiomeSource extends BiomeSource { return engine.getComplex().getTrueBiomeStream().get(blockX, blockZ); } - private static Holder resolveBiomeHolder(Registry registry, String biomeKey) { - if (registry == null || biomeKey == null || biomeKey.isBlank()) { - return null; - } - Identifier identifier = Identifier.tryParse(biomeKey); - if (identifier == null) { - return null; - } - return registry.get(ResourceKey.create(Registries.BIOME, identifier)).orElse(null); + private static H resolveBiomeHolder(NativeBiomeRegistry registry, String biomeKey) { + return registry == null ? null : registry.lookup(biomeKey); } - private static Holder resolveFallbackBiome(Registry registry, Registry customRegistry) { - Holder plains = NMSBinding.biomeToBiomeBase(registry, org.bukkit.block.Biome.PLAINS); + private static H resolveFallbackBiome(NativeBiomeRegistry registry, NativeBiomeRegistry customRegistry) { + H plains = registry.lookup(Biome.PLAINS); if (plains != null) { return plains; } - Holder vanilla = firstHolder(registry); + H vanilla = firstHolder(registry); if (vanilla != null) { return vanilla; } @@ -1214,24 +1058,8 @@ public class CustomBiomeSource extends BiomeSource { return firstHolder(customRegistry); } - private static Holder firstHolder(Registry registry) { - if (registry == null) { - return null; - } - - for (Biome biome : registry) { - Optional> optionalBiomeKey = registry.getResourceKey(biome); - if (optionalBiomeKey.isEmpty()) { - continue; - } - - Optional> optionalHolder = registry.get(optionalBiomeKey.get()); - if (optionalHolder.isPresent()) { - return optionalHolder.get(); - } - } - - return null; + private static H firstHolder(NativeBiomeRegistry registry) { + return registry == null ? null : registry.first(); } private static final class BiomeResolution { @@ -1271,10 +1099,18 @@ public class CustomBiomeSource extends BiomeSource { private record RuntimeColumnKey(int runtimeId, long coordinateKey) { } - private record RuntimeBiomeState( + private record RuntimeBiomeState( IrisDimension dimension, - KMap> customBiomes, - Map> vanillaSpawnBiomes + KMap customBiomes, + Map vanillaSpawnBiomes ) { } + public record RuntimeOptions(long seed, Engine engine, World world) { + } + @Override + public VisibleResolver visibleResolver() { + IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State(); + return (x, y, z) -> getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, state); + } + } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationPolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationPolicy.java new file mode 100644 index 000000000..551216864 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationPolicy.java @@ -0,0 +1,104 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.context.IrisContext; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.GenerationSessionException; +import art.arcane.iris.generation.runtime.GenerationSessionLease; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.platform.generation.BukkitChunkGenerator; +import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; +import art.arcane.iris.world.history.NativeTerrainReceipt; +import art.arcane.iris.world.history.SavedTerrainChunk; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationLease; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainPipelinePolicy; +import org.bukkit.NamespacedKey; + +import java.io.IOException; + +public class BukkitGenerationPolicy + implements NativeTerrainPipelinePolicy { + private static final NamespacedKey NATURAL_TERRAIN = new NamespacedKey("iris", "natural_terrain"); + + private final Engine engine; + private final BukkitChunkGenerator generator; + + public BukkitGenerationPolicy(Engine engine, BukkitChunkGenerator generator) { + this.engine = engine; + this.generator = generator; + } + + @Override + public BukkitChunkGenerator.GenerationStagePermit acquireStage(String operation) { + return generator == null + ? BukkitChunkGenerator.GenerationStagePermit.noop() + : generator.acquireGenerationStage(operation); + } + + @Override + public GenerationSessionLease acquireLease(String operation) { + try { + return engine.acquireGenerationLease(operation); + } catch (GenerationSessionException exception) { + throw new IllegalStateException("Iris " + operation + " could not acquire its engine runtime.", exception); + } + } + + @Override + public GenerationHistoryRuntimeRouter.RuntimeRoute openRoute(int chunkX, int chunkZ, String operation) { + if (!(engine instanceof IrisEngine irisEngine)) { + return null; + } + GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); + if (router == null) { + if (generator != null && generator.getGenerationHistory() != null) { + throw new IllegalStateException("Iris " + operation + " has no generation-history runtime router."); + } + return null; + } + try { + return router.openRoute(chunkX, chunkZ); + } catch (IOException failure) { + throw new IllegalStateException("Iris " + operation + + " could not route generation history for chunk " + chunkX + "," + chunkZ + ".", failure); + } + } + + @Override + public IrisContext.Scope openContext(NativeGenerationLease lease) { + return IrisContext.open(engine, lease.sessionId(), null); + } + + @Override + public boolean allowsChunkWrite(int chunkX, int chunkZ) { + return engine.getComplex().allowsMantleChunkWrite(chunkX, chunkZ); + } + + @Override + public boolean usesFlatTerrain() { + return generator != null && generator.usesFlatStudioTerrain(); + } + + @Override + public int minimumY() { + return engine.getMinHeight(); + } + + @Override + public int terrainHeight(int blockX, int blockZ, boolean floor) { + return engine.getHeight(blockX, blockZ, floor); + } + + @Override + public NamespacedKey naturalTerrainKey() { + return NATURAL_TERRAIN; + } + + @Override + public byte[] naturalTerrainReceipt(GenerationHistoryRuntimeRouter.RuntimeRoute route) throws IOException { + if (route.naturalTerrain().isEmpty()) { + return null; + } + SavedTerrainChunk terrain = route.naturalTerrain().orElseThrow(); + return NativeTerrainReceipt.encode(terrain, route.activation().activationId(), route.epoch().epochId()); + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationRegistry.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationRegistry.java new file mode 100644 index 000000000..52e0012cc --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGenerationRegistry.java @@ -0,0 +1,54 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.spi.PlatformGenerationRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRegistry; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +public record BukkitGenerationRegistry( + NativeGenerationRegistry registry, + String runtimeIdentity, + String generatedDefinitionRendererIdentity +) implements PlatformGenerationRegistry { + public BukkitGenerationRegistry { + registry = Objects.requireNonNull(registry, "registry"); + runtimeIdentity = requireText(runtimeIdentity, "runtimeIdentity"); + generatedDefinitionRendererIdentity = requireText(generatedDefinitionRendererIdentity, "rendererIdentity"); + } + + @Override + public List legacyCustomBiomeResourceKeys(String packName, String dimensionKey, String customBiomeId) { + return List.of(requireText(dimensionKey, "dimensionKey").toLowerCase(Locale.ROOT) + + ":" + requireText(customBiomeId, "customBiomeId").toLowerCase(Locale.ROOT)); + } + + @Override + public String dimensionTypeResourceKey(String packName, String dimensionKey, String dimensionTypeKey) { + return "iris:" + requireText(dimensionTypeKey, "dimensionTypeKey").toLowerCase(Locale.ROOT); + } + + @Override + public Definition definition(String registryKey, String resourceKey) { + return registry.definition(registryKey, resourceKey); + } + + @Override + public Definition generatedDefinition(String registryKey, String resourceKey) { + return registry.generatedDefinition(registryKey, resourceKey); + } + + @Override + public Definition canonicalDefinition(String registryKey, String resourceKey, String sourceJson) { + return registry.canonicalDefinition(registryKey, resourceKey, sourceJson); + } + + private static String requireText(String value, String label) { + String required = Objects.requireNonNull(value, label).trim(); + if (required.isEmpty()) { + throw new IllegalArgumentException(label + " must not be blank."); + } + return required; + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGeneratorContext.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGeneratorContext.java new file mode 100644 index 000000000..57b14a426 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitGeneratorContext.java @@ -0,0 +1,138 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.generation.runtime.EngineLifecycleTasks; +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.iris.platform.generation.BukkitChunkGenerator; +import art.arcane.iris.structure.nativegen.IrisStructurePolicy; +import art.arcane.iris.structure.nativegen.ImportedFeaturePolicy; +import art.arcane.iris.structure.nativegen.IrisStructureVolumePolicy; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; +import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; +import art.arcane.iris.world.history.NativeBiomeSpawnSelection; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.NativeBukkitGeneratorContext; +import art.arcane.volmlib.nativelib.terrain.NativeBiomeRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy; +import art.arcane.volmlib.nativelib.terrain.NativeSpawnSelection; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainColumnPolicy; +import art.arcane.volmlib.nativelib.terrain.NativeStructureBootstrapPolicy; +import art.arcane.volmlib.nativelib.terrain.feature.NativeImportedFeaturePolicy; +import art.arcane.volmlib.nativelib.terrain.structure.StructureVolumePolicy; +import org.bukkit.NamespacedKey; +import org.bukkit.World; +import java.io.IOException; +import java.util.function.IntConsumer; +import java.util.function.Supplier; + +public final class BukkitGeneratorContext extends BukkitGenerationPolicy implements NativeBukkitGeneratorContext< + Engine, IrisDimension, NativeStructureStartPlan, NativeStructureOwnershipRecord, GenerationHistoryRuntimeRouter.RuntimeRoute> { + private static final NamespacedKey STRUCTURE_ACTIVATION = new NamespacedKey("iris", "structure_activation"); + private final Options options; + private final BukkitChunkGenerator generator; + private final BukkitStructureStagePolicy structures; + private final IrisStructurePolicy injection; + + public BukkitGeneratorContext(Options options) { + super(options.engine(), options.world().getGenerator() instanceof BukkitChunkGenerator generator ? generator : null); + this.options = options; + this.generator = options.world().getGenerator() instanceof BukkitChunkGenerator generator ? generator : null; + this.structures = new BukkitStructureStagePolicy(options.engine()); + this.injection = new IrisStructurePolicy(options.engine()); + } + + @Override + public Engine current() { return options.engine(); } + + @Override + public NativeBiomeSourcePolicy biomes(NativeBiomeRegistry registry) { + return new BukkitBiomePolicy<>(new BukkitBiomePolicy.RuntimeOptions(options.seed(), current(), options.world()), registry); + } + + @Override + public NativeImportedFeaturePolicy importedFeatures() { return new ImportedFeaturePolicy(current()); } + + @Override + public NativeStructureBootstrapPolicy bootstrap() { return new BukkitStructureBootstrapPolicy(current(), options.world().getName()); } + + @Override + public NativeTerrainColumnPolicy columns() { return new BukkitTerrainColumnPolicy(current(), generator); } + + @Override + public BukkitStructureStagePolicy structures() { return structures; } + + @Override + public BukkitStructureStagePolicy structureCache() { return structures; } + + @Override + public IrisStructurePolicy injection() { return injection; } + + @Override + public StructureVolumePolicy volumes() { return IrisStructureVolumePolicy.INSTANCE; } + + @Override + public void installVolumes(VolumeResolver resolver) { + NativeStructureVolumeIndex.install(current(), resolver::volumesAt); + } + + @Override + public void onRetirement(IntConsumer listener) { + if (current() instanceof IrisEngine irisEngine) { + irisEngine.addGenerationRuntimeRetirementListener(listener); + } + } + + @Override + public int fluidHeight() { return current().getDimension().getFluidHeight(); } + + @Override + public int runtimeId() { return current().getCacheID(); } + + @Override + public boolean generateStructures() { return generator == null || generator.shouldGenerateStructures(); } + + @Override + public boolean allowsNewChunk(int chunkX, int chunkZ) { return current().getComplex().allowsNewGenerationChunk(chunkX, chunkZ); } + + @Override + public boolean active() { return !current().isClosing() && !current().isClosed(); } + + @Override + public boolean stacked() { return current().getDimensionStackContext() != null; } + + @Override + public NativeGenerationScope openCoordinateScope(int blockX, int blockZ, String operation) { + if (!(current() instanceof IrisEngine irisEngine)) { return null; } + try { + return irisEngine.openGenerationHistoryCoordinateScope(blockX, blockZ); + } catch (IOException failure) { + throw new IllegalStateException("Iris " + operation + " could not route generation history at " + + blockX + "," + blockZ + ".", failure); + } + } + + @Override + public NativeSpawnSelection spawnSelection(int blockX, int worldY, int blockZ, String physicalBiomeKey) { + return NativeBiomeSpawnSelection.at(current(), blockX, worldY, blockZ, physicalBiomeKey); + } + + @Override + public T admittedSpawns(Supplier operation, T fallback) { + return EngineLifecycleTasks.call(current(), "bukkit_nms_mob_spawns", operation, fallback); + } + + @Override + public NamespacedKey structureActivationKey() { return STRUCTURE_ACTIVATION; } + + @Override + public long activationId(GenerationHistoryRuntimeRouter.RuntimeRoute route) { return route.activation().activationId(); } + + @Override + public void failed(String operation, Throwable failure) { IrisLogging.reportError(operation, failure); } + + public record Options(long seed, Engine engine, World world) { } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureBootstrapPolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureBootstrapPolicy.java new file mode 100644 index 000000000..133489af2 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureBootstrapPolicy.java @@ -0,0 +1,23 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.NativeStructureBootstrapPolicy; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; + +public record BukkitStructureBootstrapPolicy(Engine engine, String worldName) implements NativeStructureBootstrapPolicy { + @Override + public CompletableFuture start(Runnable claim, Supplier> preparation, Runnable activation) { + if (!(engine instanceof IrisEngine irisEngine)) { + throw new IllegalStateException("Native structure bootstrap requires an IrisEngine runtime."); + } + return irisEngine.startNativeStructureBootstrap(claim, preparation, activation); + } + + @Override + public void failed(Throwable failure) { + IrisLogging.reportError("Native structure ring bootstrap failed for world '" + worldName + "'.", failure); + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureStagePolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureStagePolicy.java new file mode 100644 index 000000000..f31008015 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitStructureStagePolicy.java @@ -0,0 +1,133 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.volmlib.nativelib.terrain.structure.StructureCachePolicy; +import art.arcane.volmlib.nativelib.terrain.structure.StructureStagePolicy; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.terrain.IrisMaterialPalette; +import art.arcane.iris.structure.nativegen.GenerationWritePolicy; +import art.arcane.iris.structure.nativegen.IrisStructurePolicy; +import art.arcane.iris.structure.nativegen.IrisStructureVolumePolicy; +import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; +import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; +import art.arcane.iris.structure.nativegen.NativeStructureVolumeIndex; +import art.arcane.iris.structure.nativegen.StructureLocateSearch; +import art.arcane.iris.structure.placement.IrisStructureLocator; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateSearchAccess; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePalette; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePlacementDecision; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferencePolicy; +import art.arcane.volmlib.nativelib.terrain.structure.StructureVolumePolicy; +import art.arcane.volmlib.util.math.RNG; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntBinaryOperator; + +public final class BukkitStructureStagePolicy implements StructureStagePolicy< + Engine, NativeStructureStartPlan, NativeStructureOwnershipRecord>, StructureCachePolicy { + private final Engine engine; + + public BukkitStructureStagePolicy(Engine engine) { + this.engine = engine; + } + + @Override + public IrisDimension dimension() { + return engine.getDimension(); + } + + @Override + public int runtimeId() { + return engine.getCacheID(); + } + + @Override + public Engine current() { + return engine; + } + + @Override + public StructureReferencePolicy structurePolicy(Engine context) { + return new IrisStructurePolicy(context); + } + + @Override + public boolean hasPlacement(Engine context, String key) { + return IrisStructureLocator.hasNativePlacement(context, key); + } + + @Override + public StructureLocateSearchAccess locateSearch( + Engine context, StructureStagePolicy.LocateRequest request) { + return new StructureLocateSearch<>(new StructureLocateSearch.Options<>( + context, request.key(), request.x(), request.z(), request.radius(), request.probe(), request.ownership())); + } + + @Override + public int locatorY(NativeStructureOwnershipRecord ownership) { + return ownership.locatorY(); + } + + @Override + public StructurePlacementDecision restoredDecision(NativeStructureOwnershipRecord ownership) { + return ownership.restoredDecision(); + } + + @Override + public boolean allowsFootprint(Engine context, StructureStagePolicy.Footprint footprint) { + return context.getComplex().allowsNewGenerationFootprint( + footprint.minX(), footprint.minZ(), footprint.maxX(), footprint.maxZ()); + } + + @Override + public boolean allowsChunkWrite(Engine context, int chunkX, int chunkZ) { + return context.getComplex().allowsMantleChunkWrite(chunkX, chunkZ); + } + + @Override + public boolean historicalStructure(Engine context, long activation) { + return GenerationWritePolicy.isHistoricalStructure(context, activation); + } + + @Override + public boolean stacked(Engine context) { + return context.getDimensionStackContext() != null; + } + + @Override + public NativeBlockPositionPredicate protectedPositions(Engine context) { + return GenerationWritePolicy.protectedPositions(context.getDimension().getStaticObjectLayer(context.getData()), + context.getDimensionStackContext(), context.getMinHeight()); + } + + @Override + public IntBinaryOperator surfaceHeight(Engine context) { + return (x, z) -> Engine.hostHeight(context, x, z, true) + context.getMinHeight(); + } + + @Override + public IntBinaryOperator worldgenHeight(Engine context, int runtimeMinY, boolean floor) { + return (x, z) -> Engine.hostHeight(context, x, z, floor) + runtimeMinY + 1; + } + + @Override + public NativeBlockState paletteBlock(StructurePalette source, RNG rng, int x, int y, int z) { + return ((IrisMaterialPalette) source).get(rng, x, y, z, engine.getData()); + } + + @Override + public RuntimeException structuresDisabled(int chunkX, int chunkZ) { + return new IllegalStateException("Iris cannot generate native structures in chunk " + + chunkX + "," + chunkZ + + " because structure generation is disabled outside the pack; enable native structure generation " + + "and deny families through importedStructures.disabled or complete keys through importedStructures.disabledExact"); + } + + @Override + public String generationLabel(String structureId) { + return "Iris native structure " + structureId; + } + +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTerrainColumnPolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTerrainColumnPolicy.java new file mode 100644 index 000000000..15b974c71 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTerrainColumnPolicy.java @@ -0,0 +1,119 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.generation.context.IrisContext; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.GenerationSessionException; +import art.arcane.iris.generation.runtime.GenerationSessionLease; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.platform.generation.BukkitChunkGenerator; +import art.arcane.iris.studio.generation.JigsawStudioGenerator; +import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; +import art.arcane.iris.world.history.TerrainBoundarySignature; +import art.arcane.iris.world.history.TerrainNativeBlockKeys; +import art.arcane.volmlib.nativelib.terrain.NativeBlockColumn; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainColumnPolicy; + +import java.io.IOException; +import java.util.Optional; + +public final class BukkitTerrainColumnPolicy implements NativeTerrainColumnPolicy { + private final Engine engine; + private final BukkitChunkGenerator generator; + + public BukkitTerrainColumnPolicy(Engine engine, BukkitChunkGenerator generator) { + this.engine = engine; + this.generator = generator; + } + + @Override + public boolean usesFlatTerrain() { + return generator != null && generator.usesFlatStudioTerrain(); + } + + @Override + public int flatBaseHeight() { + return generator.getAuthoringBaseHeight(); + } + + @Override + public FlatColumn flatColumn(int blockX, int blockZ) { + String block = generator.isJigsawStudioActive() && JigsawStudioGenerator.isLightFloor(blockX, blockZ) + ? "minecraft:smooth_stone" : "minecraft:polished_deepslate"; + return new FlatColumn(generator.getAuthoringFloorY(), block); + } + + @Override + public ColumnSession openQuery(Query query) { + return new Session(query); + } + + @Override + public String placementKey(String stateKey) { + return TerrainNativeBlockKeys.placementKey(stateKey); + } + + private GenerationHistoryRuntimeRouter.CoordinateScope openHistory(Query query) { + if (!(engine instanceof IrisEngine irisEngine)) { + return null; + } + try { + return irisEngine.openGenerationHistoryCoordinateScope(query.blockX(), query.blockZ()); + } catch (IOException failure) { + throw new IllegalStateException("Iris " + query.operation() + " could not route generation history at " + + query.blockX() + "," + query.blockZ() + ".", failure); + } + } + + private final class Session implements ColumnSession { + private final Query query; + private final GenerationHistoryRuntimeRouter.CoordinateScope history; + private final GenerationSessionLease lease; + private final IrisContext.Scope context; + + private Session(Query query) { + this.query = query; + history = openHistory(query); + GenerationSessionLease acquired = null; + try { + acquired = engine.acquireGenerationLease(query.operation()); + context = IrisContext.open(engine, acquired.sessionId(), null); + lease = acquired; + } catch (GenerationSessionException | RuntimeException | Error failure) { + try (history; GenerationSessionLease cleanup = acquired) { + if (failure instanceof GenerationSessionException) { + throw new IllegalStateException("Iris " + query.operation() + + " could not acquire its engine runtime.", failure); + } + if (failure instanceof RuntimeException runtime) { + throw runtime; + } + throw (Error) failure; + } + } + } + + @Override + public int runtimeId() { + return engine.getCacheID(); + } + + @Override + public Optional resolvedColumn() { + return engine.getComplex().resolvedTerrainColumn(query.blockX(), query.blockZ()) + .map(TerrainBoundarySignature::geometry); + } + + @Override + public int height(boolean ignoreFluid) { + return engine.getDimensionStackContext() == null + ? engine.getHeight(query.blockX(), query.blockZ(), ignoreFluid) + : Engine.hostHeight(engine, query.blockX(), query.blockZ(), ignoreFluid); + } + + @Override + public void close() { + try (history; lease; context) { + } + } + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTileWriteScheduler.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTileWriteScheduler.java new file mode 100644 index 000000000..a3545c180 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitTileWriteScheduler.java @@ -0,0 +1,19 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.world.task.J; +import art.arcane.volmlib.nativelib.terrain.TileWriteScheduler; +import org.bukkit.Location; + +public enum BukkitTileWriteScheduler implements TileWriteScheduler { + INSTANCE; + + @Override + public boolean owns(Location location) { + return J.isOwnedByCurrentRegion(location.getWorld(), location.getBlockX() >> 4, location.getBlockZ() >> 4); + } + + @Override + public boolean schedule(Location location, Runnable update) { + return J.runAt(location, update); + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitWorldLifecyclePolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitWorldLifecyclePolicy.java new file mode 100644 index 000000000..eb68e28f5 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/BukkitWorldLifecyclePolicy.java @@ -0,0 +1,71 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.platform.agent.Agent; +import art.arcane.iris.platform.generation.PlatformChunkGenerator; +import art.arcane.iris.generation.runtime.EngineTarget; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.world.lifecycle.WorldLifecycleStaging; +import art.arcane.volmlib.nativelib.terrain.NativeWorldLifecyclePolicy; +import org.bukkit.generator.ChunkGenerator; + +import java.lang.instrument.Instrumentation; +import java.util.Objects; + +public enum BukkitWorldLifecyclePolicy implements NativeWorldLifecyclePolicy { + INSTANCE; + + @Override + public String pluginName() { + return "Iris"; + } + + @Override + public String classLoaderLifecycleBridgeName() { + return "art.arcane.iris.platform.agent.Installer"; + } + + @Override + public Instrumentation instrumentation() { + return Agent.getInstrumentation(); + } + + @Override + public void requireClassLoaderCloseDeferral() throws ReflectiveOperationException { + Agent.requireClassLoaderCloseDeferral(); + } + + @Override + public void retainClassLoader(ClassLoader loader) { + Agent.retainClassLoader(loader); + } + + @Override + public void releaseClassLoader(ClassLoader loader) { + Agent.releaseClassLoader(loader); + } + + @Override + public void reportFailure(String message, Throwable failure) { + IrisLogging.reportError(message, failure); + } + + @Override + public WorldDefinition stagedWorld(String levelId, ChunkGenerator constructorGenerator, boolean consume) { + ChunkGenerator resolved = constructorGenerator instanceof PlatformChunkGenerator ? constructorGenerator : null; + if (resolved == null) { + resolved = consume ? WorldLifecycleStaging.consumeStemGenerator(levelId) + : WorldLifecycleStaging.peekStemGenerator(levelId); + } + if (!(resolved instanceof PlatformChunkGenerator generator)) { + return null; + } + EngineTarget target = Objects.requireNonNull(generator.getTarget(), "Iris generator engine target"); + String identity = Objects.requireNonNull(target.getWorld(), "Iris generator world").identity(); + int separator = identity.indexOf(':'); + String namespace = separator < 0 ? "minecraft" : identity.substring(0, separator); + if (!namespace.equals("iris") && !namespace.equals("minecraft")) { + throw new IllegalStateException("Iris generator has an unmanaged world identity: " + identity); + } + return new WorldDefinition(identity, "iris:" + target.getDimension().getDimensionTypeKey()); + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMS.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMS.java index 63a5d5cd5..33ac5b3b2 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMS.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMS.java @@ -59,14 +59,6 @@ public class INMS { return binding; } - public static String getNMSTag() { - if (IrisSettings.get().getGeneral().isDisableNMS()) { - return "BUKKIT"; - } - - return NmsBindingSelector.select(requireMinecraftVersion()); - } - private static INMSBinding bind() { boolean disableNms = IrisSettings.get().getGeneral().isDisableNMS(); if (disableNms) { @@ -74,43 +66,7 @@ public class INMS { IrisLogging.warn("NMS support is disabled. Iris world creation is unavailable until general.disableNMS=false."); return new NMSBinding1X(); } - return bindExact(getNMSTag()); + return new BukkitBinding(); } - private static MinecraftVersion requireMinecraftVersion() { - try { - MinecraftVersion detected = MinecraftVersion.detect(Bukkit.getServer()); - if (detected == null) { - throw new IllegalStateException("Iris could not determine the exact Minecraft server version"); - } - return detected; - } catch (Throwable e) { - IrisLogging.reportError(e); - IrisLogging.error("Failed to determine server minecraft version!"); - if (e instanceof IllegalStateException illegalStateException) { - throw illegalStateException; - } - throw new IllegalStateException("Iris could not determine the exact Minecraft server version", e); - } - } - - private static INMSBinding bindExact(String code) { - IrisLogging.debug("Locating exact NMS Binding for " + code); - try { - Class clazz = Class.forName("art.arcane.iris.platform.bukkit.nms." + code + ".NMSBinding"); - Object candidate = clazz.getConstructor().newInstance(); - if (candidate instanceof INMSBinding binding) { - IrisLogging.debug("Craftbukkit " + code + " <-> " + candidate.getClass().getSimpleName() + " Successfully Bound"); - return binding; - } - throw new IllegalStateException("Exact NMS binding class for " + code - + " does not implement " + INMSBinding.class.getName()); - } catch (Throwable e) { - IrisLogging.reportError(e); - if (e instanceof IllegalStateException illegalStateException) { - throw illegalStateException; - } - throw new IllegalStateException("Failed to bind exact NMS revision " + code, e); - } - } } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMSBinding.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMSBinding.java index 8d04a4277..5a52b4fce 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMSBinding.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/INMSBinding.java @@ -18,23 +18,34 @@ package art.arcane.iris.platform.bukkit.nms; +import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockVolume; + +import art.arcane.volmlib.nativelib.terrain.BukkitTerrainBuffer; + +import org.bukkit.block.data.BlockData; + +import art.arcane.volmlib.util.nbt.mca.NBTWorldSupport; + import art.arcane.iris.world.history.SavedTerrainChunk; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainAccess; import java.util.concurrent.CompletableFuture; import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; import art.arcane.iris.world.lifecycle.WorldLifecycleCaller; import art.arcane.iris.world.lifecycle.WorldLifecycleRequest; import art.arcane.iris.world.lifecycle.WorldLifecycleService; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.volmlib.nativelib.terrain.BiomeColor; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.generation.chunk.TerrainChunk; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.platform.generation.PlatformChunkGenerator; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformGenerationRegistry; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -60,7 +71,7 @@ import java.awt.Color; import java.util.List; import java.util.Set; -public interface INMSBinding { +public interface INMSBinding extends NativeTerrainAccess { default CompletableFuture flushSavedTerrainCapture(World world) { return CompletableFuture.failedFuture(new UnsupportedOperationException( "Native saved terrain checkpoints are unavailable.")); @@ -84,7 +95,9 @@ public interface INMSBinding { KMap serializeTile(Location location); - void deserializeTile(KMap s, Location newPosition); + default void deserializeTile(KMap data, Location location) { + deserializeTile(data, location, BukkitTileWriteScheduler.INSTANCE); + } CompoundTag serializeEntity(Entity location); @@ -92,32 +105,14 @@ public interface INMSBinding { boolean supportsCustomHeight(); - Object getBiomeBaseFromId(int id); - int getMinHeight(World world); boolean supportsCustomBiomes(); boolean supportsIrisWorldGeneration(); - int getTrueBiomeBaseId(Object biomeBase); - - Object getTrueBiomeBase(Location location); - String getTrueBiomeBaseKey(Location location); - Object getCustomBiomeBaseFor(String mckey); - - Object getCustomBiomeBaseHolderFor(String mckey); - - int getBiomeBaseIdForKey(String key); - - String getKeyForBiomeBase(Object biomeBase); - - Object getBiomeBase(World world, Biome biome); - - Object getBiomeBase(Object registry, Biome biome); - KList getBiomes(); KList getStructureKeys(); @@ -207,10 +202,6 @@ public interface INMSBinding { } } - default Object createRuntimeLevelStem(Object registryAccess, ChunkGenerator raw) { - throw new UnsupportedOperationException("Active NMS binding does not support runtime LevelStem creation."); - } - default void ensureServerLevelInjection() { } @@ -220,13 +211,13 @@ public interface INMSBinding { return false; } - MCAPaletteAccess createPalette(); + MCAPaletteAccess createPalette(NBTWorldSupport.BlockStateCodec codec); - default boolean applyChunkBlocks(Chunk chunk, TerrainChunk data) { + default boolean applyChunkBlocks(Chunk chunk, BukkitTerrainBuffer data) { return false; } - default boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, Hunk data) { + default boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, NativeBlockVolume data) { return false; } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelector.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelector.java deleted file mode 100644 index e18e7eafa..000000000 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelector.java +++ /dev/null @@ -1,23 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms; - -final class NmsBindingSelector { - private static final String SUPPORTED_VERSIONS = "26.1.2, 26.2 or 26.3"; - private static final String SUPPORTED_TAG = "v26_2_R1"; - - private NmsBindingSelector() { - } - - static String select(MinecraftVersion version) { - if (version == null) { - throw new IllegalStateException("Iris requires an exact Minecraft version before selecting NMS"); - } - if (version.isSameRelease(26, 3, 0)) { - return "v26_3_R1"; - } - if (!version.isSameRelease(26, 2, 0) && !version.isSameRelease(26, 1, 2)) { - throw new IllegalStateException("Iris requires Minecraft " + SUPPORTED_VERSIONS - + ". Detected server version: " + version.value()); - } - return SUPPORTED_TAG; - } -} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/SavedTerrainSnapshotPolicy.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/SavedTerrainSnapshotPolicy.java new file mode 100644 index 000000000..e95d87f71 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/SavedTerrainSnapshotPolicy.java @@ -0,0 +1,20 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.iris.world.history.SavedTerrainCapturePolicy; +import art.arcane.iris.world.history.SavedTerrainChunk; +import art.arcane.iris.world.task.J; +import art.arcane.volmlib.nativelib.terrain.SnapshotPolicy; + +import java.util.concurrent.CompletableFuture; + +public final class SavedTerrainSnapshotPolicy extends SavedTerrainCapturePolicy implements SnapshotPolicy { + public static final SavedTerrainSnapshotPolicy INSTANCE = new SavedTerrainSnapshotPolicy(); + + private SavedTerrainSnapshotPolicy() { + } + + @Override + public CompletableFuture runRegion(Region region, Runnable action) { + return J.runRegionFuture(region.world(), region.chunkX(), region.chunkZ(), action); + } +} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundary.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundary.java deleted file mode 100644 index 93633d730..000000000 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundary.java +++ /dev/null @@ -1,58 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms; - -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.function.BooleanSupplier; - -public final class ServerShutdownBoundary { - private static final long MAX_JOIN_SLICE_MILLIS = 1000L; - - private final BooleanSupplier boundaryReached; - private final Thread serverThread; - - public ServerShutdownBoundary(BooleanSupplier boundaryReached, Thread serverThread) { - this.boundaryReached = Objects.requireNonNull(boundaryReached, "Server shutdown boundary"); - this.serverThread = Objects.requireNonNull(serverThread, "Server thread"); - this.boundaryReached.getAsBoolean(); - } - - public boolean await(long timeout, TimeUnit unit) { - TimeUnit activeUnit = Objects.requireNonNull(unit, "Server shutdown timeout unit"); - if (boundaryReached.getAsBoolean()) { - return true; - } - if (serverThread == Thread.currentThread()) { - return false; - } - - long timeoutNanos = Math.max(0L, activeUnit.toNanos(timeout)); - long started = System.nanoTime(); - boolean interrupted = false; - while (!boundaryReached.getAsBoolean()) { - long remaining = timeoutNanos - (System.nanoTime() - started); - if (remaining <= 0L || !serverThread.isAlive()) { - restoreInterrupt(interrupted); - return boundaryReached.getAsBoolean(); - } - - long joinMillis = Math.max( - 1L, - Math.min(MAX_JOIN_SLICE_MILLIS, TimeUnit.NANOSECONDS.toMillis(remaining)) - ); - try { - serverThread.join(joinMillis); - } catch (InterruptedException e) { - interrupted = true; - } - } - - restoreInterrupt(interrupted); - return true; - } - - private static void restoreInterrupt(boolean interrupted) { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } -} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BiomeColor.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BiomeColor.java deleted file mode 100644 index dae921f14..000000000 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BiomeColor.java +++ /dev/null @@ -1,10 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.container; - -public enum BiomeColor { - FOG, - WATER, - WATER_FOG, - SKY, - FOLIAGE, - GRASS -} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BlockProperty.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BlockProperty.java deleted file mode 100644 index d5c7123c0..000000000 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/BlockProperty.java +++ /dev/null @@ -1,204 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.container; - -import art.arcane.volmlib.util.json.JSONArray; -import art.arcane.volmlib.util.json.JSONObject; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.TreeSet; -import java.util.function.Function; - -public class BlockProperty { - private static final Set> NATIVES = Set.of(Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Boolean.class, String.class); - private final String name; - private final Class type; - - private final Object defaultValue; - private final Set values; - private final Function nameFunction; - private final Function jsonFunction; - - public > BlockProperty( - String name, - Class type, - T defaultValue, - Collection values, - Function nameFunction - ) { - this.name = name; - this.type = type; - this.defaultValue = defaultValue; - this.values = Collections.unmodifiableSet(new TreeSet<>(values)); - this.nameFunction = (Function) (Object) nameFunction; - jsonFunction = NATIVES.contains(type) ? Function.identity() : this.nameFunction::apply; - } - - public static > BlockProperty ofEnum(Class type, String name, T defaultValue) { - return new BlockProperty( - name, - type, - defaultValue, - Arrays.asList(type.getEnumConstants()), - val -> val == null ? "null" : val.name() - ); - } - - public static BlockProperty ofDouble(String name, float defaultValue, float min, float max, boolean exclusiveMin, boolean exclusiveMax) { - return new BoundedDouble( - name, - defaultValue, - min, - max, - exclusiveMin, - exclusiveMax, - (f) -> String.format("%.2f", f) - ); - } - - public static BlockProperty ofLong(String name, long defaultValue, long min, long max, boolean exclusiveMin, boolean exclusiveMax) { - return new BoundedLong( - name, - defaultValue, - min, - max, - exclusiveMin, - exclusiveMax, - value -> Long.toString(value) - ); - } - - public static BlockProperty ofBoolean(String name, boolean defaultValue) { - return new BlockProperty( - name, - Boolean.class, - defaultValue, - List.of(true, false), - (b) -> b ? "true" : "false" - ); - } - - @Override - public @NotNull String toString() { - return name + "=" + nameFunction.apply(defaultValue) + " [" + String.join(",", names()) + "]"; - } - - public String name() { - return name; - } - - public String defaultValue() { - return nameFunction.apply(defaultValue); - } - - public List names() { - return values.stream().map(nameFunction).toList(); - } - - public Object defaultValueAsJson() { - return jsonFunction.apply(defaultValue); - } - - public JSONArray valuesAsJson() { - return new JSONArray(values.stream().map(jsonFunction).toList()); - } - - public JSONObject buildJson() { - var json = new JSONObject(); - json.put("type", jsonType()); - json.put("default", defaultValueAsJson()); - if (!values.isEmpty()) json.put("enum", valuesAsJson()); - return json; - } - - public String jsonType() { - if (type == Boolean.class) - return "boolean"; - if (type == Byte.class || type == Short.class || type == Integer.class || type == Long.class) - return "integer"; - if (type == Float.class || type == Double.class) - return "number"; - return "string"; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) return true; - if (obj == null || obj.getClass() != this.getClass()) return false; - var that = (BlockProperty) obj; - return Objects.equals(this.name, that.name) && - Objects.equals(this.values, that.values) && - Objects.equals(this.type, that.type); - } - - @Override - public int hashCode() { - return Objects.hash(name, values, type); - } - - private static class BoundedLong extends BlockProperty { - private final long min; - private final long max; - private final boolean exclusiveMin; - private final boolean exclusiveMax; - - public BoundedLong( - String name, - long defaultValue, - long min, - long max, - boolean exclusiveMin, - boolean exclusiveMax, - Function nameFunction - ) { - super(name, Long.class, defaultValue, List.of(), nameFunction); - this.min = min; - this.max = max; - this.exclusiveMin = exclusiveMin; - this.exclusiveMax = exclusiveMax; - } - - @Override - public JSONObject buildJson() { - return super.buildJson() - .put("minimum", min) - .put("maximum", max) - .put("exclusiveMinimum", exclusiveMin) - .put("exclusiveMaximum", exclusiveMax); - } - } - - private static class BoundedDouble extends BlockProperty { - private final double min, max; - private final boolean exclusiveMin, exclusiveMax; - - public BoundedDouble( - String name, - double defaultValue, - double min, - double max, - boolean exclusiveMin, - boolean exclusiveMax, - Function nameFunction - ) { - super(name, Double.class, defaultValue, List.of(), nameFunction); - this.min = min; - this.max = max; - this.exclusiveMin = exclusiveMin; - this.exclusiveMax = exclusiveMax; - } - - @Override - public JSONObject buildJson() { - return super.buildJson() - .put("minimum", min) - .put("maximum", max) - .put("exclusiveMinimum", exclusiveMin) - .put("exclusiveMaximum", exclusiveMax); - } - } -} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/Pair.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/Pair.java deleted file mode 100644 index 546730c6a..000000000 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/container/Pair.java +++ /dev/null @@ -1,13 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms.container; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class Pair { - private A a; - private B b; -} diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.java b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.java index 4ca6ab3e5..6e48cd667 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.java +++ b/core/src/main/java/art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.java @@ -18,14 +18,20 @@ package art.arcane.iris.platform.bukkit.nms.v1X; +import art.arcane.volmlib.nativelib.terrain.TileWriteScheduler; + +import org.bukkit.block.data.BlockData; + +import art.arcane.volmlib.util.nbt.mca.NBTWorldSupport; + import art.arcane.iris.pack.datapack.DatapackStructureScopeIndex; import art.arcane.iris.platform.bukkit.nms.DatapackStructureScopeResult; import art.arcane.iris.structure.nativegen.IrisImportedStructureControl; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.platform.bukkit.nms.INMSBinding; -import art.arcane.iris.platform.bukkit.nms.container.BiomeColor; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.volmlib.nativelib.terrain.BiomeColor; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.generation.runtime.Engine; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -86,7 +92,7 @@ public class NMSBinding1X implements INMSBinding { } @Override - public void deserializeTile(KMap s, Location newPosition) { + public void deserializeTile(KMap s, Location newPosition, TileWriteScheduler scheduler) { } @@ -169,10 +175,7 @@ public class NMSBinding1X implements INMSBinding { return supportsCustomHeight; } - @Override - public Object getBiomeBaseFromId(int id) { - return null; - } + @Override public int getMinHeight(World world) { @@ -189,14 +192,18 @@ public class NMSBinding1X implements INMSBinding { return false; } + + + + @Override - public int getTrueBiomeBaseId(Object biomeBase) { + public int getBiomeId(Location location) { return 0; } @Override - public Object getTrueBiomeBase(Location location) { - return null; + public boolean hasBiome(String key) { + return false; } @Override @@ -204,34 +211,20 @@ public class NMSBinding1X implements INMSBinding { return null; } - @Override - public Object getCustomBiomeBaseFor(String mckey) { - return null; - } + + + @Override - public Object getCustomBiomeBaseHolderFor(String mckey) { - return null; - } - - @Override - public int getBiomeBaseIdForKey(String key) { + public int getBiomeId(String key) { return 0; } - @Override - public String getKeyForBiomeBase(Object biomeBase) { - return null; - } - public Object getBiomeBase(World world, Biome biome) { - return null; - } - @Override - public Object getBiomeBase(Object registry, Biome biome) { - return null; - } + + + @Override public KList getBiomes() { @@ -309,7 +302,7 @@ public class NMSBinding1X implements INMSBinding { } @Override - public MCAPaletteAccess createPalette() { + public MCAPaletteAccess createPalette(NBTWorldSupport.BlockStateCodec codec) { IrisLogging.error("Cannot use the global data palette! Iris is incapable of using MCA generation on this version of minecraft!"); return null; } diff --git a/core/src/main/java/art/arcane/iris/platform/generation/BukkitChunkGenerator.java b/core/src/main/java/art/arcane/iris/platform/generation/BukkitChunkGenerator.java index 9c2f3cb08..b4e3b7eb5 100644 --- a/core/src/main/java/art/arcane/iris/platform/generation/BukkitChunkGenerator.java +++ b/core/src/main/java/art/arcane/iris/platform/generation/BukkitChunkGenerator.java @@ -18,6 +18,8 @@ package art.arcane.iris.platform.generation; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; + import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.HeightMap; @@ -39,7 +41,7 @@ import art.arcane.iris.studio.StudioSVC; import art.arcane.iris.world.IrisToolbelt; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.chunk.TerrainChunk; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineTarget; @@ -64,7 +66,7 @@ import art.arcane.iris.studio.generation.JigsawStudioGenerator; import art.arcane.iris.platform.bukkit.BukkitWorldBinding; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.localization.C; import art.arcane.volmlib.util.bukkit.WorldIdentity; import art.arcane.volmlib.util.collection.KList; @@ -1312,7 +1314,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun selected.generateChunk(engine, tc, x, z); } else { ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(d); - Hunk biomes = new TerrainChunkBiomeHunkView(tc); + Hunk biomes = new TerrainChunkBiomeHunkView(tc); try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope = openGenerationHistoryCoordinateScope(engine, x << 4, z << 4); GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_terrain_stage"); @@ -1612,7 +1614,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } } - public static final class GenerationStagePermit implements AutoCloseable { + public static final class GenerationStagePermit implements NativeGenerationScope { private static final GenerationStagePermit NOOP = new GenerationStagePermit(null); private final Semaphore permits; diff --git a/core/src/main/java/art/arcane/iris/platform/generation/DummyBiomeProvider.java b/core/src/main/java/art/arcane/iris/platform/generation/DummyBiomeProvider.java index 813a6f08b..e524d4b7c 100644 --- a/core/src/main/java/art/arcane/iris/platform/generation/DummyBiomeProvider.java +++ b/core/src/main/java/art/arcane/iris/platform/generation/DummyBiomeProvider.java @@ -1,7 +1,7 @@ package art.arcane.iris.platform.generation; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import org.bukkit.block.Biome; import org.bukkit.generator.BiomeProvider; import org.bukkit.generator.WorldInfo; @@ -14,9 +14,9 @@ public class DummyBiomeProvider extends BiomeProvider { private final List ALL = resolveAll(); private static List resolveAll() { - List platformBiomes = IrisPlatforms.get().biomeWriter().allBiomes(); + List platformBiomes = IrisPlatforms.get().biomeWriter().allBiomes(); List biomes = new ArrayList<>(platformBiomes.size()); - for (PlatformBiome biome : platformBiomes) { + for (NativeBiome biome : platformBiomes) { biomes.add((Biome) biome.nativeHandle()); } return biomes; diff --git a/core/src/main/java/art/arcane/iris/platform/reflect/KeyedType.java b/core/src/main/java/art/arcane/iris/platform/reflect/KeyedType.java index cd15ab028..04b106306 100644 --- a/core/src/main/java/art/arcane/iris/platform/reflect/KeyedType.java +++ b/core/src/main/java/art/arcane/iris/platform/reflect/KeyedType.java @@ -3,8 +3,8 @@ package art.arcane.iris.platform.reflect; import com.google.gson.Gson; import com.google.gson.TypeAdapter; import com.google.gson.reflect.TypeToken; -import art.arcane.iris.platform.bukkit.registry.RegistryTypeAdapter; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryTypeAdapter; +import art.arcane.iris.platform.registry.RegistryUtil; import org.bukkit.Keyed; import org.bukkit.NamespacedKey; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Attributes.java b/core/src/main/java/art/arcane/iris/platform/registry/Attributes.java similarity index 78% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/Attributes.java rename to core/src/main/java/art/arcane/iris/platform/registry/Attributes.java index c9e01f9fc..300499578 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Attributes.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/Attributes.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; import org.bukkit.attribute.Attribute; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/KeyedRegistry.java b/core/src/main/java/art/arcane/iris/platform/registry/KeyedRegistry.java similarity index 98% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/KeyedRegistry.java rename to core/src/main/java/art/arcane/iris/platform/registry/KeyedRegistry.java index f114be688..cfe1cb81a 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/KeyedRegistry.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/KeyedRegistry.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; import art.arcane.volmlib.util.collection.KMap; import lombok.NonNull; diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Materials.java b/core/src/main/java/art/arcane/iris/platform/registry/Materials.java similarity index 53% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/Materials.java rename to core/src/main/java/art/arcane/iris/platform/registry/Materials.java index b080978df..9b3bdf3e4 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Materials.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/Materials.java @@ -1,8 +1,8 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; import org.bukkit.Material; -import static art.arcane.iris.platform.bukkit.registry.RegistryUtil.find; +import static art.arcane.iris.platform.registry.RegistryUtil.find; public class Materials { public static final Material GRASS = find(Material.class, "grass", "short_grass"); diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Particles.java b/core/src/main/java/art/arcane/iris/platform/registry/Particles.java similarity index 91% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/Particles.java rename to core/src/main/java/art/arcane/iris/platform/registry/Particles.java index 3619e4a47..efd531ff9 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/Particles.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/Particles.java @@ -1,8 +1,8 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; import org.bukkit.Particle; -import static art.arcane.iris.platform.bukkit.registry.RegistryUtil.find; +import static art.arcane.iris.platform.registry.RegistryUtil.find; /** * Bukkit particle constants. Statically imported by {@code IrisEntity} (a Gson-registered pack diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryTypeAdapter.java b/core/src/main/java/art/arcane/iris/platform/registry/RegistryTypeAdapter.java similarity index 87% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryTypeAdapter.java rename to core/src/main/java/art/arcane/iris/platform/registry/RegistryTypeAdapter.java index 127f82192..d3bf4f399 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryTypeAdapter.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/RegistryTypeAdapter.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; @@ -18,7 +18,7 @@ public class RegistryTypeAdapter extends TypeAdapter { @Nullable public static RegistryTypeAdapter of(@NonNull Class type) { - final var registry = RegistryUtil.lookup(type); + KeyedRegistry registry = RegistryUtil.lookup(type); return registry.isEmpty() ? null : new RegistryTypeAdapter<>(registry); } @@ -29,7 +29,7 @@ public class RegistryTypeAdapter extends TypeAdapter { return; } - final var key = registry.keyOf(value); + NamespacedKey key = registry.keyOf(value); if (key == null) out.nullValue(); else out.value(key.toString()); } diff --git a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryUtil.java b/core/src/main/java/art/arcane/iris/platform/registry/RegistryUtil.java similarity index 91% rename from core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryUtil.java rename to core/src/main/java/art/arcane/iris/platform/registry/RegistryUtil.java index 50cbbb7a5..791f8734b 100644 --- a/core/src/main/java/art/arcane/iris/platform/bukkit/registry/RegistryUtil.java +++ b/core/src/main/java/art/arcane/iris/platform/registry/RegistryUtil.java @@ -1,7 +1,7 @@ -package art.arcane.iris.platform.bukkit.registry; +package art.arcane.iris.platform.registry; -import art.arcane.iris.platform.bukkit.nms.container.Pair; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.collection.Pair; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import lombok.NonNull; @@ -20,7 +20,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; -import static art.arcane.iris.platform.bukkit.registry.KeyedRegistry.wrapped; +import static art.arcane.iris.platform.registry.KeyedRegistry.wrapped; @SuppressWarnings("unchecked") public class RegistryUtil { @@ -40,9 +40,9 @@ public class RegistryUtil { @NonNull public static KeyedRegistry lookup(@NonNull Class typeClass) { return (KeyedRegistry) CACHE.computeIfAbsent(typeClass, $ -> { - final var registries = new KList>(); + KList> registries = new KList>(); if (Keyed.class.isAssignableFrom(typeClass)) { - var bukkit = wrapped(getRegistry(typeClass.asSubclass(Keyed.class))); + KeyedRegistry bukkit = wrapped(getRegistry(typeClass.asSubclass(Keyed.class))); if (bukkit == null) { bukkit = Arrays.stream(Registry.class.getDeclaredFields()) .filter(field -> Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) @@ -76,7 +76,7 @@ public class RegistryUtil { .filter(field -> Keyed.class.isAssignableFrom(field.getType())) .map(field -> { try { - final var value = (Keyed) field.get(null); + Keyed value = (Keyed) field.get(null); return new Pair<>(value.getKey(), value); } catch (Throwable e) { return null; @@ -131,7 +131,7 @@ public class RegistryUtil { .filter(field -> Modifier.isPublic(field.getModifiers()) && Modifier.isStatic(field.getModifiers())) .filter(field -> Registry.class.isAssignableFrom(field.getType())) .map(field -> { - var type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; + Type type = ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0]; try { return new Pair<>(type, field.get(null)); } catch (Throwable e) { diff --git a/core/src/main/java/art/arcane/iris/structure/BulkStructureImporter.java b/core/src/main/java/art/arcane/iris/structure/BulkStructureImporter.java index f790507c9..628526012 100644 --- a/core/src/main/java/art/arcane/iris/structure/BulkStructureImporter.java +++ b/core/src/main/java/art/arcane/iris/structure/BulkStructureImporter.java @@ -19,14 +19,14 @@ package art.arcane.iris.structure; import art.arcane.iris.pack.loading.IrisData; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; import art.arcane.iris.platform.bukkit.nms.INMS; import art.arcane.iris.localization.C; import art.arcane.iris.platform.bukkit.plugin.VolmitSender; import art.arcane.volmlib.util.collection.KList; -import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -35,7 +35,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import java.util.function.Predicate; import art.arcane.iris.localization.BukkitRuntimeMessages; import art.arcane.iris.localization.IrisLanguage; @@ -524,86 +523,7 @@ public final class BulkStructureImporter { } private static List enumerateTemplateKeys() throws Exception { - Object craftServer = Bukkit.getServer(); - Object dedicated = invoke(craftServer, "getHandle"); - Object server = invoke(dedicated, "getServer"); - Object resourceManager = resolveResourceManager(server); - if (resourceManager == null) { - throw new IllegalStateException("Could not resolve the server ResourceManager via reflection"); - } - - Method listResources = null; - for (Method m : resourceManager.getClass().getMethods()) { - if (m.getName().equals("listResources") && m.getParameterCount() == 2 - && m.getParameterTypes()[0] == String.class - && m.getParameterTypes()[1] == Predicate.class - && Map.class.isAssignableFrom(m.getReturnType())) { - listResources = m; - break; - } - } - if (listResources == null) { - throw new NoSuchMethodException("listResources(String, Predicate) on " + resourceManager.getClass().getName()); - } - listResources.setAccessible(true); - - Predicate endsWithNbt = location -> { - String path = identifierPath(location); - return path != null && path.endsWith(".nbt"); - }; - - Object resultMap = listResources.invoke(resourceManager, "structure", endsWithNbt); - TreeSet keys = new TreeSet<>(); - if (resultMap instanceof Map map) { - for (Object location : map.keySet()) { - String namespace = identifierNamespace(location); - String path = identifierPath(location); - if (namespace == null || path == null) { - continue; - } - String stripped = path; - if (stripped.startsWith("structure/")) { - stripped = stripped.substring("structure/".length()); - } - if (stripped.endsWith(".nbt")) { - stripped = stripped.substring(0, stripped.length() - ".nbt".length()); - } - if (stripped.isEmpty()) { - continue; - } - keys.add(namespace + ":" + stripped); - } - } - return new ArrayList<>(keys); - } - - private static Object resolveResourceManager(Object server) { - try { - Class resourceManagerClass = Class.forName("net.minecraft.server.packs.resources.ResourceManager"); - Method getter = null; - for (Method m : server.getClass().getMethods()) { - if (m.getName().equals("getResourceManager") && m.getParameterCount() == 0 - && resourceManagerClass.isAssignableFrom(m.getReturnType())) { - getter = m; - break; - } - } - if (getter == null) { - for (Method m : server.getClass().getMethods()) { - if (m.getParameterCount() == 0 && resourceManagerClass.isAssignableFrom(m.getReturnType())) { - getter = m; - break; - } - } - } - if (getter == null) { - return null; - } - getter.setAccessible(true); - return getter.invoke(server); - } catch (Throwable e) { - return null; - } + return NativeAdapters.require(NativeStructureReader.class).templates(); } private static TreeSet normalizeKeys(Iterable keys) { @@ -621,48 +541,6 @@ public final class BulkStructureImporter { return "iris:" + name; } - private static String identifierNamespace(Object location) { - try { - Method m = location.getClass().getMethod("getNamespace"); - m.setAccessible(true); - Object value = m.invoke(location); - return value == null ? null : value.toString(); - } catch (Throwable e) { - return null; - } - } - - private static String identifierPath(Object location) { - try { - Method m = location.getClass().getMethod("getPath"); - m.setAccessible(true); - Object value = m.invoke(location); - return value == null ? null : value.toString(); - } catch (Throwable e) { - return null; - } - } - - private static Object invoke(Object target, String method) throws Exception { - Class c = target.getClass(); - while (c != null) { - for (Method m : c.getDeclaredMethods()) { - if (m.getName().equals(method) && m.getParameterCount() == 0) { - m.setAccessible(true); - return m.invoke(target); - } - } - c = c.getSuperclass(); - } - for (Method m : target.getClass().getMethods()) { - if (m.getName().equals(method) && m.getParameterCount() == 0) { - m.setAccessible(true); - return m.invoke(target); - } - } - throw new NoSuchMethodException(method + " on " + target.getClass().getName()); - } - record KeySelection(List present, List missing) { KeySelection { present = List.copyOf(present); diff --git a/core/src/main/java/art/arcane/iris/structure/VillageImporter.java b/core/src/main/java/art/arcane/iris/structure/VillageImporter.java index 4f0e20343..9f4fb5ec2 100644 --- a/core/src/main/java/art/arcane/iris/structure/VillageImporter.java +++ b/core/src/main/java/art/arcane/iris/structure/VillageImporter.java @@ -19,6 +19,8 @@ package art.arcane.iris.structure; import art.arcane.iris.pack.loading.IrisData; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; import art.arcane.iris.structure.authoring.StructureBackend; import art.arcane.iris.structure.authoring.StructureCapability; import art.arcane.iris.structure.authoring.StructureKey; @@ -40,9 +42,6 @@ import org.bukkit.structure.Structure; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; @@ -51,7 +50,6 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -93,59 +91,38 @@ public final class VillageImporter { StructureImporter.Mode activeMode = mode == null ? StructureImporter.Mode.ADD_ONLY : mode; List losses = new ArrayList<>(); boolean retryableFailure = false; - Object server; - Object registryAccess; - Object structureManager; + NativeStructureReader.Session session; String writeNote = ""; try { - Object craftServer = Bukkit.getServer(); - Object dedicated = invoke(craftServer, "getHandle"); - server = invoke(dedicated, "getServer"); - registryAccess = resolveRegistryAccess(server); - structureManager = invoke(server, "getStructureManager"); + session = NativeAdapters.require(NativeStructureReader.class).open(); } catch (Throwable e) { reportFailure(e); - return failed("Failed to access server registries via reflection: " + e, losses, true); - } - if (registryAccess == null) { - return failed("Could not resolve RegistryAccess from the server", losses, true); + return failed("Failed to access server registries: " + e, losses, true); } - Object startPool; + NativeStructureReader.Structure nativeStructure; int maxDepth; int maxDistanceFromCenter; try { - Object structureRegistry = lookupRegistry(registryAccess, "STRUCTURE"); - Object structure = registryGet(structureRegistry, structureKey); - if (structure == null) { + nativeStructure = session.structure(structureKey.toString()); + if (nativeStructure == null) { return failed("No structure registered for key " + structureKey, losses); } - if (!structure.getClass().getName().endsWith("JigsawStructure")) { + if (!nativeStructure.jigsaw()) { return failed("Structure " + structureKey + " is not a jigsaw structure (" - + structure.getClass().getSimpleName() + "); use 'import' for single-template structures", losses); + + nativeStructure.typeName() + "); use 'import' for single-template structures", losses); } - Object startPoolHolder = invoke(structure, "getStartPool"); - startPool = unwrapHolder(startPoolHolder); - maxDepth = readIntMember(structure, "maxDepth"); - maxDistanceFromCenter = readIntMember(structure, "maxDistanceFromCenter"); + maxDepth = nativeStructure.maxDepth(); + maxDistanceFromCenter = nativeStructure.maxDistanceFromCenter(); } catch (Throwable e) { reportFailure(e); return failed("Failed to read jigsaw structure graph: " + e, losses, true); } - Object templatePoolRegistry; - java.util.Random random; - try { - templatePoolRegistry = lookupRegistry(registryAccess, "TEMPLATE_POOL"); - random = new java.util.Random(structureKey.hashCode()); - } catch (Throwable e) { - reportFailure(e); - return failed("Failed to access TEMPLATE_POOL registry: " + e, losses, true); - } - + java.util.Random random = new java.util.Random(structureKey.hashCode()); String startPoolKey; try { - startPoolKey = registryKeyOf(templatePoolRegistry, startPool); + startPoolKey = nativeStructure.startPoolKey(); } catch (Throwable e) { reportFailure(e); return failed("Could not resolve the start pool key for " + structureKey + ": " + e, losses, true); @@ -179,9 +156,9 @@ public final class VillageImporter { if (!visitedPools.add(poolKey)) { continue; } - Object pool; + NativeStructureReader.Pool pool; try { - pool = registryGetByKey(templatePoolRegistry, poolKey); + pool = session.pool(poolKey); } catch (Throwable e) { reportFailure(e); retryableFailure = true; @@ -198,9 +175,7 @@ public final class VillageImporter { String fallbackKey = null; try { - Object fallbackHolder = invoke(pool, "getFallback"); - Object fallbackPool = unwrapHolder(fallbackHolder); - fallbackKey = registryKeyOf(templatePoolRegistry, fallbackPool); + fallbackKey = pool.fallbackKey(); } catch (Throwable e) { reportFailure(e); retryableFailure = true; @@ -214,9 +189,9 @@ public final class VillageImporter { poolQueue.add(fallbackKey); } - List templates; + List templates; try { - templates = (List) invoke(pool, "getTemplates"); + templates = pool.entries(); } catch (Throwable e) { reportFailure(e); retryableFailure = true; @@ -224,13 +199,12 @@ public final class VillageImporter { templates = List.of(); } - for (Object pair : templates) { - Object element; + for (NativeStructureReader.Entry pair : templates) { + NativeStructureReader.Element element; int weight; try { - element = invoke(pair, "getFirst"); - Object second = invoke(pair, "getSecond"); - weight = second instanceof Number ? Math.max(1, ((Number) second).intValue()) : 1; + element = pair.element(); + weight = Math.max(1, pair.weight()); } catch (Throwable e) { reportFailure(e); retryableFailure = true; @@ -261,12 +235,12 @@ public final class VillageImporter { losses.add(listElementFallbackLoss(elementResolution, poolKey) .affecting("jigsaw-pools/" + irisPoolName + ".json")); } - Object physicalElement = elementResolution.physicalElement(); + NativeStructureReader.Element physicalElement = elementResolution.physicalElement(); String templateLocation = elementResolution.templateLocation(); if (templateLocation == null) { String elementType = physicalElement == null - ? element.getClass().getSimpleName() - : physicalElement.getClass().getSimpleName(); + ? element.typeName() + : physicalElement.typeName(); if (elementType.endsWith("EmptyPoolElement")) { pieceEntries.add(emptyPoolEntry(weight)); emittedPoolMembers++; @@ -316,7 +290,7 @@ public final class VillageImporter { losses.add(loss.affecting("objects/" + irisPieceName + ".iob")); } - Connectors result = readConnectors(element, structureManager, random, name, irisPieceName); + Connectors result = readConnectors(element, random, name, irisPieceName); retryableFailure |= result.retryableFailure(); importedTemplate = new ImportedTemplate( captured.object(), @@ -508,8 +482,7 @@ public final class VillageImporter { } private static Connectors readConnectors( - Object element, - Object structureManager, + NativeStructureReader.Element element, java.util.Random random, String baseName, String pieceName @@ -520,10 +493,8 @@ public final class VillageImporter { boolean retryableFailure = false; String affectedResource = "jigsaw-pieces/" + pieceName + ".json"; try { - Object zero = staticField("net.minecraft.core.BlockPos", "ZERO"); - Object rotationNone = staticField("net.minecraft.world.level.block.Rotation", "NONE"); - Method m = findMethod4(element.getClass(), "getShuffledJigsawBlocks"); - if (m == null) { + NativeStructureReader.ConnectorSet extracted = element.connectors(random::nextLong); + if (extracted.status() == NativeStructureReader.ConnectorStatus.UNSUPPORTED) { losses.add(StructureLoss.warning( StructureCapability.CONNECTORS, "connector_extraction_unavailable", @@ -531,10 +502,7 @@ public final class VillageImporter { .affecting(affectedResource)); return new Connectors(connectors, targets, losses, false); } - m.setAccessible(true); - Object random0 = freshRandomSource(random); - List blocks = (List) m.invoke(element, structureManager, zero, rotationNone, random0); - if (blocks == null) { + if (extracted.status() == NativeStructureReader.ConnectorStatus.MISSING) { losses.add(StructureLoss.warning( StructureCapability.CONNECTORS, "connector_extraction_returned_null", @@ -542,7 +510,7 @@ public final class VillageImporter { .affecting(affectedResource)); return new Connectors(connectors, targets, losses, false); } - for (Object jigsaw : blocks) { + for (NativeStructureReader.Connector jigsaw : extracted.connectors()) { String[] rawPoolKey = new String[1]; try { Map connector = connectorFrom(jigsaw, baseName, rawPoolKey); @@ -572,38 +540,11 @@ public final class VillageImporter { return new Connectors(connectors, targets, losses, retryableFailure); } - private static Map connectorFrom(Object jigsaw, String baseName, String[] rawPoolKeyOut) throws Exception { - Object info = invoke(jigsaw, "info"); - Object pos = invoke(info, "pos"); - Object blockState = invoke(info, "state"); - int x = readInt(pos, "getX"); - int y = readInt(pos, "getY"); - int z = readInt(pos, "getZ"); - - Object poolKey = invoke(jigsaw, "pool"); - String poolId = identifierString(invoke(poolKey, "identifier")); - Object nameId = invoke(jigsaw, "name"); - Object targetId = invoke(jigsaw, "target"); - Object jointType = invoke(jigsaw, "jointType"); - - String front = frontFacing(blockState); - String top = topFacing(blockState); - rawPoolKeyOut[0] = poolId; - - ConnectorMetadata metadata = readConnectorMetadata(jigsaw, info); - return connectorJson( - x, - y, - z, - front, - top, - poolId, - baseName, - identifierString(nameId), - identifierString(targetId), - jointType, - metadata - ); + private static Map connectorFrom(NativeStructureReader.Connector jigsaw, String baseName, String[] rawPoolKeyOut) throws Exception { + NativeStructureReader.ConnectorData value = jigsaw.read(); + rawPoolKeyOut[0] = value.pool(); + return connectorJson(value.x(), value.y(), value.z(), value.front(), value.top(), value.pool(), baseName, + value.name(), value.target(), value.joint(), readConnectorMetadata(value.metadata())); } static Map connectorJson( @@ -637,73 +578,12 @@ public final class VillageImporter { return connector; } - static ConnectorMetadata readConnectorMetadata(Object jigsaw, Object info) throws Exception { - Object nbt = invoke(info, "nbt"); - String finalState = readNbtString(nbt, "final_state"); + static ConnectorMetadata readConnectorMetadata(NativeStructureReader.ConnectorMetadata source) { + String finalState = source.finalState(); String normalizedFinalState = finalState == null || finalState.isBlank() ? "minecraft:air" : StructureImporter.normalizeJigsawFinalState(finalState); - return new ConnectorMetadata( - normalizedFinalState, - readIntAccessor(jigsaw, "selectionPriority"), - readIntAccessor(jigsaw, "placementPriority") - ); - } - - private static String readNbtString(Object nbt, String key) throws Exception { - if (nbt == null) { - return null; - } - Method method = findMethod(nbt.getClass(), "getString", 1); - if (method == null) { - throw new NoSuchMethodException("getString(String) on " + nbt.getClass().getName()); - } - method.setAccessible(true); - Object value = method.invoke(nbt, key); - if (value instanceof String string) { - return string; - } - if (value instanceof Optional optional) { - return optional.isPresent() ? String.valueOf(optional.get()) : null; - } - return value == null ? null : String.valueOf(value); - } - - private static int readIntAccessor(Object value, String accessor) throws Exception { - Method method = findMethod(value.getClass(), accessor); - if (method == null) { - throw new NoSuchMethodException(accessor + "() on " + value.getClass().getName()); - } - method.setAccessible(true); - Object result = method.invoke(value); - if (result instanceof Number number) { - return number.intValue(); - } - throw new IllegalStateException(accessor + " on " + value.getClass().getName() + " is not numeric"); - } - - private static String frontFacing(Object blockState) throws Exception { - Class jigsawBlock = Class.forName("net.minecraft.world.level.block.JigsawBlock"); - Method getFront = jigsawBlock.getMethod("getFrontFacing", Class.forName("net.minecraft.world.level.block.state.BlockState")); - Object direction = getFront.invoke(null, blockState); - if (direction == null) { - return "north"; - } - Method getName = direction.getClass().getMethod("getName"); - getName.setAccessible(true); - return String.valueOf(getName.invoke(direction)).toLowerCase(); - } - - private static String topFacing(Object blockState) throws Exception { - Class jigsawBlock = Class.forName("net.minecraft.world.level.block.JigsawBlock"); - Method getTop = jigsawBlock.getMethod("getTopFacing", Class.forName("net.minecraft.world.level.block.state.BlockState")); - Object direction = getTop.invoke(null, blockState); - if (direction == null) { - return "up"; - } - Method getName = direction.getClass().getMethod("getName"); - getName.setAccessible(true); - return String.valueOf(getName.invoke(direction)).toLowerCase(); + return new ConnectorMetadata(normalizedFinalState, source.selectionPriority(), source.placementPriority()); } private static String irisDirection(String front) { @@ -717,26 +597,13 @@ public final class VillageImporter { }; } - private static String templateLocationOf(Object element) throws Exception { - Method m = findMethod(element.getClass(), "getTemplateLocation"); - if (m == null) { - return null; - } - m.setAccessible(true); - Object id = m.invoke(element); - return identifierString(id); - } - - static PoolElementResolution resolvePoolElement(Object element) throws Exception { + static PoolElementResolution resolvePoolElement(NativeStructureReader.Element element) throws Exception { if (element == null) { return new PoolElementResolution(null, null, 0, 0); } - if (!element.getClass().getSimpleName().endsWith("ListPoolElement")) { - return new PoolElementResolution(element, templateLocationOf(element), 0, 0); - } - Object rawElements = invoke(element, "getElements"); - if (!(rawElements instanceof List elements)) { - throw new IllegalStateException("getElements on " + element.getClass().getName() + " is not a list"); + List elements = element.children(); + if (elements == null) { + return new PoolElementResolution(element, element.templateLocation(), 0, 0); } if (elements.isEmpty()) { return new PoolElementResolution(null, null, 1, 0); @@ -763,283 +630,6 @@ public final class VillageImporter { ); } - private static Object resolveRegistryAccess(Object server) { - try { - Class frozen = Class.forName("net.minecraft.core.RegistryAccess$Frozen"); - for (Method m : server.getClass().getMethods()) { - if (m.getParameterCount() == 0 && frozen.isAssignableFrom(m.getReturnType())) { - m.setAccessible(true); - Object o = m.invoke(server); - if (o != null) { - return o; - } - } - } - Class ra = Class.forName("net.minecraft.core.RegistryAccess"); - for (Method m : server.getClass().getMethods()) { - if (m.getParameterCount() == 0 && ra.isAssignableFrom(m.getReturnType())) { - m.setAccessible(true); - Object o = m.invoke(server); - if (o != null) { - return o; - } - } - } - } catch (Throwable e) { - reportFailure(e); - } - return null; - } - - private static Object lookupRegistry(Object registryAccess, String registryName) throws Exception { - Class registries = Class.forName("net.minecraft.core.registries.Registries"); - Object resourceKey = registries.getField(registryName).get(null); - Class registryClass = Class.forName("net.minecraft.core.Registry"); - Method registryOverload = null; - for (Method m : registryAccess.getClass().getMethods()) { - if (m.getName().equals("lookupOrThrow") && m.getParameterCount() == 1 - && m.getParameterTypes()[0].getName().endsWith("ResourceKey") - && registryClass.isAssignableFrom(m.getReturnType())) { - registryOverload = m; - break; - } - } - if (registryOverload == null) { - for (Method m : registryAccess.getClass().getMethods()) { - if (m.getName().equals("lookupOrThrow") && m.getParameterCount() == 1 - && m.getParameterTypes()[0].getName().endsWith("ResourceKey")) { - registryOverload = m; - break; - } - } - } - if (registryOverload == null) { - throw new NoSuchMethodException("lookupOrThrow(ResourceKey) on " + registryAccess.getClass().getName()); - } - registryOverload.setAccessible(true); - return registryOverload.invoke(registryAccess, resourceKey); - } - - private static Object registryGet(Object registry, NamespacedKey key) throws Exception { - Object id = identifierOf(key); - for (Method m : registry.getClass().getMethods()) { - if (m.getName().equals("getValue") && m.getParameterCount() == 1 && m.getParameterTypes()[0].getName().endsWith("Identifier")) { - m.setAccessible(true); - return m.invoke(registry, id); - } - } - for (Method m : registry.getClass().getMethods()) { - if (m.getName().equals("getOptional") && m.getParameterCount() == 1 && m.getParameterTypes()[0].getName().endsWith("Identifier")) { - m.setAccessible(true); - return unwrapOptional(m.invoke(registry, id)); - } - } - return null; - } - - private static Object registryGetByKey(Object registry, String key) throws Exception { - NamespacedKey nk = NamespacedKey.fromString(key.toLowerCase()); - if (nk == null) { - return null; - } - return registryGet(registry, nk); - } - - private static String registryKeyOf(Object registry, Object value) throws Exception { - if (value == null) { - return null; - } - for (Method m : registry.getClass().getMethods()) { - if (m.getName().equals("getKey") && m.getParameterCount() == 1) { - m.setAccessible(true); - Object id = m.invoke(registry, value); - String s = identifierString(id); - if (s != null) { - return s; - } - } - } - return null; - } - - private static Object identifierOf(NamespacedKey key) throws Exception { - Class identifier = Class.forName("net.minecraft.resources.Identifier"); - try { - Method fromNamespaceAndPath = identifier.getMethod("fromNamespaceAndPath", String.class, String.class); - return fromNamespaceAndPath.invoke(null, key.getNamespace(), key.getKey()); - } catch (NoSuchMethodException e) { - Method withDefaultNamespace = identifier.getMethod("parse", String.class); - return withDefaultNamespace.invoke(null, key.toString()); - } - } - - private static String identifierString(Object id) { - if (id == null) { - return null; - } - try { - Method getNamespace = id.getClass().getMethod("getNamespace"); - Method getPath = id.getClass().getMethod("getPath"); - getNamespace.setAccessible(true); - getPath.setAccessible(true); - return getNamespace.invoke(id) + ":" + getPath.invoke(id); - } catch (Throwable e) { - return id.toString(); - } - } - - private static Object unwrapHolder(Object holder) { - if (holder == null) { - return null; - } - try { - Method value = findMethod(holder.getClass(), "value"); - if (value != null) { - value.setAccessible(true); - return value.invoke(holder); - } - } catch (Throwable ignored) { - } - return holder; - } - - private static Object unwrapOptional(Object opt) { - if (opt == null) { - return null; - } - if (opt instanceof java.util.Optional o) { - return o.orElse(null); - } - return opt; - } - - static int readIntMember(Object value, String memberName) throws Exception { - Class type = value.getClass(); - while (type != null) { - try { - Field field = type.getDeclaredField(memberName); - field.setAccessible(true); - return coerceInt(field.get(value), memberName, value); - } catch (NoSuchFieldException ignored) { - type = type.getSuperclass(); - } - } - Method method = findMethod(value.getClass(), memberName); - if (method != null) { - method.setAccessible(true); - return coerceInt(method.invoke(value), memberName, value); - } - throw new NoSuchFieldException(memberName + " on " + value.getClass().getName()); - } - - /** - * Members that are plain numbers on one server build are wrapper objects on another - * (JigsawStructure.maxDistanceFromCenter became a MaxDistance{horizontal, vertical} record). - * Numbers pass through; a wrapper contributes its horizontal component, else its largest - * integral component, so a distance bound is never under-read. - */ - private static int coerceInt(Object member, String memberName, Object owner) throws Exception { - if (member instanceof Number n) { - return n.intValue(); - } - if (member == null) { - throw new NoSuchFieldException(memberName + " on " + owner.getClass().getName() + " is null"); - } - Method horizontal = findMethod(member.getClass(), "horizontal"); - if (horizontal != null && Number.class.isAssignableFrom(boxedType(horizontal.getReturnType()))) { - horizontal.setAccessible(true); - return ((Number) horizontal.invoke(member)).intValue(); - } - Integer widest = null; - for (Field component : member.getClass().getDeclaredFields()) { - if (Modifier.isStatic(component.getModifiers()) - || !Number.class.isAssignableFrom(boxedType(component.getType()))) { - continue; - } - component.setAccessible(true); - Object componentValue = component.get(member); - if (componentValue instanceof Number n && (widest == null || n.intValue() > widest)) { - widest = n.intValue(); - } - } - if (widest != null) { - return widest; - } - throw new NoSuchFieldException(memberName + " on " + owner.getClass().getName() - + " is a " + member.getClass().getName() + " with no integral component"); - } - - private static Class boxedType(Class type) { - return type == int.class ? Integer.class : type; - } - - private static int readInt(Object o, String method) throws Exception { - Method m = o.getClass().getMethod(method); - m.setAccessible(true); - return ((Number) m.invoke(o)).intValue(); - } - - private static Object staticField(String className, String fieldName) throws Exception { - Class c = Class.forName(className); - Field f = c.getField(fieldName); - return f.get(null); - } - - private static Object invoke(Object target, String method) throws Exception { - Method m = findMethod(target.getClass(), method); - if (m == null) { - throw new NoSuchMethodException(method + " on " + target.getClass().getName()); - } - m.setAccessible(true); - return m.invoke(target); - } - - private static Method findMethod(Class type, String name) { - Class c = type; - while (c != null) { - for (Method m : c.getDeclaredMethods()) { - if (m.getName().equals(name) && m.getParameterCount() == 0) { - return m; - } - } - c = c.getSuperclass(); - } - for (Method m : type.getMethods()) { - if (m.getName().equals(name) && m.getParameterCount() == 0) { - return m; - } - } - return null; - } - - private static Method findMethod4(Class type, String name) { - return findMethod(type, name, 4); - } - - private static Method findMethod(Class type, String name, int parameterCount) { - Class c = type; - while (c != null) { - for (Method m : c.getDeclaredMethods()) { - if (m.getName().equals(name) && m.getParameterCount() == parameterCount) { - return m; - } - } - c = c.getSuperclass(); - } - for (Method m : type.getMethods()) { - if (m.getName().equals(name) && m.getParameterCount() == parameterCount) { - return m; - } - } - return null; - } - - private static Object freshRandomSource(java.util.Random random) throws Exception { - Class randomSource = Class.forName("net.minecraft.util.RandomSource"); - Method create = randomSource.getMethod("create", long.class); - return create.invoke(null, random.nextLong()); - } - static String poolName(String base, String poolKey) { StructureKey key = StructureKey.parse(poolKey); return base + "/pool/" + key.namespace() + "/" + key.path(); @@ -1257,7 +847,7 @@ public final class VillageImporter { } record PoolElementResolution( - Object physicalElement, + NativeStructureReader.Element physicalElement, String templateLocation, int listLevels, int omittedElements diff --git a/core/src/main/java/art/arcane/iris/structure/export/VanillaJigsawExportCompiler.java b/core/src/main/java/art/arcane/iris/structure/export/VanillaJigsawExportCompiler.java index aa80f5bff..cfa0f96a9 100644 --- a/core/src/main/java/art/arcane/iris/structure/export/VanillaJigsawExportCompiler.java +++ b/core/src/main/java/art/arcane/iris/structure/export/VanillaJigsawExportCompiler.java @@ -17,7 +17,7 @@ import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.structure.placement.IrisStructure; import art.arcane.iris.structure.object.ObjectPlaceMode; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -386,8 +386,8 @@ final class VanillaJigsawExportCompiler { pieceKey, "The .iob contains tile payloads; exact registry-aware block-entity NBT export is not available in core."); } - for (Map.Entry entry : object.getBlocks()) { - PlatformBlockState state = entry.getValue(); + for (Map.Entry entry : object.getBlocks()) { + NativeBlockState state = entry.getValue(); String resource = pieceKey + "@" + entry.getKey(); IrisBlockVector position = entry.getKey(); int x = position.getBlockX() + object.getCenter().getX(); @@ -519,7 +519,7 @@ final class VanillaJigsawExportCompiler { return; } IrisPosition position = connector.getPosition(); - PlatformBlockState objectState = object.getBlocks().get( + NativeBlockState objectState = object.getBlocks().get( object.getSigned(position.getX(), position.getY(), position.getZ())); String expectedSource = objectState == null ? "minecraft:structure_void" : objectState.key(); try { diff --git a/core/src/main/java/art/arcane/iris/structure/export/VanillaStructureTemplateEncoder.java b/core/src/main/java/art/arcane/iris/structure/export/VanillaStructureTemplateEncoder.java index dde8b69eb..33a6bd675 100644 --- a/core/src/main/java/art/arcane/iris/structure/export/VanillaStructureTemplateEncoder.java +++ b/core/src/main/java/art/arcane/iris/structure/export/VanillaStructureTemplateEncoder.java @@ -7,7 +7,7 @@ import art.arcane.iris.structure.jigsaw.IrisJigsawConnector; import art.arcane.iris.structure.jigsaw.IrisJigsawPiece; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.value.IrisPosition; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.nbt.io.NBTUtil; @@ -76,7 +76,7 @@ final class VanillaStructureTemplateEncoder { private Map objectBlocks(IrisObject object) { Map blocks = new LinkedHashMap<>(); Vector3i center = object.getCenter(); - for (Map.Entry entry : object.getBlocks()) { + for (Map.Entry entry : object.getBlocks()) { IrisBlockVector signed = entry.getKey(); BlockPosition position = new BlockPosition( signed.getBlockX() + center.getX(), diff --git a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawConfiguration.java b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawConfiguration.java index 0e5e6b257..0e65daddf 100644 --- a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawConfiguration.java +++ b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawConfiguration.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.jigsaw; +import art.arcane.volmlib.nativelib.terrain.structure.JigsawSettings; + import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -14,7 +16,7 @@ import lombok.experimental.Accessors; @AllArgsConstructor @Data @Description("Optional overrides applied to a live vanilla, datapack, or modded jigsaw definition before Minecraft assembles it. Omitted values preserve the registered source definition.") -public class IrisJigsawConfiguration { +public class IrisJigsawConfiguration implements JigsawSettings { @Description("Optional registered template-pool key used instead of the source jigsaw's start pool. Empty preserves the source pool.") @RegistryListNativeJigsawPool private String startPool = ""; diff --git a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawHeightmap.java b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawHeightmap.java index 96c4bc9d0..e75620d9e 100644 --- a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawHeightmap.java +++ b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawHeightmap.java @@ -1,9 +1,11 @@ package art.arcane.iris.structure.jigsaw; +import art.arcane.volmlib.nativelib.terrain.structure.JigsawHeightmap; + import art.arcane.volmlib.util.documentation.Description; @Description("Controls whether a configured native jigsaw projects its start onto a Minecraft heightmap.") -public enum IrisJigsawHeightmap { +public enum IrisJigsawHeightmap implements JigsawHeightmap { @Description("Keeps the registered structure's own heightmap projection exactly as the vanilla, datapack, or mod definition declares it.") SOURCE, @@ -26,5 +28,15 @@ public enum IrisJigsawHeightmap { MOTION_BLOCKING, @Description("Behaves like WORLD_SURFACE in Iris worlds; Iris height queries have no leaf layer to skip.") - MOTION_BLOCKING_NO_LEAVES + MOTION_BLOCKING_NO_LEAVES; + + @Override + public boolean useSource() { + return this == SOURCE; + } + + @Override + public boolean removeProjection() { + return this == NONE; + } } diff --git a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawLiquidSettings.java b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawLiquidSettings.java index 2ebda6b02..81ace3907 100644 --- a/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawLiquidSettings.java +++ b/core/src/main/java/art/arcane/iris/structure/jigsaw/IrisJigsawLiquidSettings.java @@ -1,9 +1,11 @@ package art.arcane.iris.structure.jigsaw; +import art.arcane.volmlib.nativelib.terrain.structure.JigsawLiquidSettings; + import art.arcane.volmlib.util.documentation.Description; @Description("Controls waterlogging behavior for a configured native jigsaw.") -public enum IrisJigsawLiquidSettings { +public enum IrisJigsawLiquidSettings implements JigsawLiquidSettings { @Description("Keeps the registered structure's own liquid setting; vanilla defaults to applying waterlogging when the definition omits it.") SOURCE, @@ -11,5 +13,10 @@ public enum IrisJigsawLiquidSettings { IGNORE_WATERLOGGING, @Description("Re-applies pre-existing water into placed waterloggable blocks and floods from neighboring sources, so underwater pieces come out waterlogged.") - APPLY_WATERLOGGING + APPLY_WATERLOGGING; + + @Override + public boolean useSource() { + return this == SOURCE; + } } diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/GenerationWritePolicy.java b/core/src/main/java/art/arcane/iris/structure/nativegen/GenerationWritePolicy.java new file mode 100644 index 000000000..7c055204f --- /dev/null +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/GenerationWritePolicy.java @@ -0,0 +1,49 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.generation.runtime.DimensionStackContext; +import art.arcane.iris.generation.runtime.DimensionStackLayout; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; +import art.arcane.iris.structure.object.IrisStaticObjectLayer; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public final class GenerationWritePolicy { + private GenerationWritePolicy() { + } + + public static boolean isHistoricalStructure(Engine engine, long activation) { + if (activation <= 0 || !(engine instanceof IrisEngine irisEngine)) { + return false; + } + GenerationHistoryRuntimeRouter router = irisEngine.getGenerationHistoryRuntimeRouter().orElse(null); + return router != null && activation < router.history().activeActivation().activationId(); + } + + public static NativeBlockPositionPredicate protectedPositions( + IrisStaticObjectLayer staticObjects, + DimensionStackContext stackContext, + int minimumY + ) { + Map layouts = new ConcurrentHashMap<>(); + return (x, y, z) -> { + if (!staticObjects.isEmpty() && staticObjects.contains( + x, y - minimumY, z)) { + return true; + } + if (stackContext == null) { + return false; + } + long columnKey = ((long) x << 32) + ^ (z & 0xFFFFFFFFL); + DimensionStackLayout layout = layouts.computeIfAbsent( + columnKey, + ignored -> stackContext.sample(x, z) + ); + return layout.isHostFeatureProtectedY(y - minimumY); + }; + } +} diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/ImportedFeaturePolicy.java b/core/src/main/java/art/arcane/iris/structure/nativegen/ImportedFeaturePolicy.java new file mode 100644 index 000000000..d895d4fa1 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/ImportedFeaturePolicy.java @@ -0,0 +1,130 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; + +import art.arcane.iris.generation.runtime.DimensionStackContext; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.biome.IrisBiome; +import art.arcane.iris.generation.biome.IrisBiomeCustom; +import art.arcane.iris.generation.terrain.IrisDimension; +import art.arcane.iris.structure.object.IrisStaticObjectLayer; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.feature.NativeImportedFeatureControl; +import art.arcane.volmlib.nativelib.terrain.feature.NativeImportedFeaturePolicy; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class ImportedFeaturePolicy implements NativeImportedFeaturePolicy { + private static final Placement UNGUARDED = new Placement(false, false, null, null, null, null); + + protected final Engine engine; + + public ImportedFeaturePolicy(Engine engine) { + this.engine = Objects.requireNonNull(engine, "engine"); + } + + @Override + public IrisDimension dimension() { + return engine.getDimension(); + } + + @Override + public int runtimeId() { + return engine.getCacheID(); + } + + @Override + public String dimensionKey() { + IrisDimension dimension = engine.getDimension(); + return dimension == null ? "" : dimension.getLoadKey(); + } + + @Override + public NativeImportedFeatureControl control() { + return NativeFeatureGenerationPolicy.control(engine); + } + + @Override + public Set visibleBiomeKeys() { + Set keys = new LinkedHashSet<>(); + for (IrisBiome biome : engine.getDimension().getReachableBiomes(engine)) { + keys.add(biome.getStructureDerivativeKey()); + keys.add(biome.getDerivativeKey()); + if (includeVanillaDerivative()) { + keys.add(biome.getVanillaDerivativeKey()); + } + keys.addAll(biome.getBiomeScatter()); + keys.addAll(biome.getBiomeSkyScatter()); + if (biome.isCustom()) { + for (IrisBiomeCustom custom : biome.getCustomDerivitives()) { + keys.add(customBiomeKey(custom)); + } + } + } + return keys; + } + + @Override + public List customBiomeDerivatives() { + List derivatives = new ArrayList<>(); + for (IrisBiome biome : engine.getDimension().getReachableBiomes(engine)) { + if (!biome.isCustom()) { + continue; + } + List customKeys = new ArrayList<>(); + for (IrisBiomeCustom custom : biome.getCustomDerivitives()) { + customKeys.add(customBiomeKey(custom)); + } + derivatives.add(new DerivativeGroup(biome.getLoadKey(), biome.getVanillaDerivativeKey(), customKeys)); + } + return derivatives; + } + + protected boolean includeVanillaDerivative() { + return true; + } + + protected String customBiomeKey(IrisBiomeCustom biome) { + return engine.getData().customBiomeResourceKey(engine.getDimension(), biome); + } + + @Override + public Placement placement() { + IrisStaticObjectLayer staticObjects = engine.getDimension().getStaticObjectLayer(engine.getData()); + int minimumY = engine.getMinHeight(); + DimensionStackContext stack = engine.getDimensionStackContext(); + if (staticObjects.isEmpty() && stack == null) { + return UNGUARDED; + } + NativeBlockPositionPredicate protectedPosition = + GenerationWritePolicy.protectedPositions(staticObjects, stack, minimumY); + return new Placement(!staticObjects.isEmpty() || stack != null, stack != null, + (x, z) -> Engine.hostHeight(engine, x, z, false) + minimumY + 1, + (x, z) -> Engine.hostHeight(engine, x, z, true) + minimumY + 1, + protectedPosition, + (x, y, z) -> stack == null || stack.getLayerAt(x, y, z).terrainContext().isSelfReferencing()); + } + + @Override + public void info(String message) { + IrisLogging.info("Iris " + message); + } + + @Override + public void warn(String message) { + IrisLogging.warn("Iris " + message); + } + + @Override + public void error(String message, Throwable cause) { + if (cause == null) { + IrisLogging.error("Iris " + message); + } else { + IrisLogging.reportError("Iris " + message, cause); + } + } +} diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedFeatureControl.java b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedFeatureControl.java index 9bd7b9859..5cc403dd6 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedFeatureControl.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedFeatureControl.java @@ -19,6 +19,7 @@ package art.arcane.iris.structure.nativegen; import art.arcane.iris.generation.decoration.IrisDecorationStep; +import art.arcane.volmlib.nativelib.terrain.feature.NativeImportedFeatureControl; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; @@ -35,7 +36,7 @@ import java.util.Objects; @AllArgsConstructor @Description("Controls native vanilla, mod, and ingested datapack PLACED FEATURE generation for this dimension (set as the dimension's 'importedFeatures' field). Placed features are ores, trees, plants, springs, geodes and every other decoration entry a biome declares. This is OFF by default: with 'enabled' false Iris generates exactly the terrain it always has and no native feature runs. With it true, every placed feature the biome's vanilla derivative declares runs over Iris terrain, in vanilla step order, on the same worldgen thread vanilla uses. Carvers are never imported - Iris has no NoiseGeneratorSettings, so there is nothing for a carver to carve against. Family matching on 'disabled' uses namespace, slash, or underscore boundaries, so 'minecraft:ore' covers every vanilla ore feature without matching unrelated names.") @Data -public class IrisImportedFeatureControl { +public class IrisImportedFeatureControl implements NativeImportedFeatureControl { @Description("Master switch. False (the default) means no native placed feature generates and chunk output is identical to a pack without this block at all. True runs the vanilla decoration feature pass over Iris terrain.") private boolean enabled = false; @@ -105,4 +106,14 @@ public class IrisImportedFeatureControl { } return NativeFeatureGenerationStatus.GENERATE_NATIVE; } + @Override + public boolean shouldGenerateStepOrdinal(int step) { + return shouldGenerateStep(IrisDecorationStep.byOrdinal(step)); + } + + @Override + public boolean hasFeatureFilter() { + return disabled != null && !disabled.isEmpty(); + } + } diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControl.java b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControl.java index b8f81a85a..b4cfe76ed 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControl.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControl.java @@ -18,6 +18,8 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.StructureFrequencyControl; + import art.arcane.iris.structure.placement.IrisStructureSetFrequencyOverride; import art.arcane.iris.structure.placement.IrisStructureStiltSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; @@ -42,7 +44,7 @@ import java.util.Objects; @AllArgsConstructor @Description("Controls native vanilla, mod, and ingested datapack structure generation for this dimension (set as the dimension's 'importedStructures' field). Every registered structure generates through its native placement unless its key matches 'disabled', equals a key in 'disabledExact', or a viable dimension-level Iris placement explicitly replaces its source. Family matching uses namespace, slash, or underscore boundaries, while exact matching compares normalized complete keys only. Run '/iris structure list ' to dump every valid key. Only affects newly generated chunks and is separate from Iris structure placements.") @Data -public class IrisImportedStructureControl { +public class IrisImportedStructureControl implements StructureFrequencyControl { @ArrayType(type = String.class, min = 1) @RegistryListVanillaStructure(prefixes = true) @Description("Structure keys to deny explicitly, e.g. 'minecraft:stronghold'. A namespace:path prefix also matches, so 'minecraft:village' disables every village variant and 'minecraft:ruined_portal' disables every ruined portal. Every key not matched here remains enabled.") diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisNativeStructureDecision.java b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisNativeStructureDecision.java index 5331507ee..698f65fe2 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisNativeStructureDecision.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisNativeStructureDecision.java @@ -18,6 +18,8 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.StructurePlacementDecision; + import art.arcane.iris.structure.placement.IrisStructureStiltSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; import art.arcane.iris.structure.placement.IrisStructureYBand; @@ -29,7 +31,7 @@ public record IrisNativeStructureDecision( boolean preserveSourceY, IrisStructureStiltSettings stilt, IrisStructureTerrain terrain -) { +) implements StructurePlacementDecision { public boolean generate() { return status == NativeStructureGenerationStatus.GENERATE_NATIVE; } diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructurePolicy.java b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructurePolicy.java new file mode 100644 index 000000000..f67c84732 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructurePolicy.java @@ -0,0 +1,103 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.structure.StructureFingerprint; +import art.arcane.volmlib.nativelib.terrain.structure.StructureInjectionPolicy; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferencePolicy; +import art.arcane.iris.generation.runtime.IrisEngine; +import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import java.util.List; +import java.util.Objects; + +public record IrisStructurePolicy(Engine engine) + implements StructureInjectionPolicy, + StructureReferencePolicy { + private static final Set WARNED_POLICY_INVALIDATIONS = ConcurrentHashMap.newKeySet(); + + public IrisStructurePolicy { + Objects.requireNonNull(engine, "Native structure injection requires an engine"); + } + + @Override + public List plansAt(int chunkX, int chunkZ) { + return NativeStructurePlacementPlanner.plansAt(engine, chunkX, chunkZ); + } + + @Override + public boolean sourceReplaced(String structureKey, boolean underground) { + return NativeStructureGenerationPolicy.resolve(engine, structureKey, underground).status() + == NativeStructureGenerationStatus.REPLACED_BY_IRIS; + } + + @Override + public int surfaceHeight(int blockX, int blockZ) { + return Engine.hostHeight(engine, blockX, blockZ, true) + engine.getMinHeight(); + } + + @Override + public void record(NativeStructureStartPlan plan, StructureFingerprint fingerprint) { + NativeStructureOwnershipStore.record(engine, NativeStructureOwnershipRecord.capture(fingerprint, plan)); + } + + @Override + public void discard(String structureKey, int chunkX, int chunkZ) { + NativeStructureOwnershipStore.discard(engine, structureKey, chunkX, chunkZ); + } + + @Override + public void duplicate(String structureKey) { + IrisLogging.warn("Ignoring duplicate native structure placements for '" + structureKey + + "'; the first deterministic candidate owns each chunk start"); + } + @Override + public NativeStructureStartPlan matchingPlan(String structureKey, int chunkX, int chunkZ) { + return NativeStructurePlacementPlanner.matchingPlan(engine, structureKey, chunkX, chunkZ); + } + + @Override + public NativeStructureOwnershipRecord findPersisted(String structureKey, int chunkX, int chunkZ) { + return NativeStructureOwnershipStore.findPersisted(engine, structureKey, chunkX, chunkZ); + } + + @Override + public NativeStructureOwnershipRecord capture(StructureFingerprint fingerprint, NativeStructureStartPlan plan) { + return NativeStructureOwnershipRecord.capture(fingerprint, plan); + } + + @Override + public void record(NativeStructureOwnershipRecord ownership) { + NativeStructureOwnershipStore.record(engine, ownership); + } + + @Override + public GenerationHistoryRuntimeRouter.CoordinateScope openOriginScope(int chunkX, int chunkZ) { + if (!(engine instanceof IrisEngine irisEngine)) { + return null; + } + try { + return irisEngine.openGenerationHistoryCoordinateScope(chunkX << 4, chunkZ << 4); + } catch (IOException failure) { + throw new IllegalStateException("Unable to resolve native structure references for origin " + + chunkX + "," + chunkZ + " through generation history.", failure); + } + } + + @Override + public IrisNativeStructureDecision resolve(String structureKey, boolean underground) { + return NativeStructureGenerationPolicy.resolve(engine, structureKey, underground); + } + + @Override + public void invalidated(String structureKey) { + if (WARNED_POLICY_INVALIDATIONS.add(structureKey)) { + IrisLogging.warn("Invalidating persisted natural structure start '" + + structureKey + "' because the current Iris dimension policy disables it"); + } + } + +} diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructureVolumePolicy.java b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructureVolumePolicy.java new file mode 100644 index 000000000..e1e8610cd --- /dev/null +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/IrisStructureVolumePolicy.java @@ -0,0 +1,43 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.structure.StructureVolumePolicy; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public enum IrisStructureVolumePolicy implements StructureVolumePolicy { + INSTANCE; + + private static final Set WARNED_RESOLUTION_FAILURES = ConcurrentHashMap.newKeySet(); + + @Override + public List plansAt(Engine engine, int chunkX, int chunkZ) { + return NativeStructurePlacementPlanner.plansAt(engine, chunkX, chunkZ); + } + + @Override + public IrisNativeStructureDecision decisionFor(Engine engine, NativeStructureStartPlan plan) { + return NativeStructurePlacementPlanner.decisionFor(plan); + } + + @Override + public IrisNativeStructureDecision resolve(Engine engine, String structureKey, boolean underground) { + return NativeStructureGenerationPolicy.resolve(engine, structureKey, underground); + } + + @Override + public int surfaceHeight(Engine engine, int blockX, int blockZ) { + return Engine.hostHeight(engine, blockX, blockZ, true) + engine.getMinHeight(); + } + + @Override + public void warn(String structureKey, Throwable error) { + if (WARNED_RESOLUTION_FAILURES.add(structureKey)) { + IrisLogging.reportError("Native structure volume resolution failed for '" + structureKey + + "'; objects will not be vetoed against it.", error); + } + } +} diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScale.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScale.java deleted file mode 100644 index e29a9134f..000000000 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScale.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.structure.nativegen; - -public record NativeStructureFrequencyScale(float frequency, int spacing) { - public static NativeStructureFrequencyScale randomSpread( - float frequency, - int spacing, - int separation, - double multiplier - ) { - if (!Float.isFinite(frequency) || frequency < 0F || frequency > 1F) { - throw new IllegalArgumentException("Native structure frequency must be between 0 and 1"); - } - if (spacing <= separation) { - throw new IllegalArgumentException("Native structure spacing must exceed separation"); - } - requireMultiplier(multiplier); - if (frequency == 0F || multiplier == 1D) { - return new NativeStructureFrequencyScale(frequency, spacing); - } - - double requestedFrequency = frequency * multiplier; - float scaledFrequency = (float) Math.min(1D, requestedFrequency); - double remainingDensity = requestedFrequency / scaledFrequency; - int scaledSpacing = spacing; - if (remainingDensity > 1D) { - int requestedSpacing = (int) Math.round(spacing / Math.sqrt(remainingDensity)); - scaledSpacing = Math.max(separation + 1, requestedSpacing); - } - return new NativeStructureFrequencyScale(scaledFrequency, scaledSpacing); - } - - public static float probability(float frequency, double multiplier) { - if (!Float.isFinite(frequency) || frequency < 0F || frequency > 1F) { - throw new IllegalArgumentException("Native structure frequency must be between 0 and 1"); - } - requireMultiplier(multiplier); - return (float) Math.min(1D, frequency * multiplier); - } - - private static void requireMultiplier(double multiplier) { - if (!Double.isFinite(multiplier) || multiplier < 0.01D || multiplier > 16D) { - throw new IllegalArgumentException("Native structure frequency multiplier must be between 0.01 and 16"); - } - } -} diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecord.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecord.java index a32937fe6..f112fffb5 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecord.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecord.java @@ -1,6 +1,11 @@ package art.arcane.iris.structure.nativegen; import art.arcane.iris.structure.placement.IrisStructureStiltSettings; +import art.arcane.iris.structure.placement.StructurePlacementGrid; +import art.arcane.volmlib.nativelib.terrain.structure.StructureFingerprint; +import art.arcane.volmlib.nativelib.terrain.structure.StructureOwnershipRecordView; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferenceBounds; +import art.arcane.volmlib.nativelib.terrain.structure.StructureTerrainSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; import com.google.gson.Gson; @@ -31,9 +36,8 @@ public record NativeStructureOwnershipRecord( int referenceMaxChunkZ, String contentFingerprint, DecisionSnapshot decision -) { +) implements StructureOwnershipRecordView { public static final int CURRENT_SCHEMA = 2; - public static final int MAX_REFERENCE_DISTANCE_CHUNKS = 8; private static final int MAX_KEY_BYTES = 512; private static final int MAX_FINGERPRINT_BYTES = 128; @@ -70,6 +74,21 @@ public record NativeStructureOwnershipRecord( decision = Objects.requireNonNull(decision, "Native structure decision snapshot must not be null"); } + public static NativeStructureOwnershipRecord capture( + StructureFingerprint fingerprint, NativeStructureStartPlan plan) { + Objects.requireNonNull(fingerprint, "Native structure fingerprint must not be null"); + Objects.requireNonNull(plan, "Native structure start plan must not be null"); + return create( + fingerprint.structureKey(), fingerprint.originChunkX(), fingerprint.originChunkZ(), + StructurePlacementGrid.placementIdentity(plan.placement()), plan.baseY(), + fingerprint.contentMinX(), fingerprint.contentMinY(), fingerprint.contentMinZ(), + fingerprint.contentMaxX(), fingerprint.contentMaxY(), fingerprint.contentMaxZ(), + fingerprint.locatorY(), + fingerprint.referenceMinChunkX(), fingerprint.referenceMaxChunkX(), + fingerprint.referenceMinChunkZ(), fingerprint.referenceMaxChunkZ(), + fingerprint.contentFingerprint(), NativeStructurePlacementPlanner.decisionFor(plan)); + } + public static NativeStructureOwnershipRecord create(String structureKey, int originChunkX, int originChunkZ, @@ -111,14 +130,14 @@ public record NativeStructureOwnershipRecord( ); } - public boolean covers(int chunkX, int chunkZ) { - return chunkX >= referenceMinChunkX && chunkX <= referenceMaxChunkX - && chunkZ >= referenceMinChunkZ && chunkZ <= referenceMaxChunkZ; + @Override + public StructureTerrainSettings terrain() { + return restoredDecision().terrain(); } + @Override public NativeStructureOwnershipRecord withReferenceEnvelope( - int referenceMinChunkX, int referenceMaxChunkX, - int referenceMinChunkZ, int referenceMaxChunkZ) { + StructureReferenceBounds bounds) { return new NativeStructureOwnershipRecord( schema, structureKey, @@ -133,10 +152,10 @@ public record NativeStructureOwnershipRecord( contentMaxY, contentMaxZ, locatorY, - referenceMinChunkX, - referenceMaxChunkX, - referenceMinChunkZ, - referenceMaxChunkZ, + bounds.minChunkX(), + bounds.maxChunkX(), + bounds.minChunkZ(), + bounds.maxChunkZ(), contentFingerprint, decision ); diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureStartPlan.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureStartPlan.java index 49f0b0505..36aef8651 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureStartPlan.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureStartPlan.java @@ -1,6 +1,13 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.StructureTerrainSettings; + +import art.arcane.volmlib.nativelib.terrain.structure.JigsawSettings; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureStartPlan; + import art.arcane.iris.structure.placement.IrisStructurePlacement; +import art.arcane.iris.structure.placement.StructurePlacementGrid; public record NativeStructureStartPlan( IrisStructurePlacement placement, @@ -8,5 +15,34 @@ public record NativeStructureStartPlan( int chunkX, int chunkZ, int baseY -) { +) implements StructureStartPlan { + @Override + public String structureKey() { + return source.getStructure(); + } + + @Override + public JigsawSettings jigsaw() { + return source.getJigsaw(); + } + + @Override + public boolean underground() { + return placement.isUnderground(); + } + + @Override + public boolean replacesSource() { + return placement.getNativeSuppression() == NativeStructureSuppression.REPLACE_SOURCE; + } + + @Override + public StructureTerrainSettings terrain() { + return placement.resolvedTerrain(); + } + @Override + public long placementIdentity() { + return StructurePlacementGrid.placementIdentity(placement); + } + } diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolume.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolume.java deleted file mode 100644 index 560b0b7e4..000000000 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolume.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.structure.nativegen; - -import art.arcane.volmlib.util.collection.KList; - -/** - * World-space axis-aligned bounds of one native structure piece. Volumes are resolved from seed, registry and - * pack policy alone so the same query answers identically regardless of which chunks exist. - */ -public record NativeStructureVolume( - String structure, - int minX, - int minY, - int minZ, - int maxX, - int maxY, - int maxZ -) { - // Shared empty sentinel handed to every engine and memoized by the volume caches; a silent - // add()/clear() here would corrupt them all globally, so mutation fails loudly instead. - public static final KList NONE = new KList<>() { - @Override - public boolean add(NativeStructureVolume volume) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public void add(int index, NativeStructureVolume volume) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public boolean addAll(java.util.Collection volumes) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public boolean addAll(int index, java.util.Collection volumes) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public void clear() { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public NativeStructureVolume remove(int index) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - - @Override - public boolean remove(Object volume) { - throw new UnsupportedOperationException("NativeStructureVolume.NONE is immutable"); - } - }; - - public static NativeStructureVolume of(String structure, int aX, int aY, int aZ, int bX, int bY, int bZ) { - return new NativeStructureVolume( - structure, - Math.min(aX, bX), - Math.min(aY, bY), - Math.min(aZ, bZ), - Math.max(aX, bX), - Math.max(aY, bY), - Math.max(aZ, bZ)); - } - - public boolean intersectsRect(int rectMinX, int rectMinZ, int rectMaxX, int rectMaxZ) { - return maxX >= rectMinX && minX <= rectMaxX && maxZ >= rectMinZ && minZ <= rectMaxZ; - } - - public boolean intersects(int boxMinX, int boxMinY, int boxMinZ, int boxMaxX, int boxMaxY, int boxMaxZ) { - return maxX >= boxMinX && minX <= boxMaxX - && maxY >= boxMinY && minY <= boxMaxY - && maxZ >= boxMinZ && minZ <= boxMaxZ; - } - - public boolean contains(int x, int y, int z) { - return x >= minX && x <= maxX && y >= minY && y <= maxY && z >= minZ && z <= maxZ; - } - - public boolean containsWithin(int x, int y, int z, int margin) { - return x >= minX - margin && x <= maxX + margin - && y >= minY - margin && y <= maxY + margin - && z >= minZ - margin && z <= maxZ + margin; - } -} diff --git a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVolumeIndex.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndex.java similarity index 60% rename from adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVolumeIndex.java rename to core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndex.java index bb1bfb14e..9837eb2bf 100644 --- a/adapters/minecraft-common/src/main/java/art/arcane/iris/nativegen/NativeStructureVolumeIndex.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeIndex.java @@ -1,41 +1,20 @@ -package art.arcane.iris.nativegen; +package art.arcane.iris.structure.nativegen; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureOwnershipRecordView; + +import art.arcane.volmlib.nativelib.terrain.NativeBuildFutures; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureGenerationPolicy; -import art.arcane.iris.structure.nativegen.NativeStructureOwnershipRecord; -import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; -import art.arcane.iris.structure.nativegen.NativeStructureStartPlan; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; -import art.arcane.iris.structure.nativegen.IrisNativeStructureDecision; -import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.Registries; -import net.minecraft.resources.Identifier; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.biome.BiomeSource; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; -import net.minecraft.world.level.levelgen.structure.BoundingBox; -import net.minecraft.world.level.levelgen.structure.Structure; -import net.minecraft.world.level.levelgen.structure.StructurePiece; -import net.minecraft.world.level.levelgen.structure.StructureSet; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; import java.io.IOException; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -43,7 +22,6 @@ import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.IntBinaryOperator; import java.util.function.IntConsumer; import java.util.function.Supplier; @@ -52,20 +30,18 @@ import java.util.function.Supplier; * *

The answer is derived from the seed, the registries and this pack's structure policy only: candidate origins * come from the placement grid and from vanilla structure placements, and each candidate is assembled through the - * same {@link NativeStructureFactory} path chunk generation uses. Nothing here reads chunk state, ownership records + * same native structure factory path chunk generation uses. Nothing here reads chunk state, ownership records * or generation progress, so the same query answers identically no matter which chunks already exist. */ public final class NativeStructureVolumeIndex { - private static final int ORIGIN_REACH_CHUNKS = NativeStructureOwnershipRecord.MAX_REFERENCE_DISTANCE_CHUNKS; + private static final int ORIGIN_REACH_CHUNKS = StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS; private static final int ORIGIN_WINDOW_DIAMETER = ORIGIN_REACH_CHUNKS * 2 + 1; private static final int ORIGIN_WINDOW_STRIPE_COUNT = Long.SIZE; private static final int MAX_CACHED_ORIGIN_CHUNKS = 16_384; private static final int MAX_CACHED_QUERY_CHUNKS = 4_096; private static final Map INDEXES = Collections.synchronizedMap(new WeakHashMap<>()); - private static final Set WARNED_RESOLUTION_FAILURES = ConcurrentHashMap.newKeySet(); - private final Context context; private final OriginResolver origins; private final Map> originCache = lru(MAX_CACHED_ORIGIN_CHUNKS); private final Map> queryCache = lru(MAX_CACHED_QUERY_CHUNKS); @@ -76,19 +52,13 @@ public final class NativeStructureVolumeIndex { private final ReentrantLock[] originWindowStripes = createOriginWindowStripes(); private final IntConsumer retirementListener = this::evictRuntime; - private NativeStructureVolumeIndex(Context context) { - this.context = Objects.requireNonNull(context, "Native structure volume context must not be null"); - this.origins = this::buildOriginVolumes; - } - private NativeStructureVolumeIndex(OriginResolver origins) { - this.context = null; this.origins = Objects.requireNonNull(origins, "Native structure origin resolver must not be null"); } - public static void install(Engine engine, Context context) { + public static void install(Engine engine, OriginResolver origins) { Engine requiredEngine = Objects.requireNonNull(engine, "Native structure volume index requires an engine"); - NativeStructureVolumeIndex installed = new NativeStructureVolumeIndex(context); + NativeStructureVolumeIndex installed = new NativeStructureVolumeIndex(origins); synchronized (INDEXES) { NativeStructureVolumeIndex previous = INDEXES.put(requiredEngine, installed); if (previous != null) { @@ -138,9 +108,7 @@ public final class NativeStructureVolumeIndex { } private NativeStructureVolumeIndex fresh() { - return context == null - ? new NativeStructureVolumeIndex(origins) - : new NativeStructureVolumeIndex(context); + return new NativeStructureVolumeIndex(origins); } KList resolve(Engine engine, int minX, int minZ, int maxX, int maxZ) { @@ -266,168 +234,11 @@ public final class NativeStructureVolumeIndex { } } - private KList buildOriginVolumes(Engine engine, int chunkX, int chunkZ) { - ChunkGeneratorStructureState state = context.structureState().get(); - ChunkGenerator generator = context.generator().get(); - BiomeSource biomeSource = context.biomeSource().get(); - if (state == null || generator == null || biomeSource == null) { - return NativeStructureVolume.NONE; - } - Registry registry = context.registryAccess().lookupOrThrow(Registries.STRUCTURE); - Bindings bindings = new Bindings(state, generator, biomeSource); - KList volumes = appendPlannedVolumes(engine, bindings, registry, chunkX, chunkZ, null); - volumes = appendVanillaVolumes(engine, bindings, registry, chunkX, chunkZ, volumes); - return volumes == null ? NativeStructureVolume.NONE : volumes; - } - - private KList appendPlannedVolumes(Engine engine, Bindings bindings, - Registry registry, int chunkX, int chunkZ, - KList volumes) { - KList plans; - try { - plans = NativeStructurePlacementPlanner.plansAt(engine, chunkX, chunkZ); - } catch (RuntimeException | Error error) { - warnOnce("iris-placements", error); - return volumes; - } - - KList target = volumes; - for (NativeStructureStartPlan plan : plans) { - IrisNativeStructureDecision decision = NativeStructurePlacementPlanner.decisionFor(plan); - if (!decision.generate()) { - continue; - } - Identifier identifier = Identifier.tryParse(plan.source().getStructure()); - if (identifier == null) { - continue; - } - Structure structure = registry.getValue(identifier); - if (structure == null) { - continue; - } - String structureId = identifier.toString(); - try { - StructureStart generated = NativeStructureFactory.generate( - generationContext(engine, bindings), registry.wrapAsHolder(structure), plan, 0); - target = appendPieces(target, structureId, generated); - } catch (RuntimeException | Error error) { - warnOnce(structureId, error); - } - } - return target; - } - - private KList appendVanillaVolumes(Engine engine, Bindings bindings, - Registry registry, int chunkX, int chunkZ, - KList volumes) { - ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); - IntBinaryOperator surfaceHeight = surfaceHeight(engine); - int worldMinY = context.heightAccessor().getMinY(); - int worldMaxYExclusive = worldMinY + context.heightAccessor().getHeight(); - KList target = volumes; - for (Holder setHolder : bindings.state().possibleStructureSets()) { - StructureSet set = setHolder.value(); - if (!set.placement().isStructureChunk(bindings.state(), chunkX, chunkZ)) { - continue; - } - for (StructureSet.StructureSelectionEntry entry : set.structures()) { - Holder holder = entry.structure(); - Structure structure = holder.value(); - Identifier identifier = registry.getKey(structure); - if (identifier == null) { - continue; - } - String structureId = identifier.toString(); - boolean undergroundStep = NativeStructureVegetationClearer.isUndergroundStep(structure.step()); - try { - IrisNativeStructureDecision decision = NativeStructureGenerationPolicy.resolve( - engine, structureId, undergroundStep); - if (!decision.generate()) { - continue; - } - StructureStart generated = structure.generate( - holder, - context.levelKey(), - context.registryAccess(), - bindings.generator(), - bindings.biomeSource(), - bindings.state().randomState(), - context.templateManager(), - bindings.state().getLevelSeed(), - chunkPos, - 0, - context.heightAccessor(), - structure.biomes()::contains - ); - if (generated == null || !generated.isValid()) { - continue; - } - NativeStructureVerticalPlacer.applyVerticalPlacement( - generated, - structureId, - decision.yShift(), - bindings.generator().getSeaLevel(), - worldMinY, - worldMaxYExclusive, - undergroundStep, - decision.preserveSourceY(), - decision.yBand(), - surfaceHeight); - target = appendPieces(target, structureId, generated); - } catch (RuntimeException | Error error) { - warnOnce(structureId, error); - } - } - } - return target; - } - - private NativeStructureFactory.GenerationContext generationContext(Engine engine, Bindings bindings) { - return new NativeStructureFactory.GenerationContext( - context.registryAccess(), - bindings.generator(), - bindings.biomeSource(), - bindings.state().randomState(), - context.templateManager(), - bindings.state().getLevelSeed(), - context.levelKey(), - context.heightAccessor(), - biome -> true, - bindings.generator().getSeaLevel(), - surfaceHeight(engine) - ); - } - - private record Bindings(ChunkGeneratorStructureState state, ChunkGenerator generator, BiomeSource biomeSource) { - } - @FunctionalInterface - interface OriginResolver { + public interface OriginResolver { KList volumesAt(Engine engine, int chunkX, int chunkZ); } - private static IntBinaryOperator surfaceHeight(Engine engine) { - return (x, z) -> Engine.hostHeight(engine, x, z, true) + engine.getMinHeight(); - } - - static KList appendPieces(KList volumes, - String structureId, StructureStart start) { - if (start == null || !start.isValid()) { - return volumes; - } - KList target = volumes; - for (StructurePiece piece : start.getPieces()) { - BoundingBox bounds = piece.getBoundingBox(); - if (target == null) { - target = new KList<>(); - } - target.add(new NativeStructureVolume(structureId, - bounds.minX(), bounds.minY(), bounds.minZ(), - bounds.maxX(), bounds.maxY(), bounds.maxZ())); - } - return target; - } - void evictRuntime(int runtimeId) { synchronized (originCache) { originBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId); @@ -486,13 +297,6 @@ public final class NativeStructureVolumeIndex { } } - private static void warnOnce(String structureId, Throwable error) { - if (WARNED_RESOLUTION_FAILURES.add(structureId)) { - IrisLogging.reportError("Native structure volume resolution failed for '" + structureId - + "'; objects will not be vetoed against it.", error); - } - } - private static long chunkKey(int chunkX, int chunkZ) { return ((long) chunkX << 32) ^ (chunkZ & 0xffffffffL); } @@ -624,18 +428,4 @@ public final class NativeStructureVolumeIndex { private record RuntimeChunkKey(int runtimeId, long chunkKey) { } - /** - * The generator, biome source and structure state arrive as suppliers so an installed index never holds the - * level (and through it the engine) strongly. That keeps the weakly keyed registry collectable on world unload. - */ - public record Context( - RegistryAccess registryAccess, - StructureTemplateManager templateManager, - ResourceKey levelKey, - LevelHeightAccessor heightAccessor, - Supplier generator, - Supplier biomeSource, - Supplier structureState - ) { - } } diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemo.java b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemo.java index 4d31aeb97..e9b762a78 100644 --- a/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemo.java +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemo.java @@ -18,6 +18,8 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EnginePlatformHooks; diff --git a/core/src/main/java/art/arcane/iris/structure/nativegen/StructureLocateSearch.java b/core/src/main/java/art/arcane/iris/structure/nativegen/StructureLocateSearch.java new file mode 100644 index 000000000..d308ae06f --- /dev/null +++ b/core/src/main/java/art/arcane/iris/structure/nativegen/StructureLocateSearch.java @@ -0,0 +1,64 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateCandidate; +import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateSearchAccess; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.structure.placement.IrisStructureLocator; +import art.arcane.volmlib.nativelib.terrain.StructureLocateProbe; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +public final class StructureLocateSearch implements StructureLocateSearchAccess { + + private final Options options; + private final Set rejectedChunks = new HashSet<>(); + + public StructureLocateSearch(Options options) { + this.options = Objects.requireNonNull(options, "Structure locate options must not be null"); + } + + public IrisStructureLocator.LocateResult predict() { + if (rejectedChunks.size() >= MAX_SELECTED_CANDIDATE_RETRIES) { + return new IrisStructureLocator.LocateResult( + IrisStructureLocator.LocateStatus.SEARCH_LIMIT_REACHED, 0, 0, 0); + } + return IrisStructureLocator.locateInPlacementRings( + options.engine(), options.structureKey(), options.blockX(), options.blockZ(), options.radius(), + (chunkX, chunkZ) -> !rejectedChunks.contains(chunkKey(chunkX, chunkZ)) + && options.probe().accepts(chunkX, chunkZ)); + } + + public VerifiedStart verify(StructureLocateCandidate result) { + S start = options.probe().verifySelected(result.originX() >> 4, result.originZ() >> 4); + if (start == null) { + return null; + } + NativeStructureOwnershipRecord ownership = options.ownership().apply(start); + return ownership == null ? null : new VerifiedStart<>(start, ownership); + } + + public void reject(StructureLocateCandidate result) { + rejectedChunks.add(chunkKey(result.originX() >> 4, result.originZ() >> 4)); + } + + public void reference(StructureLocateSearchAccess.Verified verified) { + options.probe().reference(verified.start()); + } + + private static long chunkKey(int chunkX, int chunkZ) { + return ((long) chunkZ << 32) | (chunkX & 0xffffffffL); + } + + public record Options(Engine engine, String structureKey, int blockX, int blockZ, int radius, + StructureLocateProbe probe, + Function ownership) { + } + + public record VerifiedStart(S start, NativeStructureOwnershipRecord ownership) + implements StructureLocateSearchAccess.Verified { + } +} diff --git a/core/src/main/java/art/arcane/iris/structure/object/DecayControlPlacer.java b/core/src/main/java/art/arcane/iris/structure/object/DecayControlPlacer.java index ea99428f2..102b3e3df 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/DecayControlPlacer.java +++ b/core/src/main/java/art/arcane/iris/structure/object/DecayControlPlacer.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.jetbrains.annotations.Nullable; public class DecayControlPlacer implements IObjectPlacer { @@ -43,12 +43,12 @@ public class DecayControlPlacer implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState d) { + public void set(int x, int y, int z, NativeBlockState d) { delegate.set(x, y, z, d); } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return delegate.get(x, y, z); } diff --git a/core/src/main/java/art/arcane/iris/structure/object/HeightmapObjectPlacer.java b/core/src/main/java/art/arcane/iris/structure/object/HeightmapObjectPlacer.java index b391f169b..e4ad06f65 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/HeightmapObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/structure/object/HeightmapObjectPlacer.java @@ -21,7 +21,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.RNG; public class HeightmapObjectPlacer implements IObjectPlacer { @@ -43,11 +43,11 @@ public class HeightmapObjectPlacer implements IObjectPlacer { return (int) Math.round(config.getHeightmap().getNoise(this.s, param1Int1, param1Int2, data)); } - public void set(int param1Int1, int param1Int2, int param1Int3, PlatformBlockState param1BlockData) { + public void set(int param1Int1, int param1Int2, int param1Int3, NativeBlockState param1BlockData) { oplacer.set(param1Int1, param1Int2, param1Int3, param1BlockData); } - public PlatformBlockState get(int param1Int1, int param1Int2, int param1Int3) { + public NativeBlockState get(int param1Int1, int param1Int2, int param1Int3) { return oplacer.get(param1Int1, param1Int2, param1Int3); } diff --git a/core/src/main/java/art/arcane/iris/structure/object/IObjectLoot.java b/core/src/main/java/art/arcane/iris/structure/object/IObjectLoot.java index 0093c6c7b..11f321c86 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IObjectLoot.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IObjectLoot.java @@ -3,12 +3,12 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; public interface IObjectLoot { KList getFilter(); - KList getFilter(IrisData manager); + KList getFilter(IrisData manager); boolean isExact(); String getName(); int getWeight(); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IObjectPlacer.java b/core/src/main/java/art/arcane/iris/structure/object/IObjectPlacer.java index e2099e1a9..8c5be8cc0 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IObjectPlacer.java @@ -25,7 +25,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.world.history.TerrainBoundarySignature; import java.util.Optional; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.jetbrains.annotations.Nullable; public interface IObjectPlacer { @@ -33,9 +33,9 @@ public interface IObjectPlacer { int getHighest(int x, int z, IrisData data, boolean ignoreFluid); - void set(int x, int y, int z, PlatformBlockState d); + void set(int x, int y, int z, NativeBlockState d); - PlatformBlockState get(int x, int y, int z); + NativeBlockState get(int x, int y, int z); boolean isPreventingDecay(); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IPostBlockAccess.java b/core/src/main/java/art/arcane/iris/structure/object/IPostBlockAccess.java index 7a34a2f90..5abecbc50 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IPostBlockAccess.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IPostBlockAccess.java @@ -20,14 +20,14 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.cave.CaveResult; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import org.bukkit.generator.ChunkGenerator.ChunkData; public interface IPostBlockAccess { - PlatformBlockState getPostBlock(int x, int y, int z, int currentPostX, int currentPostZ, ChunkData currentData); + NativeBlockState getPostBlock(int x, int y, int z, int currentPostX, int currentPostZ, ChunkData currentData); - void setPostBlock(int x, int y, int z, PlatformBlockState d, int currentPostX, int currentPostZ, ChunkData currentData); + void setPostBlock(int x, int y, int z, NativeBlockState d, int currentPostX, int currentPostZ, ChunkData currentData); int highestTerrainOrFluidBlock(int x, int z); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObject.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObject.java index 9055f01db..79bd3550d 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObject.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObject.java @@ -28,13 +28,13 @@ import art.arcane.iris.pack.validation.CompatStatus; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.loading.IrisRegistrant; import art.arcane.iris.integration.ExternalDataSVC; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.iris.generation.block.VectorMap; @@ -82,11 +82,11 @@ public class IrisObject extends IrisRegistrant { private record Bound( IrisPlatform platform, - PlatformBlockState air, - PlatformBlockState stone, - PlatformBlockState vair, - PlatformBlockState vairDebug, - PlatformBlockState[] snowLayers) { + NativeBlockState air, + NativeBlockState stone, + NativeBlockState vair, + NativeBlockState vairDebug, + NativeBlockState[] snowLayers) { } private static Bound bound() { @@ -102,28 +102,28 @@ public class IrisObject extends IrisRegistrant { B.getState("STONE"), B.getState("VOID_AIR"), B.getState("COBWEB"), - new PlatformBlockState[]{B.getState("minecraft:snow[layers=1]"), B.getState("minecraft:snow[layers=2]"), B.getState("minecraft:snow[layers=3]"), B.getState("minecraft:snow[layers=4]"), B.getState("minecraft:snow[layers=5]"), B.getState("minecraft:snow[layers=6]"), B.getState("minecraft:snow[layers=7]"), B.getState("minecraft:snow[layers=8]")}); + new NativeBlockState[]{B.getState("minecraft:snow[layers=1]"), B.getState("minecraft:snow[layers=2]"), B.getState("minecraft:snow[layers=3]"), B.getState("minecraft:snow[layers=4]"), B.getState("minecraft:snow[layers=5]"), B.getState("minecraft:snow[layers=6]"), B.getState("minecraft:snow[layers=7]"), B.getState("minecraft:snow[layers=8]")}); bound = resolved; return resolved; } - static PlatformBlockState air() { + static NativeBlockState air() { return bound().air(); } - static PlatformBlockState stone() { + static NativeBlockState stone() { return bound().stone(); } - static PlatformBlockState vair() { + static NativeBlockState vair() { return bound().vair(); } - static PlatformBlockState vairDebug() { + static NativeBlockState vairDebug() { return bound().vairDebug(); } - static PlatformBlockState snowLayer(int layerIndex) { + static NativeBlockState snowLayer(int layerIndex) { return bound().snowLayers()[layerIndex]; } } @@ -137,7 +137,7 @@ public class IrisObject extends IrisRegistrant { transient final AtomicCache> surfaceSupportOffsets = new AtomicCache<>(); transient final AtomicCache floatingFootprint = new AtomicCache<>(); @Getter - VectorMap blocks; + VectorMap blocks; @Getter VectorMap states; @Getter @@ -264,7 +264,7 @@ public class IrisObject extends IrisRegistrant { return new IrisBlockVector(x - center.getX(), y - center.getY(), z - center.getZ()); } - public void setUnsigned(int x, int y, int z, PlatformBlockState block) { + public void setUnsigned(int x, int y, int z, NativeBlockState block) { IrisBlockVector v = getSigned(x, y, z); if (block == null) { @@ -328,7 +328,7 @@ public class IrisObject extends IrisRegistrant { return place(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), placer, config, rng, rdata); } - public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { + public int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { return new IrisObjectPlacementRunner(this).place(x, yv, z, oplacer, config, rng, listener, c, rdata); } @@ -338,8 +338,8 @@ public class IrisObject extends IrisRegistrant { try { int lowestY = Integer.MAX_VALUE; KList offsets = new KList<>(); - for (Map.Entry entry : blocks) { - PlatformBlockState state = entry.getValue(); + for (Map.Entry entry : blocks) { + NativeBlockState state = entry.getValue(); if (state == null || !state.isSolid() || state.isFoliage()) { continue; } @@ -375,7 +375,7 @@ public class IrisObject extends IrisRegistrant { public void place(Location at) { readLock.lock(); try { - for (Map.Entry entry : blocks) { + for (Map.Entry entry : blocks) { IrisBlockVector i = entry.getKey(); Block b = at.clone().add(0, getCenter().getY(), 0).add(i.getX(), i.getY(), i.getZ()).getBlock(); placeBlock(b, (BlockData) Objects.requireNonNull(entry.getValue()).nativeHandle()); @@ -393,7 +393,7 @@ public class IrisObject extends IrisRegistrant { public void placeCenterY(Location at) { readLock.lock(); try { - for (Map.Entry entry : blocks) { + for (Map.Entry entry : blocks) { IrisBlockVector i = entry.getKey(); Block b = at.clone().add(getCenter().getX(), getCenter().getY(), getCenter().getZ()).add(i.getX(), i.getY(), i.getZ()).getBlock(); placeBlock(b, (BlockData) Objects.requireNonNull(entry.getValue()).nativeHandle()); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectIO.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectIO.java index 7fdaf34b3..3878660d9 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectIO.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectIO.java @@ -25,7 +25,7 @@ import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.localization.RuntimeUiMessages; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; @@ -139,7 +139,7 @@ public final class IrisObjectIO { for (int i = 0; i < s; i++) { IrisBlockVector pos = new IrisBlockVector(din.readShort(), din.readShort(), din.readShort()); - PlatformBlockState data = resolvePaletteState(self, din.readUTF()); + NativeBlockState data = resolvePaletteState(self, din.readUTF()); if (isExcludedObjectBlock(data)) { continue; } @@ -181,7 +181,7 @@ public final class IrisObjectIO { // Resolve the palette once: B.getState per BLOCK was a registry lookup times the // block count (tens of thousands) instead of times the palette size (hundreds). - PlatformBlockState[] resolved = new PlatformBlockState[palette.size()]; + NativeBlockState[] resolved = new NativeBlockState[palette.size()]; for (i = 0; i < resolved.length; i++) { resolved[i] = resolvePaletteState(self, palette.get(i)); } @@ -190,7 +190,7 @@ public final class IrisObjectIO { for (i = 0; i < s; i++) { IrisBlockVector pos = new IrisBlockVector(din.readShort(), din.readShort(), din.readShort()); - PlatformBlockState data = resolved[din.readShort()]; + NativeBlockState data = resolved[din.readShort()]; if (isExcludedObjectBlock(data)) { continue; } @@ -233,8 +233,8 @@ public final class IrisObjectIO { * before the plain lookup, so a declared fallback actually reaches the world instead of becoming air. Present * keys take exactly one registry lookup, as before. */ - private static PlatformBlockState resolvePaletteState(IrisObject self, String key) { - PlatformBlockState direct = B.getStateOrNull(key, false); + private static NativeBlockState resolvePaletteState(IrisObject self, String key) { + NativeBlockState direct = B.getStateOrNull(key, false); if (direct != null) { return direct; @@ -247,7 +247,7 @@ public final class IrisObjectIO { ContentGate gate = data.getContentGate(); if (gate != null && gate.ready()) { - PlatformBlockState viaGate = gate.resolveBlockOrPlaceholder(key); + NativeBlockState viaGate = gate.resolveBlockOrPlaceholder(key); if (viaGate != null) { return viaGate; @@ -261,7 +261,7 @@ public final class IrisObjectIO { return B.getState(key); } - private static boolean isExcludedObjectBlock(PlatformBlockState data) { + private static boolean isExcludedObjectBlock(NativeBlockState data) { if (data == null) { return false; } @@ -291,7 +291,7 @@ public final class IrisObjectIO { private static Palette buildPalette(IrisObject self) { Palette palette = new Palette(); - for (PlatformBlockState i : self.blocks.values()) { + for (NativeBlockState i : self.blocks.values()) { palette.add(i.key()); } return palette; @@ -329,7 +329,7 @@ public final class IrisObjectIO { dos.writeInt(self.blocks.size()); - for (Map.Entry entry : self.blocks) { + for (Map.Entry entry : self.blocks) { writeBlock(dos, palette, entry); } @@ -375,7 +375,7 @@ public final class IrisObjectIO { dos.writeInt(self.blocks.size()); - for (Map.Entry entry : self.blocks) { + for (Map.Entry entry : self.blocks) { writeBlock(dos, palette, entry); ++c; } @@ -424,7 +424,7 @@ public final class IrisObjectIO { } private static void writeBlock(DataOutputStream output, Palette palette, - Map.Entry entry) throws IOException { + Map.Entry entry) throws IOException { IrisBlockVector position = entry.getKey(); output.writeShort(position.getBlockX()); output.writeShort(position.getBlockY()); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectLoot.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectLoot.java index b1de7f08f..8268ae393 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectLoot.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectLoot.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.world.loot.IrisLootTable; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -34,7 +34,7 @@ import art.arcane.volmlib.util.collection.KList; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; @Snippet("object-loot") @@ -44,7 +44,7 @@ import lombok.experimental.Accessors; @Description("Represents loot within this object") @Data public class IrisObjectLoot implements IObjectLoot { - private final transient AtomicCache> filterCache = new AtomicCache<>(); + private final transient AtomicCache> filterCache = new AtomicCache<>(); @ArrayType(min = 1, type = IrisBlockData.class) @Description("The list of blocks this loot table should apply to") private KList filter = new KList<>(); @@ -58,13 +58,13 @@ public class IrisObjectLoot implements IObjectLoot { @MinNumber(1) private int weight = 1; - public KList getFilter(IrisData rdata) { + public KList getFilter(IrisData rdata) { return filterCache.aquire(() -> { - KList b = new KList<>(); + KList b = new KList<>(); for (IrisBlockData i : filter) { - PlatformBlockState bx = i.getBlockData(rdata); + NativeBlockState bx = i.getBlockData(rdata); if (bx != null) { b.add(bx); @@ -75,8 +75,8 @@ public class IrisObjectLoot implements IObjectLoot { }); } - public boolean matchesFilter(IrisData manager, PlatformBlockState data) { - for (PlatformBlockState filterState : getFilter(manager)) { + public boolean matchesFilter(IrisData manager, NativeBlockState data) { + for (NativeBlockState filterState : getFilter(manager)) { if (B.matches(filterState, data)) return true; } return false; diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectMarker.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectMarker.java index 6896f523c..03235e0e7 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectMarker.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectMarker.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.world.entity.IrisMarker; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; @@ -34,7 +34,7 @@ import art.arcane.volmlib.util.collection.KList; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; @Snippet("object-marker") @@ -44,7 +44,7 @@ import lombok.experimental.Accessors; @Description("Find blocks to mark") @Data public class IrisObjectMarker { - private final transient AtomicCache> findData = new AtomicCache<>(); + private final transient AtomicCache> findData = new AtomicCache<>(); @ArrayType(min = 1, type = IrisBlockData.class) @Required @Description("Find block types to mark") @@ -60,13 +60,13 @@ public class IrisObjectMarker { @Description("The marker to add") private String marker; - public KList getMark(IrisData rdata) { + public KList getMark(IrisData rdata) { return findData.aquire(() -> { - KList b = new KList<>(); + KList b = new KList<>(); for (IrisBlockData i : mark) { - PlatformBlockState bx = i.getBlockDataOrPlaceholder(rdata); + NativeBlockState bx = i.getBlockDataOrPlaceholder(rdata); if (bx != null) { b.add(bx); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacement.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacement.java index 2c011fd75..1acb30fbd 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacement.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacement.java @@ -42,7 +42,7 @@ import art.arcane.iris.pack.validation.CompatStatus; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.validation.PackCompatReport; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.cache.LazyBoundedCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.LootResolver; @@ -54,7 +54,7 @@ import art.arcane.iris.pack.schema.annotation.RegistryListResource; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.pack.schema.annotation.Snippet; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.iris.generation.block.DataProvider; @@ -446,7 +446,7 @@ public class IrisObjectPlacement { tc.global.put(table, loot.getWeight()); } else if (!loot.isExact()) //Table is meant to be by type { - for (PlatformBlockState filterState : loot.getFilter(manager)) { + for (NativeBlockState filterState : loot.getFilter(manager)) { BlockData filterData = (BlockData) filterState.nativeHandle(); if (!tc.basic.containsKey(filterData.getMaterial())) { tc.basic.put(filterData.getMaterial(), new WeightedTables()); @@ -456,7 +456,7 @@ public class IrisObjectPlacement { } } else //Filter is exact { - for (PlatformBlockState filterState : loot.getFilter(manager)) { + for (NativeBlockState filterState : loot.getFilter(manager)) { BlockData filterData = (BlockData) filterState.nativeHandle(); if (!tc.exact.containsKey(filterData.getMaterial())) { tc.exact.put(filterData.getMaterial(), new KMap<>()); @@ -489,7 +489,7 @@ public class IrisObjectPlacement { * @param dataManager Iris Data Manager * @return The loot table it should use. */ - public IrisLootTable getTable(PlatformBlockState state, IrisData dataManager, RNG rng) { + public IrisLootTable getTable(NativeBlockState state, IrisData dataManager, RNG rng) { TableCache cache = getCache(dataManager); BlockData data = (BlockData) state.nativeHandle(); if (BukkitBlockResolution.isStorageChest(data)) { diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacementRunner.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacementRunner.java index de75c6bb4..e203e009d 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacementRunner.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectPlacementRunner.java @@ -31,10 +31,10 @@ import art.arcane.iris.world.entity.IrisMarker; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.structure.placement.PlacedObject; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -71,7 +71,7 @@ final class IrisObjectPlacementRunner { this.self = self; } - int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { + int place(int x, int yv, int z, IObjectPlacer oplacer, IrisObjectPlacement config, RNG rng, BiConsumer listener, CarveResult c, IrisData rdata) { Objects.requireNonNull(oplacer, "Object placer is required."); Objects.requireNonNull(config, "Object placement config is required."); Objects.requireNonNull(rng, "Object placement RNG is required."); @@ -453,7 +453,7 @@ final class IrisObjectPlacementRunner { KMap markers = null; try { - VectorMap blocks = self.blocks; + VectorMap blocks = self.blocks; VectorMap states = self.states; // Zero-tile objects are the common case: skip the two Key allocations VectorMap#get needs to prove null. boolean hasStates = !states.isEmpty(); @@ -483,12 +483,12 @@ final class IrisObjectPlacementRunner { break; } - PlatformBlockState data = blocks.get(i); + NativeBlockState data = blocks.get(i); if (data == null) { continue; } - for (PlatformBlockState k : j.getMark(rdata)) { + for (NativeBlockState k : j.getMark(rdata)) { if (max <= 0) { break; } @@ -507,10 +507,10 @@ final class IrisObjectPlacementRunner { } } - VectorMap.Cursor cursor = blocks.cursor(); + VectorMap.Cursor cursor = blocks.cursor(); while (cursor.next()) { IrisBlockVector g = cursor.key(); - PlatformBlockState d; + NativeBlockState d; TileData tile = null; try { @@ -533,7 +533,7 @@ final class IrisObjectPlacementRunner { d = IrisObject.States.vairDebug(); } - PlatformBlockState data = d; + NativeBlockState data = d; IrisBlockVector i = g.clone(); spin.rotate(i); if (ceilingHang) { @@ -559,9 +559,9 @@ final class IrisObjectPlacementRunner { if (hasEdits) { for (IrisObjectReplace j : config.getEdit()) { if (rng.chance(j.getChance())) { - for (PlatformBlockState k : j.getFind(rdata)) { + for (NativeBlockState k : j.getFind(rdata)) { if (j.isExact() ? k.matches(data) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(data))) { - PlatformBlockState newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata); + NativeBlockState newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata); boolean sameMaterial = IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(data)); if (sameMaterial && !(newData.isCustom() || data.isCustom())) @@ -657,7 +657,7 @@ final class IrisObjectPlacementRunner { if (stilting) { self.readLock.lock(); try { - VectorMap blocks = self.blocks; + VectorMap blocks = self.blocks; IrisStiltSettings settings = stiltSettings; double erodeCentroidX = 0; @@ -665,7 +665,7 @@ final class IrisObjectPlacementRunner { double erodeMaxDist = 1; if (eroding) { int centroidCount = 0; - VectorMap.Cursor centroidCursor = blocks.cursor(); + VectorMap.Cursor centroidCursor = blocks.cursor(); while (centroidCursor.next()) { IrisBlockVector rot = centroidCursor.key().clone(); spin.rotate(rot); @@ -673,7 +673,7 @@ final class IrisObjectPlacementRunner { rot.add(translateOffset); } if (rot.getBlockY() == lowest) { - PlatformBlockState bd = centroidCursor.value(); + NativeBlockState bd = centroidCursor.value(); if (bd != null && IrisObjectShaping.shouldStilt(bd, excludedStiltMaterials)) { erodeCentroidX += rot.getX(); erodeCentroidZ += rot.getZ(); @@ -685,7 +685,7 @@ final class IrisObjectPlacementRunner { erodeCentroidX /= centroidCount; erodeCentroidZ /= centroidCount; } - VectorMap.Cursor spreadCursor = blocks.cursor(); + VectorMap.Cursor spreadCursor = blocks.cursor(); while (spreadCursor.next()) { IrisBlockVector rot = spreadCursor.key().clone(); spin.rotate(rot); @@ -693,7 +693,7 @@ final class IrisObjectPlacementRunner { rot.add(translateOffset); } if (rot.getBlockY() == lowest) { - PlatformBlockState bd = spreadCursor.value(); + NativeBlockState bd = spreadCursor.value(); if (bd != null && IrisObjectShaping.shouldStilt(bd, excludedStiltMaterials)) { double dx = rot.getX() - erodeCentroidX; double dz = rot.getZ() - erodeCentroidZ; @@ -706,10 +706,10 @@ final class IrisObjectPlacementRunner { } } - VectorMap.Cursor stiltCursor = blocks.cursor(); + VectorMap.Cursor stiltCursor = blocks.cursor(); while (stiltCursor.next()) { IrisBlockVector g = stiltCursor.key(); - PlatformBlockState sourceData; + NativeBlockState sourceData; try { sourceData = stiltCursor.value(); } catch (Throwable e) { @@ -727,7 +727,7 @@ final class IrisObjectPlacementRunner { continue; } - PlatformBlockState d = sourceData; + NativeBlockState d = sourceData; if (settings != null && settings.getPalette() != null) { d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata); } else { @@ -754,12 +754,12 @@ final class IrisObjectPlacementRunner { if (hasEdits) { for (IrisObjectReplace j : config.getEdit()) { if (rng.chance(j.getChance())) { - for (PlatformBlockState k : j.getFind(rdata)) { + for (NativeBlockState k : j.getFind(rdata)) { if (d == null) { continue; } if (j.isExact() ? k.matches(d) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(d))) { - PlatformBlockState newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata); + NativeBlockState newData = j.getReplace(rng, i.getX() + x, i.getY() + y, i.getZ() + z, rdata); if (IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(d))) { d = BlockDataMergeSupport.merge(d, newData); @@ -863,7 +863,7 @@ final class IrisObjectPlacementRunner { } for (int j = lowest + y; j > lowerBound; j--) { - PlatformBlockState fluidState = placer.get(xx, j, zz); + NativeBlockState fluidState = placer.get(xx, j, zz); if (B.isFluid(fluidState)) { break; } @@ -1071,9 +1071,9 @@ final class IrisObjectPlacementRunner { self.readLock.lock(); try { - VectorMap.Cursor cursor = self.blocks.cursor(); + VectorMap.Cursor cursor = self.blocks.cursor(); while (cursor.next()) { - PlatformBlockState state = cursor.value(); + NativeBlockState state = cursor.value(); if (state == null || isAirBlock(state)) { continue; } @@ -1118,7 +1118,7 @@ final class IrisObjectPlacementRunner { return false; } - private static boolean isAirBlock(PlatformBlockState state) { + private static boolean isAirBlock(NativeBlockState state) { String material = IrisObjectShaping.materialKey(state); return material.equals("minecraft:air") || material.equals("minecraft:cave_air"); } @@ -1183,7 +1183,7 @@ final class IrisObjectPlacementRunner { } targetY = Math.max(worldMin + 1, Math.min(worldMax, targetY)); if (targetY > origY) { - PlatformBlockState fill = complex != null ? complex.getRockStream().get(cx, cz) : null; + NativeBlockState fill = complex != null ? complex.getRockStream().get(cx, cz) : null; if (B.isAir(fill)) { fill = IrisObject.States.stone(); } @@ -1226,7 +1226,7 @@ final class IrisObjectPlacementRunner { return false; } - PlatformBlockState existing = placer.get(x, y, z); + NativeBlockState existing = placer.get(x, y, z); if (existing == null) { return false; } @@ -1234,14 +1234,14 @@ final class IrisObjectPlacementRunner { return B.isWater(existing) || B.isWaterLogged(existing); } - private static PlatformBlockState attachVineFaces(IObjectPlacer placer, PlatformBlockState data, int x, int y, int z) { - PlatformBlockState result = data; + private static NativeBlockState attachVineFaces(IObjectPlacer placer, NativeBlockState data, int x, int y, int z) { + NativeBlockState result = data; for (String face : IrisProceduralBlocks.FACE_PROPERTIES) { if (!IrisProceduralBlocks.hasProperty(data, face)) { continue; } int[] mod = IrisProceduralBlocks.faceOffset(face); - PlatformBlockState facing = placer.get(x + mod[0], y + mod[1], z + mod[2]); + NativeBlockState facing = placer.get(x + mod[0], y + mod[1], z + mod[2]); if (B.isSolid(facing) && !B.isVineBlock(facing)) { result = result.withProperty(face, "true"); } diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectReplace.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectReplace.java index 3b57bbc27..01db39b9a 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectReplace.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectReplace.java @@ -23,7 +23,7 @@ import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; @@ -36,7 +36,7 @@ import art.arcane.volmlib.util.noise.CNG; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; import java.util.LinkedHashMap; @@ -50,8 +50,8 @@ import java.util.Map; @Data public class IrisObjectReplace { private final transient AtomicCache replaceGen = new AtomicCache<>(); - private final transient AtomicCache> findData = new AtomicCache<>(); - private final transient AtomicCache> replaceData = new AtomicCache<>(); + private final transient AtomicCache> findData = new AtomicCache<>(); + private final transient AtomicCache> replaceData = new AtomicCache<>(); @ArrayType(min = 1, type = IrisBlockData.class) @Required @Description("Find this block") @@ -66,13 +66,13 @@ public class IrisObjectReplace { @Description("Modifies the chance the block is replaced") private float chance = 1; - public KList getFind(IrisData rdata) { + public KList getFind(IrisData rdata) { return findData.aquire(() -> { - KList b = new KList<>(); + KList b = new KList<>(); for (IrisBlockData i : find) { - PlatformBlockState bx = i.getBlockDataOrPlaceholder(rdata); + NativeBlockState bx = i.getBlockDataOrPlaceholder(rdata); if (bx != null) { b.add(bx); @@ -83,7 +83,7 @@ public class IrisObjectReplace { }); } - public PlatformBlockState getReplace(RNG seed, double x, double y, double z, IrisData rdata) { + public NativeBlockState getReplace(RNG seed, double x, double y, double z, IrisData rdata) { return getReplace().get(seed, x, y, z, rdata); } @@ -91,7 +91,7 @@ public class IrisObjectReplace { * Key-level mirror of the runtime find match, for the version-content gate: it has to decide whether a palette * key the server does not have is rewritten by this rule, and a missing key has no resolved state to compare. * Non-exact compares the base block; exact requires every property named by the find entry to match, the same - * partial-match contract {@code PlatformBlockState.matches} uses at runtime. + * partial-match contract {@code NativeBlockState.matches} uses at runtime. */ public boolean matchesState(String stateKey) { String state = ContentGate.normalizeState(stateKey); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectRotation.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectRotation.java index f5267894c..6d08b0af8 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectRotation.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectRotation.java @@ -26,7 +26,7 @@ import art.arcane.iris.pack.schema.annotation.Snippet; import art.arcane.iris.pack.validation.MissingBlockState; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KList; @@ -61,7 +61,7 @@ public class IrisObjectRotation { private static volatile StateRotator PLATFORM_ROTATOR = null; public interface StateRotator { - PlatformBlockState rotate(IrisObjectRotation rotation, PlatformBlockState state, int spinx, int spiny, int spinz); + NativeBlockState rotate(IrisObjectRotation rotation, NativeBlockState state, int spinx, int spiny, int spinz); } public static synchronized StateRotator bindPlatformRotator(StateRotator rotator) { @@ -289,7 +289,7 @@ public class IrisObjectRotation { } - public PlatformBlockState rotate(PlatformBlockState state, int spinx, int spiny, int spinz) { + public NativeBlockState rotate(NativeBlockState state, int spinx, int spiny, int spinz) { if (state == null) { return null; } diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectShaping.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectShaping.java index fbacab75e..4f48c1ffc 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectShaping.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectShaping.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; @@ -59,11 +59,11 @@ final class IrisObjectShaping { private static void ensureSmartBoredLocked(IrisObject self) { PrecisionStopwatch p = PrecisionStopwatch.start(); - PlatformBlockState vair = IrisObject.States.vair(); + NativeBlockState vair = IrisObject.States.vair(); int applied = 0; IrisBlockVector max = new IrisBlockVector(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); IrisBlockVector min = new IrisBlockVector(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - VectorMap source = self.blocks; + VectorMap source = self.blocks; if (source.isEmpty()) { IrisLogging.warn("Cannot Smart Bore " + self.getLoadKey() + " because it has 0 blocks in it."); self.smartBored = true; @@ -79,7 +79,7 @@ final class IrisObjectShaping { min.setZ(Math.min(i.getZ(), min.getZ())); } - VectorMap bore = new VectorMap<>(); + VectorMap bore = new VectorMap<>(); // Inline on purpose: the three axis passes read AND write the shared bore map, so the // bored volume must come from a fixed X->Y->Z order on the calling (lock-owning) // thread, not from pool scheduling. This matches what burst-worker callers (studio, @@ -154,12 +154,12 @@ final class IrisObjectShaping { IrisLogging.debug("Smart Bore: " + self.getLoadKey() + " in " + Form.duration(p.getMilliseconds(), 2) + " (" + Form.f(applied) + ")"); } - private static boolean boreContains(VectorMap source, VectorMap bore, IrisBlockVector v) { + private static boolean boreContains(VectorMap source, VectorMap bore, IrisBlockVector v) { return source.containsKey(v) || bore.containsKey(v); } - private static int boreCell(VectorMap source, VectorMap bore, IrisBlockVector v, PlatformBlockState vair) { - PlatformBlockState existing = source.get(v); + private static int boreCell(VectorMap source, VectorMap bore, IrisBlockVector v, NativeBlockState vair) { + NativeBlockState existing = source.get(v); if (existing == null) { if (vair.equals(bore.get(v))) { @@ -208,7 +208,7 @@ final class IrisObjectShaping { return; } - VectorMap b = new VectorMap<>(); + VectorMap b = new VectorMap<>(); VectorMap s = new VectorMap<>(); IrisBlockVector shift = new IrisBlockVector(offset.getX(), offset.getY(), offset.getZ()); @@ -234,7 +234,7 @@ final class IrisObjectShaping { } static void clean(IrisObject self) { - VectorMap d = new VectorMap<>(); + VectorMap d = new VectorMap<>(); d.putAll(self.blocks); VectorMap dx = new VectorMap<>(); @@ -246,7 +246,7 @@ final class IrisObjectShaping { self.floatingFootprint.reset(); } - static boolean isStiltLayerBlock(PlatformBlockState state) { + static boolean isStiltLayerBlock(NativeBlockState state) { String material = materialKey(state); if (!state.isOccluding() && !material.equals("minecraft:ice") && !material.equals("minecraft:glass") && !material.equals("minecraft:tinted_glass") && !material.endsWith("_stained_glass")) { @@ -258,12 +258,12 @@ final class IrisObjectShaping { return !material.equals("minecraft:dirt_path"); } - static boolean shouldStilt(PlatformBlockState state, Set excludedMaterials) { + static boolean shouldStilt(NativeBlockState state, Set excludedMaterials) { return isStiltLayerBlock(state) && !state.isStorage() && !state.hasTileEntity() && !excludedMaterials.contains(materialKey(state)); } - static String materialKey(PlatformBlockState state) { + static String materialKey(NativeBlockState state) { return IrisProceduralBlocks.materialKey(state); } } diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectTransforms.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectTransforms.java index b627d4100..444abbb35 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectTransforms.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectTransforms.java @@ -21,7 +21,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.pack.value.IrisPosition; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.AxisAlignedBB; @@ -56,11 +56,11 @@ final class IrisObjectTransforms { static void rotate(IrisObject self, IrisObjectRotation r, int spinx, int spiny, int spinz) { self.writeLock.lock(); try { - VectorMap d = new VectorMap<>(); + VectorMap d = new VectorMap<>(); Set omitted = new HashSet<>(); - for (Map.Entry entry : self.blocks) { - PlatformBlockState rotated = r.rotate(entry.getValue(), spinx, spiny, spinz); + for (Map.Entry entry : self.blocks) { + NativeBlockState rotated = r.rotate(entry.getValue(), spinx, spiny, spinz); if (rotated == null) { omitted.add(entry.getKey()); continue; @@ -114,7 +114,7 @@ final class IrisObjectTransforms { Vector3i sourceCenter = self.getCenter(); IrisBlockVector targetCenter = new IrisBlockVector(output.getCenter().getX(), output.getCenter().getY(), output.getCenter().getZ()); - for (Map.Entry entry : self.blocks) { + for (Map.Entry entry : self.blocks) { IrisBlockVector sourcePosition = entry.getKey(); IrisBlockVector position = savedOrigin ? sourcePosition : new IrisBlockVector( sourcePosition.getBlockX() + sourceCenter.getX(), @@ -142,7 +142,7 @@ final class IrisObjectTransforms { writeScaledVoxel(output, voxel); } - VectorMap scaledBlocks = output.blocks; + VectorMap scaledBlocks = output.blocks; if (scale > 1) { switch (interpolation) { case TRILINEAR -> trilinear(output, (int) Math.round(scale)); @@ -215,11 +215,11 @@ final class IrisObjectTransforms { Math.ceil((position.getZ() + 1) * scale) - 1); } - private static void removeInapplicableTiles(IrisObject object, VectorMap sourceBlocks) { + private static void removeInapplicableTiles(IrisObject object, VectorMap sourceBlocks) { Iterator> iterator = object.states.iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - PlatformBlockState block = object.blocks.get(entry.getKey()); + NativeBlockState block = object.blocks.get(entry.getKey()); String tileKey = entry.getValue().getMaterialKey(); if (tileKey == null) { tileKey = IrisObjectShaping.materialKey(sourceBlocks.get(entry.getKey())); @@ -238,8 +238,8 @@ final class IrisObjectTransforms { static void trilinear(IrisObject self, int rad) { self.writeLock.lock(); try { - VectorMap v = self.blocks; - VectorMap b = new VectorMap<>(); + VectorMap v = self.blocks; + VectorMap b = new VectorMap<>(); IrisPosition min = self.getAABB().min(); IrisPosition max = self.getAABB().max(); NearestBlockIndex nearestBlocks = NearestBlockIndex.create(v); @@ -248,7 +248,7 @@ final class IrisObjectTransforms { for (int y = min.getY(); y <= max.getY(); y++) { for (int z = min.getZ(); z <= max.getZ(); z++) { if (Interpolation3D.getTrilinear(x, y, z, rad, (xx, yy, zz) -> { - PlatformBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); + NativeBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); if (B.isAir(data)) { return 0; @@ -275,8 +275,8 @@ final class IrisObjectTransforms { static void tricubic(IrisObject self, int rad) { self.writeLock.lock(); try { - VectorMap v = self.blocks; - VectorMap b = new VectorMap<>(); + VectorMap v = self.blocks; + VectorMap b = new VectorMap<>(); IrisPosition min = self.getAABB().min(); IrisPosition max = self.getAABB().max(); NearestBlockIndex nearestBlocks = NearestBlockIndex.create(v); @@ -285,7 +285,7 @@ final class IrisObjectTransforms { for (int y = min.getY(); y <= max.getY(); y++) { for (int z = min.getZ(); z <= max.getZ(); z++) { if (Interpolation3D.getTricubic(x, y, z, rad, (xx, yy, zz) -> { - PlatformBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); + NativeBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); if (B.isAir(data)) { return 0; @@ -316,8 +316,8 @@ final class IrisObjectTransforms { static void trihermite(IrisObject self, int rad, double tension, double bias) { self.writeLock.lock(); try { - VectorMap v = self.blocks; - VectorMap b = new VectorMap<>(); + VectorMap v = self.blocks; + VectorMap b = new VectorMap<>(); IrisPosition min = self.getAABB().min(); IrisPosition max = self.getAABB().max(); NearestBlockIndex nearestBlocks = NearestBlockIndex.create(v); @@ -326,7 +326,7 @@ final class IrisObjectTransforms { for (int y = min.getY(); y <= max.getY(); y++) { for (int z = min.getZ(); z <= max.getZ(); z++) { if (Interpolation3D.getTrihermite(x, y, z, rad, (xx, yy, zz) -> { - PlatformBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); + NativeBlockState data = v.get(new IrisBlockVector((int) xx, (int) yy, (int) zz)); if (B.isAir(data)) { return 0; @@ -350,11 +350,11 @@ final class IrisObjectTransforms { } } - private static PlatformBlockState nearestBlockData(VectorMap blocks, + private static NativeBlockState nearestBlockData(VectorMap blocks, NearestBlockIndex nearestBlocks, int x, int y, int z) { IrisBlockVector vv = new IrisBlockVector(x, y, z); - PlatformBlockState direct = blocks.get(vv); + NativeBlockState direct = blocks.get(vv); if (!B.isAir(direct)) { return direct; } @@ -373,7 +373,7 @@ final class IrisObjectTransforms { .thenComparingInt(NearestBlock::rank); private final NearestNode root; - private PlatformBlockState bestState; + private NativeBlockState bestState; private double bestDistance; private int bestRank; @@ -381,12 +381,12 @@ final class IrisObjectTransforms { this.root = root; } - static NearestBlockIndex create(VectorMap blocks) { + static NearestBlockIndex create(VectorMap blocks) { List points = new ArrayList<>(blocks.size()); - VectorMap.Cursor cursor = blocks.cursor(); + VectorMap.Cursor cursor = blocks.cursor(); int rank = 0; while (cursor.next()) { - PlatformBlockState state = cursor.value(); + NativeBlockState state = cursor.value(); if (!B.isAir(state)) { IrisBlockVector position = cursor.key(); points.add(new NearestBlock(position.getBlockX(), position.getBlockY(), position.getBlockZ(), @@ -399,7 +399,7 @@ final class IrisObjectTransforms { return new NearestBlockIndex(build(pointArray, 0, pointArray.length, 0)); } - PlatformBlockState nearest(int x, int y, int z, PlatformBlockState fallback) { + NativeBlockState nearest(int x, int y, int z, NativeBlockState fallback) { if (root == null) { return fallback; } @@ -468,11 +468,11 @@ final class IrisObjectTransforms { private record NearestNode(NearestBlock point, int axis, NearestNode lower, NearestNode upper) { } - private record NearestBlock(int x, int y, int z, int rank, PlatformBlockState state) { + private record NearestBlock(int x, int y, int z, int rank, NativeBlockState state) { } private record ScaledVoxel(IrisBlockVector source, IrisBlockVector minimum, IrisBlockVector maximum, - PlatformBlockState block, TileData tile) { + NativeBlockState block, TileData tile) { } private enum ScaleOrigin { diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectVanillaLoot.java b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectVanillaLoot.java index fadfe8645..8ae0be6b3 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisObjectVanillaLoot.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisObjectVanillaLoot.java @@ -3,7 +3,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -15,7 +15,7 @@ import art.arcane.volmlib.util.collection.KList; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; @Snippet("object-vanilla-loot") @@ -25,7 +25,7 @@ import lombok.experimental.Accessors; @Description("Represents vanilla loot within this object") @Data public class IrisObjectVanillaLoot implements IObjectLoot { - private final transient AtomicCache> filterCache = new AtomicCache<>(); + private final transient AtomicCache> filterCache = new AtomicCache<>(); @ArrayType(min = 1, type = IrisBlockData.class) @Description("The list of blocks this loot table should apply to") private KList filter = new KList<>(); @@ -39,13 +39,13 @@ public class IrisObjectVanillaLoot implements IObjectLoot { @MinNumber(1) private int weight = 1; - public KList getFilter(IrisData rdata) { + public KList getFilter(IrisData rdata) { return filterCache.aquire(() -> { - KList b = new KList<>(); + KList b = new KList<>(); for (IrisBlockData i : filter) { - PlatformBlockState bx = i.getBlockData(rdata); + NativeBlockState bx = i.getBlockData(rdata); if (bx != null) { b.add(bx); diff --git a/core/src/main/java/art/arcane/iris/structure/object/IrisStaticObjectLayer.java b/core/src/main/java/art/arcane/iris/structure/object/IrisStaticObjectLayer.java index b3f1f6c4d..05ae61c99 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/IrisStaticObjectLayer.java +++ b/core/src/main/java/art/arcane/iris/structure/object/IrisStaticObjectLayer.java @@ -8,7 +8,7 @@ import art.arcane.iris.integration.Identifier; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.decoration.tree.TreeBlockMaterial; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.iris.world.storage.matter.TileWrapper; import art.arcane.volmlib.util.mantle.flag.MantleFlag; @@ -94,7 +94,7 @@ public final class IrisStaticObjectLayer { return chunks.isEmpty(); } - public void apply(Engine engine, int x, int z, Hunk output) { + public void apply(Engine engine, int x, int z, Hunk output) { List blocks = blocks(x >> 4, z >> 4); if (blocks.isEmpty()) { return; @@ -110,7 +110,7 @@ public final class IrisStaticObjectLayer { slice.set(block.x(), block.y() & 15, block.z(), null); } } - PlatformBlockState state = block.state(); + NativeBlockState state = block.state(); if (state.isCustom()) { section.slice(Identifier.class).set(block.x(), block.y() & 15, block.z(), Identifier.fromString(state.deferredPlacementKey())); @@ -131,7 +131,7 @@ public final class IrisStaticObjectLayer { return ((long) x << 32) | (z & 0xffffffffL); } - public record Block(int x, int y, int z, PlatformBlockState state, TileData tile) { + public record Block(int x, int y, int z, NativeBlockState state, TileData tile) { } private record Chunk(Map blocks, List ordered) { @@ -155,7 +155,7 @@ public final class IrisStaticObjectLayer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (y < 0 || y >= height) { throw new IllegalArgumentException("Transformed object extends outside the dimension height"); } @@ -171,7 +171,7 @@ public final class IrisStaticObjectLayer { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { Block block = block(x, y, z); return block == null ? IrisObject.States.air() : block.state(); } diff --git a/core/src/main/java/art/arcane/iris/structure/object/WorldObjectPlacer.java b/core/src/main/java/art/arcane/iris/structure/object/WorldObjectPlacer.java index 5f6dbd748..e48f158f1 100644 --- a/core/src/main/java/art/arcane/iris/structure/object/WorldObjectPlacer.java +++ b/core/src/main/java/art/arcane/iris/structure/object/WorldObjectPlacer.java @@ -15,7 +15,7 @@ import art.arcane.iris.world.loot.IrisLootTable; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.world.task.J; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.generation.block.IrisCustomData; @@ -55,7 +55,7 @@ public class WorldObjectPlacer implements IObjectPlacer { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { BlockData d = (BlockData) state.nativeHandle(); int worldY = y + world.getMinHeight(); if (worldY < world.getMinHeight() || worldY >= world.getMaxHeight()) return; @@ -99,7 +99,7 @@ public class WorldObjectPlacer implements IObjectPlacer { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return BukkitBlockState.of(world.getBlockAt(x, y + world.getMinHeight(), z).getBlockData()); } diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureCarveShape.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureCarveShape.java deleted file mode 100644 index 86c2de54c..000000000 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureCarveShape.java +++ /dev/null @@ -1,36 +0,0 @@ -package art.arcane.iris.structure.placement; - -import art.arcane.volmlib.util.documentation.Description; - -@Description("Controls the shape of the expanded terrain carve around an overbored structure.") -public enum IrisStructureCarveShape { - @Description("Carves the full expanded rectangular volume with straight walls, ceiling, and floor.") - BOX, - - @Description("Carves a smooth rounded volume around the structure without erosion noise.") - ROUNDED, - - @Description("Carves a rounded volume whose outer boundary is eroded by deterministic noise.") - ERODED; - - public double maximumCeilingScale() { - return maximumCeilingScale(1D); - } - - public double maximumCeilingScale(double erosionStrength) { - double strength = Double.isFinite(erosionStrength) - ? Math.max(0D, Math.min(1D, erosionStrength)) : 1D; - return switch (this) { - case BOX, ROUNDED -> 1D; - case ERODED -> 1D + 0.8D * strength; - }; - } - - public int maximumCeilingExtension(int configuredHeight) { - return maximumCeilingExtension(configuredHeight, 1D); - } - - public int maximumCeilingExtension(int configuredHeight, double erosionStrength) { - return (int) Math.ceil(Math.max(0, configuredHeight) * maximumCeilingScale(erosionStrength)); - } -} diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureLocator.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureLocator.java index 45196992d..7279f0fce 100644 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureLocator.java +++ b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureLocator.java @@ -18,6 +18,14 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.nativelib.terrain.structure.StructureLocateCandidate; + +import art.arcane.volmlib.nativelib.terrain.StructureLocateLimitException; + +import art.arcane.volmlib.util.structure.StructureTerrainMode; + +import art.arcane.volmlib.util.structure.StructureCarveShape; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.SeedManager; import art.arcane.iris.structure.nativegen.NativeStructurePlacementPlanner; @@ -200,7 +208,7 @@ public final class IrisStructureLocator { int maxRadiusChunks, CandidateFilter candidateFilter) { try { return locateFiltered(engine, key, fromBlockX, fromBlockZ, maxRadiusChunks, candidateFilter); - } catch (CandidateSearchLimitException ignored) { + } catch (StructureLocateLimitException ignored) { return SEARCH_LIMIT_RESULT; } } @@ -211,7 +219,7 @@ public final class IrisStructureLocator { try { return locateInPlacementRingsFiltered( engine, key, fromBlockX, fromBlockZ, maxSearchRadius, candidateFilter); - } catch (CandidateSearchLimitException ignored) { + } catch (StructureLocateLimitException ignored) { return SEARCH_LIMIT_RESULT; } } @@ -723,11 +731,11 @@ public final class IrisStructureLocator { } IrisStructureTerrain terrain = placement.resolvedTerrain(); - IrisStructureTerrainMode terrainMode = terrain.resolvedMode(); - boolean forceCarve = terrainMode == IrisStructureTerrainMode.FORCE_CARVE; - boolean bore = terrainMode == IrisStructureTerrainMode.BORE; + StructureTerrainMode terrainMode = terrain.resolvedMode(); + boolean forceCarve = terrainMode == StructureTerrainMode.FORCE_CARVE; + boolean bore = terrainMode == StructureTerrainMode.BORE; int sideExtension = forceCarve || bore ? Math.max(0, terrain.getHorizontalPadding()) : 0; - IrisStructureCarveShape carveShape = terrain.resolvedShape(); + StructureCarveShape carveShape = terrain.resolvedShape(); int topExtension = forceCarve ? carveShape.maximumCeilingExtension( terrain.getCeilingPadding(), terrain.resolvedErosionStrength()) @@ -743,7 +751,7 @@ public final class IrisStructureLocator { Long2IntOpenHashMap surfaceHeights = new Long2IntOpenHashMap(); surfaceHeights.defaultReturnValue(Integer.MIN_VALUE); - if (bore || forceCarve && carveShape == IrisStructureCarveShape.BOX) { + if (bore || forceCarve && carveShape == StructureCarveShape.BOX) { maximumShift = resolveBurialEnvelopeShift( engine, bounds[0] - sideExtension, @@ -908,9 +916,9 @@ public final class IrisStructureLocator { return false; } IrisStructureTerrain terrain = placement.resolvedTerrain(); - IrisStructureTerrainMode mode = terrain.resolvedMode(); - int padding = mode == IrisStructureTerrainMode.FORCE_CARVE - || mode == IrisStructureTerrainMode.BORE + StructureTerrainMode mode = terrain.resolvedMode(); + int padding = mode == StructureTerrainMode.FORCE_CARVE + || mode == StructureTerrainMode.BORE ? Math.max(0, terrain.getHorizontalPadding()) : 0; return engine.getComplex().allowsNewGenerationFootprint( @@ -1306,7 +1314,11 @@ public final class IrisStructureLocator { SEARCH_LIMIT_REACHED } - public record LocateResult(LocateStatus status, int originX, int baseY, int originZ) { + public record LocateResult(LocateStatus status, int originX, int baseY, int originZ) implements StructureLocateCandidate { + public boolean limitReached() { + return status == LocateStatus.SEARCH_LIMIT_REACHED; + } + public boolean found() { return status == LocateStatus.FOUND; } @@ -1317,12 +1329,6 @@ public final class IrisStructureLocator { boolean accept(int chunkX, int chunkZ); } - public static final class CandidateSearchLimitException extends RuntimeException { - public CandidateSearchLimitException() { - super(null, null, false, false); - } - } - private record RandomSpreadParameters(int spacing, int separation, int salt) { } @@ -1346,7 +1352,7 @@ public final class IrisStructureLocator { private void claim() { if (checked >= CANDIDATE_BUDGET) { - throw new CandidateSearchLimitException(); + throw new StructureLocateLimitException(); } checked++; } diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureStiltSettings.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureStiltSettings.java index 90e7b82ad..806761fcb 100644 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureStiltSettings.java +++ b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureStiltSettings.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.nativelib.terrain.structure.StructureStiltSettings; + import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.volmlib.util.documentation.Description; @@ -15,7 +17,7 @@ import lombok.experimental.Accessors; @AllArgsConstructor @Description("Defines foundation columns placed beneath a structure.") @Data -public class IrisStructureStiltSettings { +public class IrisStructureStiltSettings implements StructureStiltSettings { @MinNumber(1) @MaxNumber(4064) @Description("Maximum number of blocks each foundation column may descend while searching for solid ground.") diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrain.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrain.java index f9e3ef98b..f828fb388 100644 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrain.java +++ b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrain.java @@ -1,5 +1,11 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.nativelib.terrain.structure.StructureTerrainSettings; + +import art.arcane.volmlib.util.structure.StructureTerrainMode; + +import art.arcane.volmlib.util.structure.StructureCarveShape; + import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.volmlib.util.documentation.Description; @@ -15,7 +21,7 @@ import lombok.experimental.Accessors; @AllArgsConstructor @Data @Description("Terrain integration applied after a structure graph is assembled and before its pieces are placed.") -public class IrisStructureTerrain { +public class IrisStructureTerrain implements StructureTerrainSettings { private static final double AUTO_LOBE_FREQUENCY_RATIO = 0.3D; private static final double DEFAULT_EROSION_STRENGTH = 0.8D; private static final double DEFAULT_EROSION_FREQUENCY = 0.07D; @@ -25,7 +31,7 @@ public class IrisStructureTerrain { private static final double MAX_LOBE_FREQUENCY = 1D; @Description("Terrain operation. SOURCE applies the registered native structure's authored terrain adaptation and is a no-op for editable Iris structures. PRESERVE disables terrain integration. FLATTEN cuts and fills exposed native structure terrain within flattenRange, blending across horizontalPadding blocks. VACUUM raises terrain from processed rigid-template foundations with a fixed 12-block falloff without lowering ground. BORE and FORCE_CARVE clear the requested envelope, while ENCASE fills it before placement.") - private IrisStructureTerrainMode mode = IrisStructureTerrainMode.SOURCE; + private StructureTerrainMode mode = StructureTerrainMode.SOURCE; @MinNumber(0) @MaxNumber(128) @@ -48,7 +54,7 @@ public class IrisStructureTerrain { private int floorPadding = 0; @Description("Shape used by FORCE_CARVE.") - private IrisStructureCarveShape shape = IrisStructureCarveShape.BOX; + private StructureCarveShape shape = StructureCarveShape.BOX; @Description("Block palette used by ENCASE. When unset, the Overworld uses stone or deepslate, the Nether uses netherrack, and the End uses end stone.") private IrisMaterialPalette encasePalette = null; @@ -73,12 +79,12 @@ public class IrisStructureTerrain { @Description("Fraction of the horizontal padding the lobes may remove for ERODED force carving. Zero keeps a uniform boundary.") private double lobeStrength = DEFAULT_LOBE_STRENGTH; - public IrisStructureTerrainMode resolvedMode() { - return mode == null ? IrisStructureTerrainMode.SOURCE : mode; + public StructureTerrainMode resolvedMode() { + return mode == null ? StructureTerrainMode.SOURCE : mode; } - public IrisStructureCarveShape resolvedShape() { - return shape == null ? IrisStructureCarveShape.BOX : shape; + public StructureCarveShape resolvedShape() { + return shape == null ? StructureCarveShape.BOX : shape; } public int resolvedFlattenRange() { diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrainMode.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrainMode.java deleted file mode 100644 index ac6f42128..000000000 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureTerrainMode.java +++ /dev/null @@ -1,27 +0,0 @@ -package art.arcane.iris.structure.placement; - -import art.arcane.volmlib.util.documentation.Description; - -@Description("Controls how terrain is integrated with a structure.") -public enum IrisStructureTerrainMode { - @Description("Replays the registered native structure's own authored terrain adaptation (beard surface fitting, BURY/ENCAPSULATE fill, legacy template-air clearing). Does nothing for editable Iris structures, which have no source adaptation to replay.") - SOURCE, - - @Description("Disables all terrain integration. Pieces are placed into completely unmodified terrain and may end up embedded in solid rock or floating over dips.") - PRESERVE, - - @Description("Clears one straight axis-aligned box around the assembled pieces, expanded by horizontalPadding, ceilingPadding, and floorPadding. Ignores the carve shape and erosion settings.") - BORE, - - @Description("Clears the padded envelope using the configured carve shape (BOX, ROUNDED, or noise-eroded ERODED) so the cavity hugs the assembled pieces instead of a straight box.") - FORCE_CARVE, - - @Description("Raises surface terrain from processed solid rigid-template foundations at or below each authored ground plane with a 12-block falloff, even when the registered structure has no terrain adaptation. Existing higher terrain and authored air remain untouched.") - VACUUM, - - @Description("Cuts and fills exposed native structure foundations within flattenRange, blending into surrounding terrain across horizontalPadding blocks. Buried and submerged pieces retain their terrain.") - FLATTEN, - - @Description("Fills the padded piece volume with solid blocks before any piece is placed so shells, walls, and floors land in solid ground instead of pre-carved air. Only air and liquid cells are filled; existing terrain and structures are never overwritten. Native pieces then carve their own interiors.") - ENCASE -} diff --git a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureYBand.java b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureYBand.java index c34867f59..863b54f61 100644 --- a/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureYBand.java +++ b/core/src/main/java/art/arcane/iris/structure/placement/IrisStructureYBand.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.nativelib.terrain.structure.StructureVerticalBand; + import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; @@ -13,7 +15,7 @@ import lombok.experimental.Accessors; @AllArgsConstructor @Data @Description("An absolute world Y band a native structure is relocated into. Reversed bounds are normalized.") -public class IrisStructureYBand { +public class IrisStructureYBand implements StructureVerticalBand { @MinNumber(-4064) @MaxNumber(4064) @Description("Lowest absolute world Y of the band.") diff --git a/core/src/main/java/art/arcane/iris/structure/placement/StructureVerticalBounds.java b/core/src/main/java/art/arcane/iris/structure/placement/StructureVerticalBounds.java deleted file mode 100644 index 0fd295d8d..000000000 --- a/core/src/main/java/art/arcane/iris/structure/placement/StructureVerticalBounds.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.structure.placement; - -public final class StructureVerticalBounds { - private StructureVerticalBounds() { - } - - public static int clampOffset( - int structureMinY, - int structureMaxY, - int requestedOffset, - int worldMinY, - int worldMaxYExclusive - ) { - if (structureMaxY < structureMinY) { - throw new IllegalArgumentException("Structure maximum Y cannot be below its minimum Y"); - } - if (worldMaxYExclusive <= worldMinY) { - throw new IllegalArgumentException("World maximum Y must be above its minimum Y"); - } - - long structureHeight = (long) structureMaxY - structureMinY + 1L; - long worldHeight = (long) worldMaxYExclusive - worldMinY; - if (structureHeight > worldHeight) { - throw new IllegalArgumentException("Structure height " + structureHeight - + " exceeds writable world height " + worldHeight); - } - - long minimumOffset = (long) worldMinY - structureMinY; - long maximumOffset = (long) worldMaxYExclusive - 1L - structureMaxY; - long clamped = Math.max(minimumOffset, Math.min(maximumOffset, (long) requestedOffset)); - return Math.toIntExact(clamped); - } -} diff --git a/core/src/main/java/art/arcane/iris/studio/StudioSVC.java b/core/src/main/java/art/arcane/iris/studio/StudioSVC.java index 9cd32ec81..2efb96ee8 100644 --- a/core/src/main/java/art/arcane/iris/studio/StudioSVC.java +++ b/core/src/main/java/art/arcane/iris/studio/StudioSVC.java @@ -48,7 +48,7 @@ import art.arcane.iris.world.runtime.TransientWorldCleanupSupport; import art.arcane.iris.world.runtime.WorldDeletionQueue; import art.arcane.iris.studio.jigsaw.JigsawStudioActivation; import art.arcane.iris.world.IrisToolbelt; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.world.history.GenerationActivation; import art.arcane.iris.world.history.GenerationEpoch; import art.arcane.iris.world.history.GenerationEpochContractFactory; diff --git a/core/src/main/java/art/arcane/iris/studio/edit/DustRevealer.java b/core/src/main/java/art/arcane/iris/studio/edit/DustRevealer.java index f91c8093e..a9bd664f3 100644 --- a/core/src/main/java/art/arcane/iris/studio/edit/DustRevealer.java +++ b/core/src/main/java/art/arcane/iris/studio/edit/DustRevealer.java @@ -260,7 +260,7 @@ public class DustRevealer { lines.add(new DustLine(IrisLanguage.text( RuntimeUiMessages.DUST_SERVER_BIOME, MessageArgument.untrusted("biome", INMS.get().getTrueBiomeBaseKey(block.getLocation())), - MessageArgument.trusted("id", INMS.get().getTrueBiomeBaseId(INMS.get().getTrueBiomeBase(block.getLocation()))) + MessageArgument.trusted("id", INMS.get().getBiomeId(block.getLocation())) ), false)); } catch (Throwable e) { IrisLogging.reportError(e); diff --git a/core/src/main/java/art/arcane/iris/studio/generation/JigsawStudioGenerator.java b/core/src/main/java/art/arcane/iris/studio/generation/JigsawStudioGenerator.java index b3fcf0925..cc8fe26bc 100644 --- a/core/src/main/java/art/arcane/iris/studio/generation/JigsawStudioGenerator.java +++ b/core/src/main/java/art/arcane/iris/studio/generation/JigsawStudioGenerator.java @@ -24,7 +24,7 @@ import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.structure.jigsaw.JigsawJoint; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.bukkit.BukkitWorldBinding; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -48,18 +48,18 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { private final JigsawStudioActivation.Request request; private final JigsawStudioSession session; - private final PlatformBlockState lightFloor; - private final PlatformBlockState darkFloor; - private final PlatformBlockState frame; - private final PlatformBlockState topologyBase; - private final PlatformBlockState topologyPath; - private final PlatformBlockState connectorCap; - private final PlatformBlockState invalidMarker; - private final PlatformBlockState controlChest; + private final NativeBlockState lightFloor; + private final NativeBlockState darkFloor; + private final NativeBlockState frame; + private final NativeBlockState topologyBase; + private final NativeBlockState topologyPath; + private final NativeBlockState connectorCap; + private final NativeBlockState invalidMarker; + private final NativeBlockState controlChest; private final FinalStateRenderer finalStateRenderer; private final AtomicBoolean serviceRegistered = new AtomicBoolean(false); private final ConcurrentMap renderedBays = new ConcurrentHashMap<>(); - private final ConcurrentMap jigsawStates = new ConcurrentHashMap<>(); + private final ConcurrentMap jigsawStates = new ConcurrentHashMap<>(); public static boolean isLightFloor(int blockX, int blockZ) { int tileX = Math.floorDiv(blockX, CHECKER_SIZE); @@ -92,14 +92,14 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { Engine engine, JigsawStudioActivation.Request request, JigsawStudioSession session, - PlatformBlockState lightFloor, - PlatformBlockState darkFloor, - PlatformBlockState frame, - PlatformBlockState topologyBase, - PlatformBlockState topologyPath, - PlatformBlockState connectorCap, - PlatformBlockState invalidMarker, - PlatformBlockState controlChest, + NativeBlockState lightFloor, + NativeBlockState darkFloor, + NativeBlockState frame, + NativeBlockState topologyBase, + NativeBlockState topologyPath, + NativeBlockState connectorCap, + NativeBlockState invalidMarker, + NativeBlockState controlChest, FinalStateRenderer finalStateRenderer ) { super(engine); @@ -267,7 +267,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { for (int localZ = 0; localZ < 16; localZ++) { int worldX = chunkWorldX + localX; int worldZ = chunkWorldZ + localZ; - PlatformBlockState block = isLightFloor(worldX, worldZ) ? lightFloor : darkFloor; + NativeBlockState block = isLightFloor(worldX, worldZ) ? lightFloor : darkFloor; terrainChunk.setBlock(localX, floorY, localZ, block); } } @@ -367,14 +367,14 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { int localX = worldX - bounds.originX(); for (int worldZ = minimumZ; worldZ <= maximumZ; worldZ++) { int localZ = worldZ - bounds.originZ(); - PlatformBlockState state = topologyGlyphState(topology, bounds.dimensions(), localX, localZ, + NativeBlockState state = topologyGlyphState(topology, bounds.dimensions(), localX, localZ, centerX, centerZ); setWorldBlock(terrainChunk, worldX, glyphY, worldZ, state, chunkWorldX, chunkWorldZ); } } } - private PlatformBlockState topologyGlyphState( + private NativeBlockState topologyGlyphState( JigsawPlanarTopology topology, JigsawStudioCellDimensions dimensions, int localX, @@ -485,11 +485,11 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { int quarterTurns, IrisObjectRotation rotation ) { - VectorMap sourceBlocks = object.getBlocks(); + VectorMap sourceBlocks = object.getBlocks(); VectorMap sourceTiles = object.getStates(); Vector3i center = object.getCenter(); List blocks = new ArrayList<>(sourceBlocks.size()); - for (Map.Entry entry : sourceBlocks) { + for (Map.Entry entry : sourceBlocks) { IrisBlockVector signed = entry.getKey(); int sourceX = signed.getBlockX() + center.getBlockX(); int sourceY = signed.getBlockY() + center.getBlockY(); @@ -513,7 +513,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { dimensions.depth())) { return null; } - PlatformBlockState blockState = quarterTurns == 0 + NativeBlockState blockState = quarterTurns == 0 ? entry.getValue() : rotation.rotate(entry.getValue(), 0, 0, 0); if (blockState == null) { @@ -607,14 +607,14 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { IrisObjectRotation rotation, int quarterTurns ) { - PlatformBlockState state = B.getStateOrNull(finalState, false); + NativeBlockState state = B.getStateOrNull(finalState, false); if (state == null) { return null; } if (quarterTurns == 0) { return state.key(); } - PlatformBlockState rotated = rotation.rotate(state, 0, 0, 0); + NativeBlockState rotated = rotation.rotate(state, 0, 0, 0); return rotated == null ? null : rotated.key(); } @@ -647,7 +647,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { ) { JigsawStudioBounds bounds = workcell.bounds(); for (RenderedConnector connector : renderedBay.connectors()) { - PlatformBlockState state = jigsawStates.computeIfAbsent( + NativeBlockState state = jigsawStates.computeIfAbsent( connector.orientation(), key -> B.getState("minecraft:jigsaw[orientation=" + key + "]")); setWorldBlock( @@ -677,7 +677,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { if (occupied.contains(new RenderedPosition(connector.x(), connector.y(), connector.z()))) { continue; } - PlatformBlockState finalState = B.getStateOrNull(connector.connector().getFinalState(), false); + NativeBlockState finalState = B.getStateOrNull(connector.connector().getFinalState(), false); setWorldBlock( terrainChunk, bounds.originX() + connector.x(), @@ -835,7 +835,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { int worldX, int worldY, int worldZ, - PlatformBlockState block, + NativeBlockState block, int chunkWorldX, int chunkWorldZ ) { @@ -884,7 +884,7 @@ public final class JigsawStudioGenerator extends EnginedStudioGenerator { } } - public record RenderedBlock(int x, int y, int z, PlatformBlockState state, TileData tileData) { + public record RenderedBlock(int x, int y, int z, NativeBlockState state, TileData tileData) { public RenderedBlock { state = Objects.requireNonNull(state, "Jigsaw Studio rendered block state"); } diff --git a/core/src/main/java/art/arcane/iris/studio/generation/ObjectStudioGenerator.java b/core/src/main/java/art/arcane/iris/studio/generation/ObjectStudioGenerator.java index a44f6b94f..556e2f394 100644 --- a/core/src/main/java/art/arcane/iris/studio/generation/ObjectStudioGenerator.java +++ b/core/src/main/java/art/arcane/iris/studio/generation/ObjectStudioGenerator.java @@ -32,8 +32,8 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.bukkit.BukkitBiome; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.VectorMap; import art.arcane.volmlib.util.math.Vector3i; import org.bukkit.Material; @@ -51,15 +51,15 @@ import java.util.concurrent.atomic.AtomicBoolean; public class ObjectStudioGenerator extends EnginedStudioGenerator { public static final int DEFAULT_PADDING = 2; - private static final PlatformBlockState FLOOR = BukkitBlockState.of(Material.POLISHED_DEEPSLATE.createBlockData()); - private static final PlatformBlockState FRAME = BukkitBlockState.of(Material.SMOOTH_QUARTZ.createBlockData()); - private static final PlatformBlockState MARKER = BukkitBlockState.of(Material.END_ROD.createBlockData()); - private static final PlatformBiome DEFAULT_BIOME = BukkitBiome.of(Biome.PLAINS); + private static final NativeBlockState FLOOR = BukkitBlockState.of(Material.POLISHED_DEEPSLATE.createBlockData()); + private static final NativeBlockState FRAME = BukkitBlockState.of(Material.SMOOTH_QUARTZ.createBlockData()); + private static final NativeBlockState MARKER = BukkitBlockState.of(Material.END_ROD.createBlockData()); + private static final NativeBiome DEFAULT_BIOME = BukkitBiome.of(Biome.PLAINS); private final int padding; - private final PlatformBlockState floor; - private final PlatformBlockState frame; - private final PlatformBlockState marker; + private final NativeBlockState floor; + private final NativeBlockState frame; + private final NativeBlockState marker; private final AtomicBoolean layoutBuilt = new AtomicBoolean(false); private final Object layoutLock = new Object(); private final Map objectCache = new ConcurrentHashMap<>(); @@ -70,7 +70,7 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator { this(engine, DEFAULT_PADDING, FLOOR, FRAME, MARKER); } - public ObjectStudioGenerator(Engine engine, int padding, PlatformBlockState floor, PlatformBlockState frame, PlatformBlockState marker) { + public ObjectStudioGenerator(Engine engine, int padding, NativeBlockState floor, NativeBlockState frame, NativeBlockState marker) { super(engine); this.padding = padding; this.floor = floor; @@ -203,14 +203,14 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator { } private void placeSlice(DisplayObject object, GridCell cell, TerrainChunk tc, int chunkWorldX, int chunkWorldZ, int minHeight, int maxHeight, List tiles) { - VectorMap blocks = object.source().getBlocks(); + VectorMap blocks = object.source().getBlocks(); if (blocks == null || blocks.isEmpty()) return; int originX = cell.originX(); int originY = cell.originY(); int originZ = cell.originZ(); - for (Map.Entry entry : blocks) { + for (Map.Entry entry : blocks) { IrisBlockVector signed = entry.getKey(); int worldX = originX + signed.getBlockX() - object.minimumX(); int worldY = originY + signed.getBlockY() - object.minimumY(); @@ -220,7 +220,7 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator { if (worldZ < chunkWorldZ || worldZ > chunkWorldZ + 15) continue; if (worldY < minHeight || worldY >= maxHeight) continue; - PlatformBlockState data = entry.getValue(); + NativeBlockState data = entry.getValue(); if (data == null) continue; tc.setBlock(worldX - chunkWorldX, worldY, worldZ - chunkWorldZ, data); @@ -332,7 +332,7 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator { int minimumX = Integer.MAX_VALUE; int minimumY = Integer.MAX_VALUE; int minimumZ = Integer.MAX_VALUE; - for (Map.Entry entry : object.getBlocks()) { + for (Map.Entry entry : object.getBlocks()) { if (entry.getValue() == null) { continue; } diff --git a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioCapture.java b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioCapture.java index 4acec7dd0..0f594de9b 100644 --- a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioCapture.java +++ b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioCapture.java @@ -10,7 +10,7 @@ import art.arcane.iris.structure.jigsaw.JigsawJoint; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.bukkit.BukkitBlockState; import art.arcane.iris.platform.bukkit.BukkitPlatform; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KMap; import org.bukkit.Bukkit; @@ -189,7 +189,7 @@ final class JigsawStudioCapture { sourceWidth, sourceDepth, quarterTurns); - PlatformBlockState retainedAir = retainedSourceAir( + NativeBlockState retainedAir = retainedSourceAir( captureSource, sourcePosition.x(), sourcePosition.y(), @@ -254,7 +254,7 @@ final class JigsawStudioCapture { return Map.copyOf(displayed); } - static PlatformBlockState retainedSourceAir( + static NativeBlockState retainedSourceAir( IrisObject sourceObject, int x, int y, @@ -272,7 +272,7 @@ final class JigsawStudioCapture { || z >= source.getD()) { return null; } - PlatformBlockState original = source.getBlocks().get(source.getSigned(x, y, z)); + NativeBlockState original = source.getBlocks().get(source.getSigned(x, y, z)); return original != null && original.isAir() ? original : null; } @@ -359,7 +359,7 @@ final class JigsawStudioCapture { throw new IOException("Duplicate captured block at " + block.x() + "," + block.y() + "," + block.z()); } - PlatformBlockState sourceState = quarterTurns == 0 + NativeBlockState sourceState = quarterTurns == 0 ? block.state() : inverseRotation.rotate(block.state(), 0, 0, 0); if (sourceState == null) { @@ -397,13 +397,13 @@ final class JigsawStudioCapture { connector.setPosition(new IrisPosition(position.x(), position.y(), position.z())); connector.setDirection(inverseRotation.rotate(connector.getDirection())); connector.setTop(inverseRotation.rotate(connector.getTop())); - PlatformBlockState finalState = B.getStateOrNull(connector.getFinalState(), false); + NativeBlockState finalState = B.getStateOrNull(connector.getFinalState(), false); if (finalState == null) { throw new IOException("Captured connector final state cannot be parsed at " + capturedConnector.x() + "," + capturedConnector.y() + "," + capturedConnector.z()); } - PlatformBlockState sourceFinalState = inverseRotation.rotate(finalState, 0, 0, 0); + NativeBlockState sourceFinalState = inverseRotation.rotate(finalState, 0, 0, 0); if (sourceFinalState == null) { throw new IOException("Captured connector final state cannot be inverse-rotated at " + capturedConnector.x() + "," + capturedConnector.y() + "," @@ -583,7 +583,7 @@ final class JigsawStudioCapture { int x, int y, int z, - PlatformBlockState state, + NativeBlockState state, TileData tileData ) { CapturedBlock { diff --git a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioChunkWriter.java b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioChunkWriter.java index 3da10be34..2f6fb7dbb 100644 --- a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioChunkWriter.java +++ b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioChunkWriter.java @@ -13,7 +13,7 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.studio.generation.JigsawStudioGenerator; import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KMap; import org.bukkit.Bukkit; import org.bukkit.Material; @@ -100,7 +100,7 @@ final class JigsawStudioChunkWriter { return rendered.failure(); } for (JigsawStudioGenerator.RenderedBlock block : rendered.blocks()) { - PlatformBlockState state = block.state(); + NativeBlockState state = block.state(); if (state.isCustom()) { return "custom block '" + state.key() + "' requires provider-owned placement and cannot be swapped live in Studio"; diff --git a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioObjectResizer.java b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioObjectResizer.java index 30707f774..adac435ab 100644 --- a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioObjectResizer.java +++ b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioObjectResizer.java @@ -7,7 +7,7 @@ import art.arcane.iris.structure.jigsaw.IrisJigsawWorkcellArchetype; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import java.io.IOException; @@ -57,7 +57,7 @@ final class JigsawStudioObjectResizer { sourceObject.getH(), sourceObject.getD()); JigsawStudioCellDimensions canonicalDimensions = canonicalDimensions(sourceDimensions, quarterTurns); - Map canonicalBlocks = canonicalBlocks( + Map canonicalBlocks = canonicalBlocks( sourceObject, quarterTurns, targetPieceKey); @@ -100,17 +100,17 @@ final class JigsawStudioObjectResizer { return new PlanarPieceObjectResize(resizedObject, relocatedConnectors); } - private static Map canonicalBlocks( + private static Map canonicalBlocks( IrisObject source, int quarterTurns, String pieceKey ) throws IOException { - Map blocks = new LinkedHashMap<>(); - for (Map.Entry entry : source.getBlocks()) { + Map blocks = new LinkedHashMap<>(); + for (Map.Entry entry : source.getBlocks()) { LocalPosition sourcePosition = unsignedPosition(entry.getKey(), source); requireInside(sourcePosition, source.getW(), source.getH(), source.getD(), "stored block", pieceKey); - PlatformBlockState state = entry.getValue(); + NativeBlockState state = entry.getValue(); if (state == null) { throw new IOException("Planar piece '" + pieceKey + "' contains a null stored block state"); } @@ -221,7 +221,7 @@ final class JigsawStudioObjectResizer { } private static void relocateConnectorPayloads( - Map blocks, + Map blocks, Map tiles, List connectorResizes, String pieceKey @@ -256,7 +256,7 @@ final class JigsawStudioObjectResizer { continue; } boolean present = blocks.containsKey(connectorResize.sourceCanonical()); - PlatformBlockState state = blocks.remove(connectorResize.sourceCanonical()); + NativeBlockState state = blocks.remove(connectorResize.sourceCanonical()); payloads.add(new BlockRelocation(connectorResize.targetCanonical(), state, present)); } for (BlockRelocation payload : payloads) { @@ -271,7 +271,7 @@ final class JigsawStudioObjectResizer { } private static void requireContentInsideTarget( - Map blocks, + Map blocks, Map tiles, JigsawStudioCellDimensions dimensions, String pieceKey @@ -291,7 +291,7 @@ final class JigsawStudioObjectResizer { } private static IrisObject rebuildSourceObject( - Map canonicalBlocks, + Map canonicalBlocks, Map canonicalTiles, JigsawStudioCellDimensions sourceDimensions, int quarterTurns, @@ -302,7 +302,7 @@ final class JigsawStudioObjectResizer { sourceDimensions.height(), sourceDimensions.depth()); Set sourcePositions = new LinkedHashSet<>(); - for (Map.Entry entry : canonicalBlocks.entrySet()) { + for (Map.Entry entry : canonicalBlocks.entrySet()) { LocalPosition sourcePosition = toSource(entry.getKey(), sourceDimensions, quarterTurns); if (!sourcePositions.add(sourcePosition)) { throw new IOException("Planar piece '" + pieceKey @@ -447,7 +447,7 @@ final class JigsawStudioObjectResizer { "Jigsaw Studio target object dimensions"); String normalizedPiece = pieceKey == null || pieceKey.isBlank() ? "unknown" : pieceKey; IrisObject resized = new IrisObject(target.width(), target.height(), target.depth()); - for (Map.Entry entry : object.getBlocks()) { + for (Map.Entry entry : object.getBlocks()) { IrisBlockVector position = entry.getKey(); LocalPosition unsigned = unsignedPosition(position, object); if (!inside(unsigned, target)) { @@ -523,7 +523,7 @@ final class JigsawStudioObjectResizer { } } - private record BlockRelocation(LocalPosition target, PlatformBlockState state, boolean present) { + private record BlockRelocation(LocalPosition target, NativeBlockState state, boolean present) { private BlockRelocation { Objects.requireNonNull(target, "Planar Jigsaw Studio connector block target"); if (present) { diff --git a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRenderer.java b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRenderer.java index 2944bf81c..f3423bef9 100644 --- a/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRenderer.java +++ b/core/src/main/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRenderer.java @@ -5,7 +5,7 @@ import art.arcane.iris.structure.jigsaw.IrisJigsawConnector; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.structure.object.IrisObjectRotation; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.iris.world.task.J; @@ -193,9 +193,9 @@ public final class JigsawStudioPreviewRenderer { ) throws IOException { IrisObject object = piece.getObject(); IrisObjectRotation rotation = piece.getRotation(); - for (Map.Entry entry : object.getBlocks()) { + for (Map.Entry entry : object.getBlocks()) { IrisBlockVector rotated = rotation.rotate(entry.getKey()); - PlatformBlockState state = rotation.rotate(entry.getValue(), 0, 0, 0); + NativeBlockState state = rotation.rotate(entry.getValue(), 0, 0, 0); putState( blocks, new BlockPosition( @@ -225,8 +225,8 @@ public final class JigsawStudioPreviewRenderer { connector.getPosition().getY() - object.getCenter().getBlockY(), connector.getPosition().getZ() - object.getCenter().getBlockZ()); IrisBlockVector rotated = rotation.rotate(signed); - PlatformBlockState source = B.getStateOrNull(connector.getFinalState(), false); - PlatformBlockState state = source == null ? null : rotation.rotate(source, 0, 0, 0); + NativeBlockState source = B.getStateOrNull(connector.getFinalState(), false); + NativeBlockState state = source == null ? null : rotation.rotate(source, 0, 0, 0); putState( blocks, new BlockPosition( @@ -241,7 +241,7 @@ public final class JigsawStudioPreviewRenderer { private static void putState( Map blocks, BlockPosition position, - PlatformBlockState state, + NativeBlockState state, String source ) throws IOException { if (state == null || state.key() == null || state.key().isBlank()) { diff --git a/core/src/main/java/art/arcane/iris/studio/tree/TreePlausibilizer.java b/core/src/main/java/art/arcane/iris/studio/tree/TreePlausibilizer.java index 8772656c9..a7e2054c5 100644 --- a/core/src/main/java/art/arcane/iris/studio/tree/TreePlausibilizer.java +++ b/core/src/main/java/art/arcane/iris/studio/tree/TreePlausibilizer.java @@ -20,7 +20,7 @@ package art.arcane.iris.studio.tree; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.RNG; @@ -95,7 +95,7 @@ public final class TreePlausibilizer { } private static Result run(IrisObject obj, boolean mutate, long seed, int reach) { - VectorMap blocks = obj.getBlocks(); + VectorMap blocks = obj.getBlocks(); Workspace ws = new Workspace(blocks); int totalLeaves = ws.leaves.size(); @@ -132,9 +132,9 @@ public final class TreePlausibilizer { List sortedLeaves = new ArrayList<>(ws.leaves); Collections.sort(sortedLeaves); for (long leaf : sortedLeaves) { - PlatformBlockState state = ws.positions.get(leaf); + NativeBlockState state = ws.positions.get(leaf); Integer d = finalDistances.get(leaf); - PlatformBlockState next; + NativeBlockState next; if (d != null) { next = state.withProperty("persistent", "false") .withProperty("distance", String.valueOf(Math.max(1, Math.min(MAX_DISTANCE, d)))); @@ -170,7 +170,7 @@ public final class TreePlausibilizer { ); } - private static PlatformBlockState pickBranchTemplate(Map positions, Set wood) { + private static NativeBlockState pickBranchTemplate(Map positions, Set wood) { if (wood.isEmpty()) { return null; } @@ -188,9 +188,9 @@ public final class TreePlausibilizer { } } Map stateCounts = new HashMap<>(); - Map statesByKey = new HashMap<>(); + Map statesByKey = new HashMap<>(); for (long key : wood) { - PlatformBlockState state = positions.get(key); + NativeBlockState state = positions.get(key); if (!IrisProceduralBlocks.materialKey(state).equals(dominant)) { continue; } @@ -356,7 +356,7 @@ public final class TreePlausibilizer { return dist; } - private static boolean isWood(PlatformBlockState state) { + private static boolean isWood(NativeBlockState state) { String material = IrisProceduralBlocks.materialKey(state); return material.endsWith("_log") || material.endsWith("_wood") @@ -364,12 +364,12 @@ public final class TreePlausibilizer { || STEM_WOOD.contains(material); } - private static boolean isDecayableLeaf(PlatformBlockState state) { + private static boolean isDecayableLeaf(NativeBlockState state) { return IrisProceduralBlocks.hasProperty(state, "distance") && IrisProceduralBlocks.hasProperty(state, "persistent"); } - private static PlatformBlockState axisVariant(PlatformBlockState template, int dx, int dy, int dz) { + private static NativeBlockState axisVariant(NativeBlockState template, int dx, int dy, int dz) { if (!IrisProceduralBlocks.hasProperty(template, "axis")) { return template; } @@ -429,20 +429,20 @@ public final class TreePlausibilizer { return new IrisBlockVector(xyz[0], xyz[1], xyz[2]); } - private record WoodPlacement(long key, PlatformBlockState state) { + private record WoodPlacement(long key, NativeBlockState state) { } - private record LeafRewrite(long key, PlatformBlockState state) { + private record LeafRewrite(long key, NativeBlockState state) { } private static final class Workspace { - private final Map positions; + private final Map positions; private final Set wood = new HashSet<>(); private final Set leaves = new HashSet<>(); private final List placements = new ArrayList<>(); private Map distances = new HashMap<>(); private Set unreached = new HashSet<>(); - private PlatformBlockState template; + private NativeBlockState template; private GapTracker tracker; private int minX = Integer.MAX_VALUE; private int minY = Integer.MAX_VALUE; @@ -454,11 +454,11 @@ public final class TreePlausibilizer { private int converted; private int branches; - private Workspace(VectorMap blocks) { + private Workspace(VectorMap blocks) { positions = new HashMap<>(blocks.size() * 2); - for (Map.Entry entry : blocks) { + for (Map.Entry entry : blocks) { IrisBlockVector pos = entry.getKey(); - PlatformBlockState state = entry.getValue(); + NativeBlockState state = entry.getValue(); long key = packKey(pos); positions.put(key, state); int x = pos.getBlockX(); @@ -640,12 +640,12 @@ public final class TreePlausibilizer { if (wood.contains(key)) { return; } - PlatformBlockState existing = positions.get(key); + NativeBlockState existing = positions.get(key); boolean convertLeaf = existing != null && leaves.contains(key); if (existing != null && !convertLeaf) { return; } - PlatformBlockState state = axisVariant(template, dx, dy, dz); + NativeBlockState state = axisVariant(template, dx, dy, dz); positions.put(key, state); wood.add(key); if (convertLeaf) { diff --git a/core/src/main/java/art/arcane/iris/studio/view/GuiMarker.java b/core/src/main/java/art/arcane/iris/studio/view/GuiMarker.java deleted file mode 100644 index da7e086ed..000000000 --- a/core/src/main/java/art/arcane/iris/studio/view/GuiMarker.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.studio.view; - -public record GuiMarker(String label, double worldX, double worldY, double worldZ, double health, double maxHealth) { - public static GuiMarker player(String name, double worldX, double worldZ) { - return new GuiMarker(name, worldX, 0, worldZ, 0, 0); - } - - public static GuiMarker entity(String label, double worldX, double worldY, double worldZ, double health, double maxHealth) { - return new GuiMarker(label, worldX, worldY, worldZ, health, maxHealth); - } -} diff --git a/core/src/main/java/art/arcane/iris/studio/view/GuiOverlay.java b/core/src/main/java/art/arcane/iris/studio/view/GuiOverlay.java index da4a307fa..da4f3f0b6 100644 --- a/core/src/main/java/art/arcane/iris/studio/view/GuiOverlay.java +++ b/core/src/main/java/art/arcane/iris/studio/view/GuiOverlay.java @@ -18,15 +18,17 @@ package art.arcane.iris.studio.view; +import art.arcane.volmlib.nativelib.view.WorldMarker; + import art.arcane.iris.studio.render.RenderType; import java.util.List; import java.util.function.Consumer; public interface GuiOverlay { - List players(); + List players(); - void requestEntities(Consumer> sink); + void requestEntities(Consumer> sink); void teleport(double worldX, double worldZ); diff --git a/core/src/main/java/art/arcane/iris/studio/view/VisionGUI.java b/core/src/main/java/art/arcane/iris/studio/view/VisionGUI.java index be761bc3b..39218c83f 100644 --- a/core/src/main/java/art/arcane/iris/studio/view/VisionGUI.java +++ b/core/src/main/java/art/arcane/iris/studio/view/VisionGUI.java @@ -18,6 +18,8 @@ package art.arcane.iris.studio.view; +import art.arcane.volmlib.nativelib.view.WorldMarker; + import art.arcane.iris.localization.DesktopUiMessages; import art.arcane.iris.localization.IrisLanguage; import art.arcane.iris.generation.runtime.Engine; @@ -129,8 +131,8 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi private JToggleButton entitiesToggle; private JToggleButton followToggle; private RenderType currentType; - private List players; - private List entities; + private List players; + private List entities; private HoverInfo hoverInfo; private Point cursor; private double centerX; @@ -428,7 +430,7 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi private void renderMarkers(Graphics2D canvas) { if (entitiesVisible) { - for (GuiMarker marker : entities) { + for (WorldMarker marker : entities) { int screenX = (int) Math.round(worldToScreenX(marker.worldX())); int screenY = (int) Math.round(worldToScreenZ(marker.worldZ())); canvas.setColor(MOB_COLOR); @@ -436,7 +438,7 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi } } - for (GuiMarker marker : players) { + for (WorldMarker marker : players) { int screenX = (int) Math.round(worldToScreenX(marker.worldX())); int screenY = (int) Math.round(worldToScreenZ(marker.worldZ())); canvas.setColor(new Color(81, 201, 128, 48)); @@ -460,9 +462,9 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi } double worldX = screenToWorldX(point.x); double worldZ = screenToWorldZ(point.y); - GuiMarker nearest = null; + WorldMarker nearest = null; double nearestDistance = Double.MAX_VALUE; - for (GuiMarker marker : entities) { + for (WorldMarker marker : entities) { double deltaX = marker.worldX() - worldX; double deltaZ = marker.worldZ() - worldZ; double distance = deltaX * deltaX + deltaZ * deltaZ; @@ -818,7 +820,7 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi return; } try { - List nextPlayers = overlay.players(); + List nextPlayers = overlay.players(); players = nextPlayers == null ? List.of() : List.copyOf(nextPlayers); if (entitiesVisible) { overlay.requestEntities(next -> EventQueue.invokeLater(() -> { @@ -831,7 +833,7 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi entities = List.of(); } if (follow && !players.isEmpty()) { - GuiMarker player = players.get(0); + WorldMarker player = players.get(0); if (Math.abs(centerX - player.worldX()) > 0.5D || Math.abs(centerZ - player.worldZ()) > 0.5D) { centerX = player.worldX(); centerZ = player.worldZ(); @@ -915,7 +917,7 @@ public final class VisionGUI extends JPanel implements MouseWheelListener, KeyLi follow = false; notifyUser(IrisLanguage.plain(DesktopUiMessages.VISION_NO_PLAYER)); } else if (follow) { - GuiMarker player = players.get(0); + WorldMarker player = players.get(0); centerX = player.worldX(); centerZ = player.worldZ(); requestRender(); diff --git a/core/src/main/java/art/arcane/iris/world/IrisCreator.java b/core/src/main/java/art/arcane/iris/world/IrisCreator.java index 57e48e218..e8d90b201 100644 --- a/core/src/main/java/art/arcane/iris/world/IrisCreator.java +++ b/core/src/main/java/art/arcane/iris/world/IrisCreator.java @@ -18,6 +18,8 @@ package art.arcane.iris.world; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.server.NativeServerDiagnostics; import art.arcane.iris.world.runtime.TransientWorldCleanupSupport; import art.arcane.iris.world.runtime.WorldRuntimeControlService; import art.arcane.iris.world.history.GenerationHistory; @@ -718,19 +720,8 @@ public class IrisCreator { } private static boolean containsCreateWorldUnsupportedOperation(Throwable throwable) { - Throwable cursor = throwable; - while (cursor != null) { - if (cursor instanceof UnsupportedOperationException) { - for (StackTraceElement element : cursor.getStackTrace()) { - if ("org.bukkit.craftbukkit.CraftServer".equals(element.getClassName()) - && "createWorld".equals(element.getMethodName())) { - return true; - } - } - } - cursor = cursor.getCause(); - } - return false; + return NativeAdapters.find(NativeServerDiagnostics.class) + .map(diagnostics -> diagnostics.isUnsupportedWorldCreation(throwable)).orElse(false); } private static boolean containsMissingDimensionTypes(Throwable throwable) { diff --git a/core/src/main/java/art/arcane/iris/world/IrisTimeBlock.java b/core/src/main/java/art/arcane/iris/world/IrisTimeBlock.java index 55df3d58f..8aa6e3942 100644 --- a/core/src/main/java/art/arcane/iris/world/IrisTimeBlock.java +++ b/core/src/main/java/art/arcane/iris/world/IrisTimeBlock.java @@ -20,7 +20,7 @@ package art.arcane.iris.world; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.Snippet; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import lombok.Data; @Snippet("time-block") @@ -33,7 +33,7 @@ public class IrisTimeBlock { @Description("The ending hour. Set both to the same number for any time. If they are both set to -1, it will always be not allowed.") private double endHour = 0; - public boolean isWithin(PlatformWorld world) { + public boolean isWithin(NativeWorld world) { return isWithin(((world.getTime() / 1000D) + 6) % 24); } diff --git a/core/src/main/java/art/arcane/iris/world/IrisWeather.java b/core/src/main/java/art/arcane/iris/world/IrisWeather.java index 428137812..ded53817a 100644 --- a/core/src/main/java/art/arcane/iris/world/IrisWeather.java +++ b/core/src/main/java/art/arcane/iris/world/IrisWeather.java @@ -19,7 +19,7 @@ package art.arcane.iris.world; import art.arcane.volmlib.util.documentation.Description; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; @Description("Represents a weather type") public enum IrisWeather { @@ -35,7 +35,7 @@ public enum IrisWeather { @Description("Any weather") ANY; - public boolean is(PlatformWorld world) { + public boolean is(NativeWorld world) { return switch (this) { case NONE -> !world.isStorming() && !world.isThundering(); case DOWNFALL -> world.isStorming(); diff --git a/core/src/main/java/art/arcane/iris/world/IrisWorld.java b/core/src/main/java/art/arcane/iris/world/IrisWorld.java index 413139704..0602c7357 100644 --- a/core/src/main/java/art/arcane/iris/world/IrisWorld.java +++ b/core/src/main/java/art/arcane/iris/world/IrisWorld.java @@ -18,7 +18,7 @@ package art.arcane.iris.world; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import lombok.AccessLevel; import lombok.Builder; import lombok.Data; @@ -39,7 +39,7 @@ public class IrisWorld { @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) private long seed; - private PlatformWorld platformWorld; + private NativeWorld platformWorld; private int minHeight; private int maxHeight; diff --git a/core/src/main/java/art/arcane/iris/world/IrisWorlds.java b/core/src/main/java/art/arcane/iris/world/IrisWorlds.java index 20f101298..725f55a40 100644 --- a/core/src/main/java/art/arcane/iris/world/IrisWorlds.java +++ b/core/src/main/java/art/arcane/iris/world/IrisWorlds.java @@ -6,7 +6,7 @@ import art.arcane.iris.world.lifecycle.MissingWorldStorageLog; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.PackDownloader; import art.arcane.iris.studio.StudioSVC; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatforms; diff --git a/core/src/main/java/art/arcane/iris/world/entity/IrisEffect.java b/core/src/main/java/art/arcane/iris/world/entity/IrisEffect.java index 1c27d9d02..0c39dd8e0 100644 --- a/core/src/main/java/art/arcane/iris/world/entity/IrisEffect.java +++ b/core/src/main/java/art/arcane/iris/world/entity/IrisEffect.java @@ -20,7 +20,7 @@ package art.arcane.iris.world.entity; import art.arcane.iris.command.IrisCommandRegistry; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.DependsOn; import art.arcane.volmlib.util.documentation.Description; @@ -32,7 +32,7 @@ import art.arcane.iris.pack.schema.annotation.Snippet; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.scheduling.ChronoLatch; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import art.arcane.iris.world.task.J; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/core/src/main/java/art/arcane/iris/world/entity/IrisEntity.java b/core/src/main/java/art/arcane/iris/world/entity/IrisEntity.java index 432c2c6dd..6baff17b4 100644 --- a/core/src/main/java/art/arcane/iris/world/entity/IrisEntity.java +++ b/core/src/main/java/art/arcane/iris/world/entity/IrisEntity.java @@ -18,6 +18,8 @@ package art.arcane.iris.world.entity; +import art.arcane.volmlib.nativelib.entity.NativeEntityOptions; + import art.arcane.iris.command.IrisCommand; import art.arcane.iris.generation.decoration.IrisSurface; import art.arcane.iris.world.loot.InventorySlotType; @@ -41,9 +43,9 @@ import art.arcane.iris.pack.schema.annotation.RegistryListEntityType; import art.arcane.iris.pack.schema.annotation.RegistryListSpecialEntity; import art.arcane.iris.pack.schema.annotation.Required; import art.arcane.iris.platform.bukkit.BukkitPlatform; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import art.arcane.iris.platform.bukkit.BukkitWorld; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.volmlib.util.collection.KList; import art.arcane.iris.localization.C; import art.arcane.volmlib.util.math.M; @@ -83,7 +85,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import static art.arcane.iris.platform.bukkit.registry.Particles.ITEM; +import static art.arcane.iris.platform.registry.Particles.ITEM; @SuppressWarnings("ALL") @Accessors(chain = true) @@ -92,7 +94,7 @@ import static art.arcane.iris.platform.bukkit.registry.Particles.ITEM; @Description("Represents an iris entity.") @Data @EqualsAndHashCode(callSuper = false) -public class IrisEntity extends IrisRegistrant { +public class IrisEntity extends IrisRegistrant implements NativeEntityOptions { @Required @RegistryListEntityType @Description("The namespaced key of the entity type to spawn, such as minecraft:zombie or alexsmobs:grizzly_bear. To spawn a mob from another plugin or provider, set this type to unknown and define the special type.") @@ -384,7 +386,7 @@ public class IrisEntity extends IrisRegistrant { if (rawCommands.isNotEmpty()) { final Location fat = at; - final PlatformWorld commandWorld = new BukkitWorld(fat.getWorld()); + final NativeWorld commandWorld = new BukkitWorld(fat.getWorld()); rawCommands.forEach(r -> r.run(commandWorld, fat.getBlockX(), fat.getBlockY(), fat.getBlockZ())); } diff --git a/core/src/main/java/art/arcane/iris/world/entity/IrisEntitySpawn.java b/core/src/main/java/art/arcane/iris/world/entity/IrisEntitySpawn.java index 2785afc69..f6ed7a04b 100644 --- a/core/src/main/java/art/arcane/iris/world/entity/IrisEntitySpawn.java +++ b/core/src/main/java/art/arcane/iris/world/entity/IrisEntitySpawn.java @@ -26,7 +26,7 @@ import art.arcane.volmlib.util.math.Rarity; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.platform.bukkit.BukkitWorldBinding; import art.arcane.iris.platform.bukkit.BukkitPlatform; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.LootResolver; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/world/entity/IrisPotionEffect.java b/core/src/main/java/art/arcane/iris/world/entity/IrisPotionEffect.java index a11878c48..bd542506d 100644 --- a/core/src/main/java/art/arcane/iris/world/entity/IrisPotionEffect.java +++ b/core/src/main/java/art/arcane/iris/world/entity/IrisPotionEffect.java @@ -18,7 +18,7 @@ package art.arcane.iris.world.entity; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; import art.arcane.iris.pack.schema.annotation.MinNumber; diff --git a/core/src/main/java/art/arcane/iris/world/entity/IrisSpawner.java b/core/src/main/java/art/arcane/iris/world/entity/IrisSpawner.java index c382f4efd..adb767637 100644 --- a/core/src/main/java/art/arcane/iris/world/entity/IrisSpawner.java +++ b/core/src/main/java/art/arcane/iris/world/entity/IrisSpawner.java @@ -33,7 +33,7 @@ import art.arcane.iris.pack.loading.IrisRegistrant; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.volmlib.util.collection.KList; import lombok.AllArgsConstructor; import lombok.Data; @@ -101,12 +101,12 @@ public class IrisSpawner extends IrisRegistrant { }; } - public boolean isValid(PlatformWorld world) { + public boolean isValid(NativeWorld world) { return timeBlock.isWithin(world) && weather.is(world); } public boolean canSpawn(Engine engine) { - PlatformWorld world = engine.getWorld().platformWorld(); + NativeWorld world = engine.getWorld().platformWorld(); if (world == null || !isValid(world)) return false; diff --git a/core/src/main/java/art/arcane/iris/world/history/BoundaryColumnGeometry.java b/core/src/main/java/art/arcane/iris/world/history/BoundaryColumnGeometry.java index ba155e143..eadd195ca 100644 --- a/core/src/main/java/art/arcane/iris/world/history/BoundaryColumnGeometry.java +++ b/core/src/main/java/art/arcane/iris/world/history/BoundaryColumnGeometry.java @@ -1,5 +1,7 @@ package art.arcane.iris.world.history; +import art.arcane.volmlib.nativelib.terrain.NativeBlockColumn; + import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -9,7 +11,7 @@ import java.util.Map; import java.util.Objects; import java.util.TreeSet; -public final class BoundaryColumnGeometry { +public final class BoundaryColumnGeometry implements NativeBlockColumn { public static final int MAXIMUM_HEIGHT = 65_536; private static final Comparator VOXEL_ORDER = Comparator.comparing(Voxel::stateKey) .thenComparing(Voxel::phase) @@ -104,6 +106,11 @@ public final class BoundaryColumnGeometry { return palette.get(paletteIndices[run]); } + @Override + public String stateKeyAt(int worldY) { + return voxelAt(worldY).stateKey(); + } + public Voxel voxelAt(int worldY) { long offset = (long) worldY - minimumY; if (offset < 0 || offset >= height()) { diff --git a/core/src/main/java/art/arcane/iris/world/history/GenerationEpochContractFactory.java b/core/src/main/java/art/arcane/iris/world/history/GenerationEpochContractFactory.java index 5e597a3ce..5bc7aa480 100644 --- a/core/src/main/java/art/arcane/iris/world/history/GenerationEpochContractFactory.java +++ b/core/src/main/java/art/arcane/iris/world/history/GenerationEpochContractFactory.java @@ -1,7 +1,7 @@ package art.arcane.iris.world.history; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisDimensionMode; import art.arcane.iris.generation.terrain.IrisDimensionModeType; diff --git a/core/src/main/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouter.java b/core/src/main/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouter.java index 481167759..c307845a4 100644 --- a/core/src/main/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouter.java +++ b/core/src/main/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouter.java @@ -1,5 +1,10 @@ package art.arcane.iris.world.history; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRoute; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationScope; + import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.generation.runtime.IrisEngineMantle; @@ -1316,7 +1321,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable { } } - public static final class RuntimeRoute implements AutoCloseable { + public static final class RuntimeRoute implements NativeGenerationRoute { private final GenerationHistoryRuntimeRouter router; private final GenerationHistory.GenerationStage stage; private final RuntimeLease lease; @@ -1378,7 +1383,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable { return claimGeneratedSemantics((x, y, z) -> true); } - public boolean claimGeneratedSemantics(GenerationSemanticCapture.CaveSpace caveSpace) throws IOException { + public boolean claimGeneratedSemantics(NativeBlockPositionPredicate caveSpace) throws IOException { if (router.scopedRoute.get() != this) { throw new IllegalStateException( "Generation semantics must be captured inside this route's runtime scope." @@ -1484,7 +1489,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable { activeScopes--; } - public static final class RuntimeScope implements AutoCloseable { + public static final class RuntimeScope implements NativeGenerationScope { private final RuntimeRoute route; private final IrisEngine.GenerationRuntimeScope runtimeScope; private final Thread owner; @@ -1627,7 +1632,7 @@ public final class GenerationHistoryRuntimeRouter implements AutoCloseable { } } - public static final class CoordinateScope implements AutoCloseable { + public static final class CoordinateScope implements NativeGenerationScope { private final int blockX; private final int blockZ; private final RuntimeRoute route; diff --git a/core/src/main/java/art/arcane/iris/world/history/GenerationRegistryContractFactory.java b/core/src/main/java/art/arcane/iris/world/history/GenerationRegistryContractFactory.java index 2f5368a57..c84f34c9d 100644 --- a/core/src/main/java/art/arcane/iris/world/history/GenerationRegistryContractFactory.java +++ b/core/src/main/java/art/arcane/iris/world/history/GenerationRegistryContractFactory.java @@ -4,8 +4,8 @@ import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.validation.KeyStatus; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.ResourceLoader; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.PackDirectoryResolver; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiomeCustom; diff --git a/core/src/main/java/art/arcane/iris/world/history/GenerationSemanticCapture.java b/core/src/main/java/art/arcane/iris/world/history/GenerationSemanticCapture.java index 4979d76cd..5849bc0ad 100644 --- a/core/src/main/java/art/arcane/iris/world/history/GenerationSemanticCapture.java +++ b/core/src/main/java/art/arcane/iris/world/history/GenerationSemanticCapture.java @@ -1,5 +1,7 @@ package art.arcane.iris.world.history; +import art.arcane.volmlib.nativelib.terrain.NativeBlockPositionPredicate; + import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.structure.placement.IrisStructureLocator; @@ -36,7 +38,7 @@ public final class GenerationSemanticCapture { public static ChunkGenerationSemantics capture( Engine engine, GenerationHistory.GenerationStage stage, - CaveSpace caveSpace + NativeBlockPositionPredicate caveSpace ) { Engine requiredEngine = Objects.requireNonNull(engine, "engine"); GenerationHistory.GenerationStage requiredStage = Objects.requireNonNull(stage, "stage"); @@ -63,7 +65,7 @@ public final class GenerationSemanticCapture { int chunkX, int chunkZ, long activationId, - CaveSpace caveSpace + NativeBlockPositionPredicate caveSpace ) { Objects.requireNonNull(caveSpace, "cave space"); Engine requiredEngine = Objects.requireNonNull(engine, "engine"); @@ -131,14 +133,14 @@ public final class GenerationSemanticCapture { int chunkX, int chunkZ, ChunkGenerationSemantics.Builder semantics, - CaveSpace caveSpace + NativeBlockPositionPredicate caveSpace ) { Mantle mantle = engine.getMantle().getMantle(); int minimumX = Math.multiplyExact(chunkX, CHUNK_SIZE); int minimumZ = Math.multiplyExact(chunkZ, CHUNK_SIZE); BitSet resolvedCaves = new BitSet(); mantle.iterateChunk(chunkX, chunkZ, MatterCavern.class, (localX, y, localZ, cavern) -> { - if (!caveSpace.isOpen(localX, y, localZ)) { + if (!caveSpace.test(localX, y, localZ)) { return; } captureCave( @@ -152,7 +154,7 @@ public final class GenerationSemanticCapture { ); }); mantle.iterateChunk(chunkX, chunkZ, HydrologyCaveCell.class, (localX, y, localZ, cell) -> { - if (!caveSpace.isOpen(localX, y, localZ)) { + if (!caveSpace.test(localX, y, localZ)) { return; } int blockX = minimumX + localX; @@ -254,9 +256,4 @@ public final class GenerationSemanticCapture { } } - @FunctionalInterface - public interface CaveSpace { - boolean isOpen(int localX, int internalY, int localZ); - } - } diff --git a/core/src/main/java/art/arcane/iris/world/history/NativeBiomeSpawnSelection.java b/core/src/main/java/art/arcane/iris/world/history/NativeBiomeSpawnSelection.java index d85093d71..4d8a6fce6 100644 --- a/core/src/main/java/art/arcane/iris/world/history/NativeBiomeSpawnSelection.java +++ b/core/src/main/java/art/arcane/iris/world/history/NativeBiomeSpawnSelection.java @@ -7,11 +7,12 @@ import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.runtime.Engine; +import art.arcane.volmlib.nativelib.terrain.NativeSpawnSelection; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -public record NativeBiomeSpawnSelection(Mode mode, String derivativeKey) { +public record NativeBiomeSpawnSelection(Mode mode, String derivativeKey) implements NativeSpawnSelection { private static final NativeBiomeSpawnSelection CURRENT = new NativeBiomeSpawnSelection(Mode.CURRENT, ""); private static final NativeBiomeSpawnSelection NONE = new NativeBiomeSpawnSelection(Mode.NONE, ""); private static final NativeBiomeSpawnSelection LOADING = new NativeBiomeSpawnSelection(Mode.LOADING, ""); @@ -60,10 +61,4 @@ public record NativeBiomeSpawnSelection(Mode mode, String derivativeKey) { return Map.copyOf(derivatives); } - public enum Mode { - CURRENT, - RETAINED, - NONE, - LOADING - } } diff --git a/core/src/main/java/art/arcane/iris/world/history/NativeTerrainReceipt.java b/core/src/main/java/art/arcane/iris/world/history/NativeTerrainReceipt.java index 1231b2b81..45245b9e8 100644 --- a/core/src/main/java/art/arcane/iris/world/history/NativeTerrainReceipt.java +++ b/core/src/main/java/art/arcane/iris/world/history/NativeTerrainReceipt.java @@ -15,8 +15,6 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; public final class NativeTerrainReceipt { - public static final String STRUCTURE_ACTIVATION_KEY = "iris:structure_activation"; - public static final String NBT_KEY = "iris:natural_terrain"; private static final int MAGIC = 0x4952544E; private static final int VERSION = 1; private static final int MAXIMUM_BYTES = 64 * 1024 * 1024; diff --git a/core/src/main/java/art/arcane/iris/world/history/SavedTerrainCapturePolicy.java b/core/src/main/java/art/arcane/iris/world/history/SavedTerrainCapturePolicy.java new file mode 100644 index 000000000..5dd915c7f --- /dev/null +++ b/core/src/main/java/art/arcane/iris/world/history/SavedTerrainCapturePolicy.java @@ -0,0 +1,47 @@ +package art.arcane.iris.world.history; + +import art.arcane.volmlib.nativelib.terrain.NativeTerrainReceiptStorage; + +import art.arcane.volmlib.nativelib.terrain.TerrainSnapshotPolicy; + +import java.io.IOException; +import java.nio.file.Path; + +public class SavedTerrainCapturePolicy implements TerrainSnapshotPolicy { + public static final SavedTerrainCapturePolicy INSTANCE = new SavedTerrainCapturePolicy(); + + protected SavedTerrainCapturePolicy() { + } + + @Override + public boolean hasTerrain(String status) { + return SavedTerrainChunk.hasTerrain(status); + } + + @Override + public String receiptKey() { + return NativeTerrainReceiptStorage.NBT_KEY; + } + + @Override + public String activationKey() { + return NativeTerrainReceiptStorage.STRUCTURE_ACTIVATION_KEY; + } + + @Override + public SavedTerrainChunk readPersisted(CaptureTarget target) throws IOException { + return SavedTerrainChunk.read(target.worldFolder(), target.chunkX(), target.chunkZ(), + target.minimumY(), target.height()); + } + + @Override + public SavedTerrainChunk decodeNbt(byte[] bytes, CaptureTarget target) throws IOException { + return SavedTerrainChunk.readNbt(bytes, target.chunkX(), target.chunkZ(), target.minimumY(), target.height()); + } + + @Override + public void verifyCheckpoint(Path worldFolder, CheckpointData checkpoint) throws IOException { + SavedTerrainChunk.verifyCheckpoint(worldFolder, checkpoint.chunkX(), checkpoint.chunkZ(), + checkpoint.status(), checkpoint.receipt(), checkpoint.activation()); + } +} diff --git a/core/src/main/java/art/arcane/iris/world/history/SavedTerrainChunkReader.java b/core/src/main/java/art/arcane/iris/world/history/SavedTerrainChunkReader.java index 23a593643..3e9ab8a5f 100644 --- a/core/src/main/java/art/arcane/iris/world/history/SavedTerrainChunkReader.java +++ b/core/src/main/java/art/arcane/iris/world/history/SavedTerrainChunkReader.java @@ -1,8 +1,10 @@ package art.arcane.iris.world.history; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainReceiptStorage; + import art.arcane.volmlib.util.nbt.io.NBTUtil; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport; import art.arcane.volmlib.util.nbt.tag.CompoundTag; import art.arcane.volmlib.util.nbt.tag.ByteArrayTag; @@ -61,9 +63,9 @@ final class SavedTerrainChunkReader { if (!SavedTerrainChunk.hasTerrain(status)) { throw new IOException("Saved chunk " + chunkX + "," + chunkZ + " has no terrain at native status " + status); } - Tag receiptTag = root.get(NativeTerrainReceipt.NBT_KEY); + Tag receiptTag = root.get(NativeTerrainReceiptStorage.NBT_KEY); if (root.get("ChunkBukkitValues") instanceof CompoundTag bukkitValues) { - receiptTag = bukkitValues.get(NativeTerrainReceipt.NBT_KEY); + receiptTag = bukkitValues.get(NativeTerrainReceiptStorage.NBT_KEY); } if (receiptTag != null) { if (!(receiptTag instanceof ByteArrayTag bytes)) { @@ -123,9 +125,9 @@ final class SavedTerrainChunkReader { } private static long structureActivation(CompoundTag root) throws IOException { - Tag activation = root.get(NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY); + Tag activation = root.get(NativeTerrainReceiptStorage.STRUCTURE_ACTIVATION_KEY); if (root.get("ChunkBukkitValues") instanceof CompoundTag values) { - activation = values.get(NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY); + activation = values.get(NativeTerrainReceiptStorage.STRUCTURE_ACTIVATION_KEY); } if (activation == null) { return 0; @@ -137,9 +139,9 @@ final class SavedTerrainChunkReader { } private static byte[] receipt(CompoundTag root) throws IOException { - Tag receipt = root.get(NativeTerrainReceipt.NBT_KEY); + Tag receipt = root.get(NativeTerrainReceiptStorage.NBT_KEY); if (root.get("ChunkBukkitValues") instanceof CompoundTag values) { - receipt = values.get(NativeTerrainReceipt.NBT_KEY); + receipt = values.get(NativeTerrainReceiptStorage.NBT_KEY); } if (receipt == null) { return null; @@ -174,7 +176,7 @@ final class SavedTerrainChunkReader { state = wrapped.get(""); } if (format == MCABlockStateCodecSupport.Format.LOWERCASE && state instanceof StringTag name) { - PlatformBlockState resolved = IrisPlatforms.get().registries().blockOrNull(name.getValue()); + NativeBlockState resolved = IrisPlatforms.get().registries().blockOrNull(name.getValue()); if (resolved == null) { throw new IOException("Saved block state cannot be resolved: " + name.getValue()); } diff --git a/core/src/main/java/art/arcane/iris/world/history/TerrainNativeBlockKeys.java b/core/src/main/java/art/arcane/iris/world/history/TerrainNativeBlockKeys.java new file mode 100644 index 000000000..304921926 --- /dev/null +++ b/core/src/main/java/art/arcane/iris/world/history/TerrainNativeBlockKeys.java @@ -0,0 +1,20 @@ +package art.arcane.iris.world.history; + +import art.arcane.iris.spi.IrisPlatforms; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; + +public final class TerrainNativeBlockKeys { + private TerrainNativeBlockKeys() { + } + + public static String placementKey(String key) { + if (!key.startsWith("minecraft:") && IrisPlatforms.isBound()) { + NativeBlockState logicalState = IrisPlatforms.get().registries().blockOrNull(key); + NativeBlockState baseState = logicalState == null ? null : logicalState.placementBaseState(); + if (baseState != null) { + return baseState.key(); + } + } + return key; + } +} diff --git a/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilityResolution.java b/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilityResolution.java index a04a96a2f..362e583bf 100644 --- a/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilityResolution.java +++ b/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilityResolution.java @@ -1,7 +1,5 @@ package art.arcane.iris.world.lifecycle; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.function.Predicate; @@ -9,170 +7,6 @@ final class CapabilityResolution { private CapabilityResolution() { } - static Method resolveCreateLevelMethod(Class owner) throws NoSuchMethodException { - Method current = resolveMethod(owner, "createLevel", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 3 - && "LevelStem".equals(params[0].getSimpleName()) - && "WorldLoadingInfoAndData".equals(params[1].getSimpleName()) - && "WorldDataAndGenSettings".equals(params[2].getSimpleName()); - }); - if (current != null) { - return current; - } - - Method legacy = resolveMethod(owner, "createLevel", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 4 - && "LevelStem".equals(params[0].getSimpleName()) - && "WorldLoadingInfo".equals(params[1].getSimpleName()) - && "LevelStorageAccess".equals(params[2].getSimpleName()) - && "PrimaryLevelData".equals(params[3].getSimpleName()); - }); - if (legacy != null) { - return legacy; - } - - throw new NoSuchMethodException(owner.getName() + "#createLevel"); - } - - static Method resolveLevelStorageAccessMethod(Class owner) throws NoSuchMethodException { - Method exactValidate = resolveMethod(owner, "validateAndCreateAccess", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 2 - && String.class.equals(params[0]) - && "ResourceKey".equals(params[1].getSimpleName()) - && "LevelStorageAccess".equals(method.getReturnType().getSimpleName()); - }); - if (exactValidate != null) { - return exactValidate; - } - - Method oneArgValidate = resolveMethod(owner, "validateAndCreateAccess", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 - && String.class.equals(params[0]) - && "LevelStorageAccess".equals(method.getReturnType().getSimpleName()); - }); - if (oneArgValidate != null) { - return oneArgValidate; - } - - Method exactCreate = resolveMethod(owner, "createAccess", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 2 - && String.class.equals(params[0]) - && "ResourceKey".equals(params[1].getSimpleName()) - && "LevelStorageAccess".equals(method.getReturnType().getSimpleName()); - }); - if (exactCreate != null) { - return exactCreate; - } - - Method oneArgCreate = resolveMethod(owner, "createAccess", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 - && String.class.equals(params[0]) - && "LevelStorageAccess".equals(method.getReturnType().getSimpleName()); - }); - if (oneArgCreate != null) { - return oneArgCreate; - } - - throw new NoSuchMethodException(owner.getName() + "#validateAndCreateAccess/createAccess"); - } - - static Method resolvePaperWorldDataMethod(Class owner) throws NoSuchMethodException { - Method current = resolveMethod(owner, "loadWorldData", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 3 - && "MinecraftServer".equals(params[0].getSimpleName()) - && "ResourceKey".equals(params[1].getSimpleName()) - && String.class.equals(params[2]) - && "LoadedWorldData".equals(method.getReturnType().getSimpleName()); - }); - if (current != null) { - return current; - } - - Method legacy = resolveMethod(owner, "getLevelData", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && "LevelStorageAccess".equals(params[0].getSimpleName()); - }); - if (legacy != null) { - return legacy; - } - - throw new NoSuchMethodException(owner.getName() + "#loadWorldData/getLevelData"); - } - - static Constructor resolveWorldLoadingInfoConstructor(Class owner) throws NoSuchMethodException { - Constructor current = resolveConstructor(owner, constructor -> { - Class[] params = constructor.getParameterTypes(); - return params.length == 4 - && "Environment".equals(params[0].getSimpleName()) - && "ResourceKey".equals(params[1].getSimpleName()) - && "ResourceKey".equals(params[2].getSimpleName()) - && boolean.class.equals(params[3]); - }); - if (current != null) { - return current; - } - - Constructor legacy = resolveConstructor(owner, constructor -> { - Class[] params = constructor.getParameterTypes(); - return params.length == 5 - && int.class.equals(params[0]) - && String.class.equals(params[1]) - && String.class.equals(params[2]) - && "ResourceKey".equals(params[3].getSimpleName()) - && boolean.class.equals(params[4]); - }); - if (legacy != null) { - return legacy; - } - - throw new NoSuchMethodException(owner.getName() + "#"); - } - - static Constructor resolveWorldLoadingInfoAndDataConstructor(Class owner) throws NoSuchMethodException { - Constructor constructor = resolveConstructor(owner, candidate -> { - Class[] params = candidate.getParameterTypes(); - return params.length == 2 - && "WorldLoadingInfo".equals(params[0].getSimpleName()) - && "LoadedWorldData".equals(params[1].getSimpleName()); - }); - if (constructor == null) { - throw new NoSuchMethodException(owner.getName() + "#"); - } - return constructor; - } - - static Method resolveCreateNewWorldDataMethod(Class owner) throws NoSuchMethodException { - Method method = resolveMethod(owner, "createNewWorldData", candidate -> { - Class[] params = candidate.getParameterTypes(); - return params.length == 5 - && "DedicatedServerSettings".equals(params[0].getSimpleName()) - && "DataLoadContext".equals(params[1].getSimpleName()) - && "Registry".equals(params[2].getSimpleName()) - && boolean.class.equals(params[3]) - && boolean.class.equals(params[4]); - }); - if (method == null) { - throw new NoSuchMethodException(owner.getName() + "#createNewWorldData"); - } - return method; - } - - static Method resolveServerRegistryAccessMethod(Class owner) throws NoSuchMethodException { - Method method = resolveMethod(owner, "registryAccess", candidate -> candidate.getParameterCount() == 0 - && !void.class.equals(candidate.getReturnType())); - if (method == null) { - throw new NoSuchMethodException(owner.getName() + "#registryAccess"); - } - return method; - } - static Method resolveMethod(Class owner, String name, Predicate predicate) { Method selected = scanMethods(owner.getMethods(), name, predicate); if (selected != null) { @@ -192,20 +26,6 @@ final class CapabilityResolution { return null; } - static Field resolveField(Class owner, String name) throws NoSuchFieldException { - Class current = owner; - while (current != null) { - try { - Field field = current.getDeclaredField(name); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException ignored) { - current = current.getSuperclass(); - } - } - throw new NoSuchFieldException(owner.getName() + "#" + name); - } - private static Method scanMethods(Method[] methods, String name, Predicate predicate) { for (Method method : methods) { if (!method.getName().equals(name)) { @@ -218,18 +38,4 @@ final class CapabilityResolution { return null; } - private static Constructor resolveConstructor(Class owner, Predicate> predicate) { - for (Constructor constructor : owner.getConstructors()) { - if (predicate.test(constructor)) { - return constructor; - } - } - for (Constructor constructor : owner.getDeclaredConstructors()) { - if (predicate.test(constructor)) { - constructor.setAccessible(true); - return constructor; - } - } - return null; - } } diff --git a/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilitySnapshot.java b/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilitySnapshot.java index 7e838c2c4..3c88a4026 100644 --- a/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilitySnapshot.java +++ b/core/src/main/java/art/arcane/iris/world/lifecycle/CapabilitySnapshot.java @@ -4,48 +4,26 @@ import art.arcane.iris.spi.CapabilityProbe; import art.arcane.iris.world.task.J; import art.arcane.volmlib.util.scheduling.FoliaScheduler; import org.bukkit.Bukkit; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.server.NativeServerDiagnostics; +import art.arcane.volmlib.nativelib.terrain.NativeWorldRuntime; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.plugin.RegisteredServiceProvider; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Collection; import java.util.Locale; public final class CapabilitySnapshot { - public enum PaperLikeFlavor { - CURRENT_INFO_AND_DATA, - LEGACY_STORAGE_ACCESS, - UNSUPPORTED - } - private final ServerFamily serverFamily; private final boolean regionizedRuntime; private final Object worldsProvider; private final Class worldsLevelStemClass; private final Class worldsGeneratorTypeClass; private final String worldsProviderResolution; - private final Object bukkitServer; - private final Object minecraftServer; - private final Method createLevelMethod; - private final PaperLikeFlavor paperLikeFlavor; - private final Class paperWorldLoaderClass; - private final Method paperWorldDataMethod; - private final Constructor worldLoadingInfoConstructor; - private final Constructor worldLoadingInfoAndDataConstructor; - private final Method createNewWorldDataMethod; - private final Method levelStorageAccessMethod; - private final Field worldLoaderContextField; - private final Method serverRegistryAccessMethod; - private final Field settingsField; - private final Field optionsField; - private final Method isDemoMethod; - private final Method unloadWorldAsyncMethod; + private final NativeWorldRuntime nativeRuntime; private final Method chunkAtAsyncMethod; - private final Method removeLevelMethod; - private final String paperLikeResolution; CapabilitySnapshot( ServerFamily serverFamily, @@ -54,25 +32,8 @@ public final class CapabilitySnapshot { Class worldsLevelStemClass, Class worldsGeneratorTypeClass, String worldsProviderResolution, - Object bukkitServer, - Object minecraftServer, - Method createLevelMethod, - PaperLikeFlavor paperLikeFlavor, - Class paperWorldLoaderClass, - Method paperWorldDataMethod, - Constructor worldLoadingInfoConstructor, - Constructor worldLoadingInfoAndDataConstructor, - Method createNewWorldDataMethod, - Method levelStorageAccessMethod, - Field worldLoaderContextField, - Method serverRegistryAccessMethod, - Field settingsField, - Field optionsField, - Method isDemoMethod, - Method unloadWorldAsyncMethod, - Method chunkAtAsyncMethod, - Method removeLevelMethod, - String paperLikeResolution + NativeWorldRuntime nativeRuntime, + Method chunkAtAsyncMethod ) { this.serverFamily = serverFamily; this.regionizedRuntime = regionizedRuntime; @@ -80,30 +41,12 @@ public final class CapabilitySnapshot { this.worldsLevelStemClass = worldsLevelStemClass; this.worldsGeneratorTypeClass = worldsGeneratorTypeClass; this.worldsProviderResolution = worldsProviderResolution; - this.bukkitServer = bukkitServer; - this.minecraftServer = minecraftServer; - this.createLevelMethod = createLevelMethod; - this.paperLikeFlavor = paperLikeFlavor; - this.paperWorldLoaderClass = paperWorldLoaderClass; - this.paperWorldDataMethod = paperWorldDataMethod; - this.worldLoadingInfoConstructor = worldLoadingInfoConstructor; - this.worldLoadingInfoAndDataConstructor = worldLoadingInfoAndDataConstructor; - this.createNewWorldDataMethod = createNewWorldDataMethod; - this.levelStorageAccessMethod = levelStorageAccessMethod; - this.worldLoaderContextField = worldLoaderContextField; - this.serverRegistryAccessMethod = serverRegistryAccessMethod; - this.settingsField = settingsField; - this.optionsField = optionsField; - this.isDemoMethod = isDemoMethod; - this.unloadWorldAsyncMethod = unloadWorldAsyncMethod; + this.nativeRuntime = nativeRuntime; this.chunkAtAsyncMethod = chunkAtAsyncMethod; - this.removeLevelMethod = removeLevelMethod; - this.paperLikeResolution = paperLikeResolution; } public static CapabilitySnapshot probe() { Server server = Bukkit.getServer(); - Object bukkitServer = server; boolean regionizedRuntime = FoliaScheduler.isRegionizedRuntime(server); ServerFamily serverFamily = detectServerFamily(server, regionizedRuntime); @@ -121,99 +64,7 @@ public final class CapabilitySnapshot { worldsProviderResolution = e.getClass().getSimpleName() + ": " + String.valueOf(e.getMessage()); } - Object minecraftServer = null; - Method createLevelMethod = null; - PaperLikeFlavor paperLikeFlavor = PaperLikeFlavor.UNSUPPORTED; - Class paperWorldLoaderClass = null; - Method paperWorldDataMethod = null; - Constructor worldLoadingInfoConstructor = null; - Constructor worldLoadingInfoAndDataConstructor = null; - Method createNewWorldDataMethod = null; - Method levelStorageAccessMethod = null; - Field worldLoaderContextField = null; - Method serverRegistryAccessMethod = null; - Field settingsField = null; - Field optionsField = null; - Method isDemoMethod = null; - Method removeLevelMethod = null; - String paperLikeResolution = "inactive"; - - try { - if (bukkitServer != null) { - Method getServerMethod = CapabilityResolution.resolveMethod(bukkitServer.getClass(), "getServer", method -> method.getParameterCount() == 0); - if (getServerMethod != null) { - minecraftServer = getServerMethod.invoke(bukkitServer); - } - } - - if (minecraftServer != null) { - Class minecraftServerClass = Class.forName("net.minecraft.server.MinecraftServer"); - if (!minecraftServerClass.isInstance(minecraftServer)) { - throw new IllegalStateException("resolved server is not a MinecraftServer: " + minecraftServer.getClass().getName()); - } - - createLevelMethod = CapabilityResolution.resolveCreateLevelMethod(minecraftServer.getClass()); - removeLevelMethod = CapabilityResolution.resolveMethod(minecraftServer.getClass(), "removeLevel", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && "ServerLevel".equals(params[0].getSimpleName()); - }); - worldLoaderContextField = CapabilityResolution.resolveField(minecraftServer.getClass(), "worldLoaderContext"); - serverRegistryAccessMethod = CapabilityResolution.resolveServerRegistryAccessMethod(minecraftServer.getClass()); - settingsField = CapabilityResolution.resolveField(minecraftServer.getClass(), "settings"); - optionsField = CapabilityResolution.resolveField(minecraftServer.getClass(), "options"); - isDemoMethod = CapabilityResolution.resolveMethod(minecraftServer.getClass(), "isDemo", method -> method.getParameterCount() == 0 && boolean.class.equals(method.getReturnType())); - - Class mainClass = Class.forName("net.minecraft.server.Main"); - createNewWorldDataMethod = CapabilityResolution.resolveCreateNewWorldDataMethod(mainClass); - - Class paperLoaderCandidate = Class.forName("io.papermc.paper.world.PaperWorldLoader"); - paperWorldLoaderClass = paperLoaderCandidate; - paperWorldDataMethod = CapabilityResolution.resolvePaperWorldDataMethod(paperLoaderCandidate); - Class worldLoadingInfoClass = Class.forName("io.papermc.paper.world.PaperWorldLoader$WorldLoadingInfo"); - worldLoadingInfoConstructor = CapabilityResolution.resolveWorldLoadingInfoConstructor(worldLoadingInfoClass); - - if (createLevelMethod.getParameterCount() == 3) { - Class worldLoadingInfoAndDataClass = Class.forName("io.papermc.paper.world.PaperWorldLoader$WorldLoadingInfoAndData"); - worldLoadingInfoAndDataConstructor = CapabilityResolution.resolveWorldLoadingInfoAndDataConstructor(worldLoadingInfoAndDataClass); - paperLikeFlavor = PaperLikeFlavor.CURRENT_INFO_AND_DATA; - } else { - Class levelStorageSourceClass = Class.forName("net.minecraft.world.level.storage.LevelStorageSource"); - levelStorageAccessMethod = CapabilityResolution.resolveLevelStorageAccessMethod(levelStorageSourceClass); - paperLikeFlavor = PaperLikeFlavor.LEGACY_STORAGE_ACCESS; - } - - paperLikeResolution = "available(flavor=" + paperLikeFlavor.name().toLowerCase(Locale.ROOT) - + ", createLevel=" + createLevelMethod.toGenericString() + ")"; - } - } catch (Throwable e) { - paperLikeResolution = e.getClass().getSimpleName() + ": " + String.valueOf(e.getMessage()); - createLevelMethod = null; - paperLikeFlavor = PaperLikeFlavor.UNSUPPORTED; - paperWorldLoaderClass = null; - paperWorldDataMethod = null; - worldLoadingInfoConstructor = null; - worldLoadingInfoAndDataConstructor = null; - createNewWorldDataMethod = null; - levelStorageAccessMethod = null; - worldLoaderContextField = null; - serverRegistryAccessMethod = null; - settingsField = null; - optionsField = null; - isDemoMethod = null; - removeLevelMethod = null; - } - - Object resolvedServer = bukkitServer; - Method unloadWorldAsyncMethod = resolvedServer == null ? null : CapabilityProbe.attempt( - "server#unloadWorldAsync", - () -> CapabilityResolution.resolveMethod(resolvedServer.getClass(), "unloadWorldAsync", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 3 - && World.class.equals(params[0]) - && boolean.class.equals(params[1]) - && "Consumer".equals(params[2].getSimpleName()); - }), - null); + NativeWorldRuntime nativeRuntime = NativeAdapters.find(NativeWorldRuntime.class).orElse(null); Method chunkAtAsyncMethod = CapabilityProbe.attempt( "world#getChunkAtAsync", @@ -233,25 +84,8 @@ public final class CapabilitySnapshot { worldsLevelStemClass, worldsGeneratorTypeClass, worldsProviderResolution, - bukkitServer, - minecraftServer, - createLevelMethod, - paperLikeFlavor, - paperWorldLoaderClass, - paperWorldDataMethod, - worldLoadingInfoConstructor, - worldLoadingInfoAndDataConstructor, - createNewWorldDataMethod, - levelStorageAccessMethod, - worldLoaderContextField, - serverRegistryAccessMethod, - settingsField, - optionsField, - isDemoMethod, - unloadWorldAsyncMethod, - chunkAtAsyncMethod, - removeLevelMethod, - paperLikeResolution + nativeRuntime, + chunkAtAsyncMethod ); } @@ -275,76 +109,12 @@ public final class CapabilitySnapshot { return worldsGeneratorTypeClass; } - public Object bukkitServer() { - return bukkitServer; - } - - public Object minecraftServer() { - return minecraftServer; - } - - public Method createLevelMethod() { - return createLevelMethod; - } - - public PaperLikeFlavor paperLikeFlavor() { - return paperLikeFlavor; - } - - public Class paperWorldLoaderClass() { - return paperWorldLoaderClass; - } - - public Method paperWorldDataMethod() { - return paperWorldDataMethod; - } - - public Constructor worldLoadingInfoConstructor() { - return worldLoadingInfoConstructor; - } - - public Constructor worldLoadingInfoAndDataConstructor() { - return worldLoadingInfoAndDataConstructor; - } - - public Method createNewWorldDataMethod() { - return createNewWorldDataMethod; - } - - public Method levelStorageAccessMethod() { - return levelStorageAccessMethod; - } - - public Field worldLoaderContextField() { - return worldLoaderContextField; - } - - public Method serverRegistryAccessMethod() { - return serverRegistryAccessMethod; - } - - public Field settingsField() { - return settingsField; - } - - public Field optionsField() { - return optionsField; - } - - public Method isDemoMethod() { - return isDemoMethod; - } - - public Method unloadWorldAsyncMethod() { - return unloadWorldAsyncMethod; - } - public Method chunkAtAsyncMethod() { return chunkAtAsyncMethod; } - public Method removeLevelMethod() { - return removeLevelMethod; + public NativeWorldRuntime nativeRuntime() { + return nativeRuntime; } public boolean hasWorldsProvider() { @@ -352,10 +122,7 @@ public final class CapabilitySnapshot { } public boolean hasPaperLikeRuntime() { - return minecraftServer != null - && createLevelMethod != null - && serverRegistryAccessMethod != null - && paperLikeFlavor != PaperLikeFlavor.UNSUPPORTED; + return nativeRuntime != null && nativeRuntime.available(); } public String worldsProviderResolution() { @@ -363,16 +130,16 @@ public final class CapabilitySnapshot { } public String paperLikeResolution() { - return paperLikeResolution; + return nativeRuntime == null ? "unsupported" : nativeRuntime.description(); } public String describe() { return "family=" + serverFamily.id() + ", regionizedRuntime=" + regionizedRuntime + ", worldsProvider=" + worldsProviderResolution - + ", paperLike=" + paperLikeResolution - + ", serverRegistryAccess=" + (serverRegistryAccessMethod != null) - + ", unloadAsync=" + (unloadWorldAsyncMethod != null) + + ", paperLike=" + paperLikeResolution() + + ", serverRegistryAccess=" + (nativeRuntime != null && nativeRuntime.supportsRegistryAccess()) + + ", unloadAsync=" + (nativeRuntime != null && nativeRuntime.supportsUnloadAsync()) + ", chunkAsync=" + (chunkAtAsyncMethod != null); } @@ -432,8 +199,8 @@ public final class CapabilitySnapshot { } private static boolean hasCanvasRuntime() { - return CapabilityProbe.succeeds("io.canvasmc.canvas.region.WorldRegionizer", - () -> Class.forName("io.canvasmc.canvas.region.WorldRegionizer")); + return NativeAdapters.find(NativeServerDiagnostics.class) + .map(diagnostics -> diagnostics.isCanvas(CapabilitySnapshot.class.getClassLoader())).orElse(false); } private static boolean containsIgnoreCase(String value, String needle) { diff --git a/core/src/main/java/art/arcane/iris/world/lifecycle/PaperLikeRuntimeBackend.java b/core/src/main/java/art/arcane/iris/world/lifecycle/PaperLikeRuntimeBackend.java index a850794a3..542e2f0a3 100644 --- a/core/src/main/java/art/arcane/iris/world/lifecycle/PaperLikeRuntimeBackend.java +++ b/core/src/main/java/art/arcane/iris/world/lifecycle/PaperLikeRuntimeBackend.java @@ -3,8 +3,11 @@ package art.arcane.iris.world.lifecycle; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.bukkit.WorldIdentity; import org.bukkit.World; +import org.bukkit.NamespacedKey; +import art.arcane.iris.platform.generation.PlatformChunkGenerator; +import art.arcane.iris.world.IrisWorldStorage; +import art.arcane.volmlib.nativelib.terrain.WorldRuntimeOptions; -import java.util.Locale; import java.util.concurrent.CompletableFuture; final class PaperLikeRuntimeBackend implements WorldLifecycleBackend { @@ -29,7 +32,6 @@ final class PaperLikeRuntimeBackend implements WorldLifecycleBackend { @Override public CompletableFuture create(WorldLifecycleRequest request) { - Object legacyStorageAccess = null; try { World existing = WorldIdentity.resolve(request.worldKey()).orElse(null); if (existing != null) { @@ -41,42 +43,21 @@ final class PaperLikeRuntimeBackend implements WorldLifecycleBackend { } WorldLifecycleStaging.stageGenerator(request.worldName(), request.generator(), request.biomeProvider()); - WorldLifecycleSupport.stageRuntimeConfiguration(request.worldName()); - + NamespacedKey dimensionTypeKey = request.generator() instanceof PlatformChunkGenerator generator + ? new NamespacedKey("iris", generator.getTarget().getDimension().getDimensionTypeKey()) : null; IrisLogging.debug("WorldLifecycle runtime LevelStem: world=" + request.worldName() - + ", backend=paper_like_runtime, flavor=" + capabilities.paperLikeFlavor().name().toLowerCase(Locale.ROOT) - + ", registrySource=" + WorldLifecycleSupport.runtimeLevelStemRegistrySource(request)); - Object levelStem = WorldLifecycleSupport.resolveRuntimeLevelStem(capabilities, request); - Object stemKey = WorldLifecycleSupport.createRuntimeLevelStemKey(request.worldKey()); - - if (capabilities.paperLikeFlavor() == CapabilitySnapshot.PaperLikeFlavor.CURRENT_INFO_AND_DATA) { - Object dimensionKey = WorldLifecycleSupport.createDimensionKey(stemKey); - Object loadedWorldData = capabilities.paperWorldDataMethod().invoke(null, capabilities.minecraftServer(), dimensionKey, request.worldName()); - Object worldLoadingInfo = capabilities.worldLoadingInfoConstructor().newInstance(request.environment(), stemKey, dimensionKey, !request.studio()); - Object worldLoadingInfoAndData = capabilities.worldLoadingInfoAndDataConstructor().newInstance(worldLoadingInfo, loadedWorldData); - Object worldDataAndGenSettings = WorldLifecycleSupport.createCurrentWorldDataAndSettings(capabilities, request.worldName()); - if (!WorldLifecycleSupport.hasExistingWorldData(request.worldKey())) { - worldDataAndGenSettings = WorldLifecycleSupport.applySeedToWorldDataAndGenSettings(worldDataAndGenSettings, request.seed()); - } - capabilities.createLevelMethod().invoke(capabilities.minecraftServer(), levelStem, worldLoadingInfoAndData, worldDataAndGenSettings); - } else { - legacyStorageAccess = WorldLifecycleSupport.createLegacyStorageAccess(capabilities); - Object primaryLevelData = WorldLifecycleSupport.createLegacyPrimaryLevelData(capabilities, legacyStorageAccess, request.worldName()); - Object worldLoadingInfo = capabilities.worldLoadingInfoConstructor().newInstance(0, request.worldName(), request.environment().name().toLowerCase(Locale.ROOT), stemKey, !request.studio()); - capabilities.createLevelMethod().invoke(capabilities.minecraftServer(), levelStem, worldLoadingInfo, legacyStorageAccess, primaryLevelData); - } - - World loadedWorld = WorldIdentity.resolve(request.worldKey()).orElse(null); - if (loadedWorld == null) { - return CompletableFuture.failedFuture(new IllegalStateException("Paper-like runtime backend did not load world \"" + request.worldName() + "\".")); - } + + ", backend=paper_like_runtime, flavor=" + capabilities.nativeRuntime().flavor() + + ", registrySource=" + (dimensionTypeKey == null ? "datapack_level_stem_registry" : "full_server_registry")); + WorldRuntimeOptions options = new WorldRuntimeOptions(request.worldName(), request.worldKey(), + request.environment(), dimensionTypeKey, "Iris:runtime", !request.studio(), request.seed(), + WorldLifecycleSupport.hasExistingWorldData(request.worldKey()), IrisWorldStorage.levelRoot()); + World loadedWorld = capabilities.nativeRuntime().create(options, WorldLifecycleSupport.EXECUTION); return CompletableFuture.completedFuture(loadedWorld); } catch (Throwable e) { return CompletableFuture.failedFuture(WorldLifecycleSupport.unwrap(e)); } finally { WorldLifecycleStaging.clearGenerator(request.worldName()); - WorldLifecycleSupport.closeLevelStorageAccess(legacyStorageAccess); } } diff --git a/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleService.java b/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleService.java index ed142f3e8..638d732c9 100644 --- a/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleService.java +++ b/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleService.java @@ -196,7 +196,7 @@ public final class WorldLifecycleService { } public boolean unload(World world, boolean save) { - if (J.isPrimaryThread() || (J.isFolia() && WorldLifecycleSupport.isGlobalTickThread())) { + if (J.isPrimaryThread() || (J.isFolia() && WorldLifecycleSupport.isGlobalTickThread(capabilities))) { throw new IllegalStateException("WorldLifecycle unload cannot block the primary/global tick thread; use unloadAsync instead."); } diff --git a/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupport.java b/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupport.java index 57ac0c591..fff2e3481 100644 --- a/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupport.java +++ b/core/src/main/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupport.java @@ -1,39 +1,24 @@ package art.arcane.iris.world.lifecycle; -import art.arcane.iris.world.IrisWorldStorage; -import art.arcane.iris.integration.Identifier; -import art.arcane.iris.platform.bukkit.nms.INMS; -import art.arcane.iris.platform.bukkit.nms.INMSBinding; -import art.arcane.iris.platform.generation.PlatformChunkGenerator; import art.arcane.iris.platform.bukkit.BukkitPlatform; -import art.arcane.iris.spi.CapabilityProbe; import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.world.IrisWorldStorage; import art.arcane.iris.world.task.J; -import art.arcane.volmlib.util.bukkit.WorldIdentity; +import art.arcane.volmlib.nativelib.terrain.WorldRuntimeExecution; import art.arcane.volmlib.util.scheduling.FoliaScheduler; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.World; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.event.world.WorldUnloadEvent; -import org.bukkit.generator.ChunkGenerator; import java.io.File; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.nio.file.Path; -import java.util.Map; -import java.util.Optional; -import java.util.OptionalLong; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; final class WorldLifecycleSupport { + static final WorldRuntimeExecution EXECUTION = new IrisWorldRuntimeExecution(); + private WorldLifecycleSupport() { } @@ -50,213 +35,11 @@ final class WorldLifecycleSupport { return throwable; } - static Object invoke(Method method, Object target, Object... args) throws ReflectiveOperationException { - return method.invoke(target, args); - } - static Object invokeNamed(Object target, String methodName, Class[] parameterTypes, Object... args) throws ReflectiveOperationException { Method method = target.getClass().getMethod(methodName, parameterTypes); return method.invoke(target, args); } - static Object read(Field field, Object target) throws IllegalAccessException { - return field.get(target); - } - - static void stageRuntimeConfiguration(String worldName) throws ReflectiveOperationException { - Object bukkitServer = Bukkit.getServer(); - if (bukkitServer == null) { - throw new IllegalStateException("Bukkit server is unavailable."); - } - - Field configurationField = CapabilityResolution.resolveField(bukkitServer.getClass(), "configuration"); - Object rawConfiguration = configurationField.get(bukkitServer); - if (!(rawConfiguration instanceof YamlConfiguration configuration)) { - throw new IllegalStateException("CraftServer configuration field is unavailable."); - } - - ConfigurationSection worldsSection = configuration.getConfigurationSection("worlds"); - if (worldsSection == null) { - worldsSection = configuration.createSection("worlds"); - } - - ConfigurationSection worldSection = worldsSection.getConfigurationSection(worldName); - if (worldSection == null) { - worldSection = worldsSection.createSection(worldName); - } - - worldSection.set("generator", "Iris:runtime"); - } - - static Object getRuntimeDatapackDimensions(CapabilitySnapshot capabilities) throws ReflectiveOperationException { - Object worldLoaderContext = read(capabilities.worldLoaderContextField(), capabilities.minecraftServer()); - Method datapackDimensionsMethod = CapabilityResolution.resolveMethod(worldLoaderContext.getClass(), "datapackDimensions", method -> method.getParameterCount() == 0); - if (datapackDimensionsMethod == null) { - throw new IllegalStateException("DataLoadContext does not expose datapackDimensions()."); - } - Object datapackDimensions = datapackDimensionsMethod.invoke(worldLoaderContext); - if (datapackDimensions == null) { - throw new IllegalStateException("DataLoadContext.datapackDimensions() returned null."); - } - return datapackDimensions; - } - - static Object getRuntimeServerRegistryAccess(CapabilitySnapshot capabilities) throws ReflectiveOperationException { - Method registryAccessMethod = capabilities.serverRegistryAccessMethod(); - if (registryAccessMethod == null) { - throw new IllegalStateException("MinecraftServer does not expose registryAccess()."); - } - Object registryAccess = registryAccessMethod.invoke(capabilities.minecraftServer()); - if (registryAccess == null) { - throw new IllegalStateException("MinecraftServer.registryAccess() returned null."); - } - return registryAccess; - } - - static Object getRuntimeLevelStemRegistry(CapabilitySnapshot capabilities) throws ReflectiveOperationException { - Object datapackDimensions = getRuntimeDatapackDimensions(capabilities); - Object levelStemRegistryKey = Class.forName("net.minecraft.core.registries.Registries") - .getField("LEVEL_STEM") - .get(null); - Method lookupMethod = CapabilityResolution.resolveMethod(datapackDimensions.getClass(), "lookupOrThrow", method -> method.getParameterCount() == 1); - if (lookupMethod == null) { - throw new IllegalStateException("Registry access does not expose lookupOrThrow(...)."); - } - return lookupMethod.invoke(datapackDimensions, levelStemRegistryKey); - } - - static Object createRuntimeLevelStemKey(NamespacedKey worldKey) throws ReflectiveOperationException { - Identifier identifier = new Identifier(worldKey.getNamespace(), worldKey.getKey()); - Object rawIdentifier = Class.forName("net.minecraft.resources.Identifier") - .getMethod("fromNamespaceAndPath", String.class, String.class) - .invoke(null, identifier.namespace(), identifier.key()); - Object registryKey = Class.forName("net.minecraft.core.registries.Registries") - .getField("LEVEL_STEM") - .get(null); - Method createMethod = Class.forName("net.minecraft.resources.ResourceKey") - .getMethod("create", registryKey.getClass(), rawIdentifier.getClass()); - return createMethod.invoke(null, registryKey, rawIdentifier); - } - - static Object createDimensionKey(Object stemKey) throws ReflectiveOperationException { - Class resourceKeyClass = Class.forName("net.minecraft.resources.ResourceKey"); - Method identifierMethod = CapabilityResolution.resolveMethod(resourceKeyClass, "identifier", method -> method.getParameterCount() == 0); - Object identifier = identifierMethod.invoke(stemKey); - Object dimensionRegistryKey = Class.forName("net.minecraft.core.registries.Registries") - .getField("DIMENSION") - .get(null); - Method createMethod = resourceKeyClass.getMethod("create", dimensionRegistryKey.getClass(), identifier.getClass()); - return createMethod.invoke(null, dimensionRegistryKey, identifier); - } - - static Object resolveRuntimeLevelStem(CapabilitySnapshot capabilities, WorldLifecycleRequest request) throws ReflectiveOperationException { - return resolveRuntimeLevelStem(capabilities, request, INMS.get()); - } - - static Object resolveRuntimeLevelStem(CapabilitySnapshot capabilities, WorldLifecycleRequest request, INMSBinding binding) throws ReflectiveOperationException { - ChunkGenerator generator = request.generator(); - if (generator instanceof PlatformChunkGenerator) { - Object registryAccess = getRuntimeServerRegistryAccess(capabilities); - try { - Object levelStem = binding.createRuntimeLevelStem(registryAccess, generator); - if (levelStem == null) { - throw new IllegalStateException("Iris NMS binding returned null runtime LevelStem."); - } - return levelStem; - } catch (Throwable e) { - throw new IllegalStateException("Failed to create runtime LevelStem from full server registry access for world \"" + request.worldName() + "\".", unwrap(e)); - } - } - - try { - Object levelStemRegistry = getRuntimeLevelStemRegistry(capabilities); - Object overworldKey = Class.forName("net.minecraft.world.level.dimension.LevelStem") - .getField("OVERWORLD") - .get(null); - Method getValueMethod = CapabilityResolution.resolveMethod(levelStemRegistry.getClass(), "getValue", method -> method.getParameterCount() == 1); - if (getValueMethod != null) { - Object resolved = getValueMethod.invoke(levelStemRegistry, overworldKey); - if (resolved != null) { - return resolved; - } - } - - Method getMethod = CapabilityResolution.resolveMethod(levelStemRegistry.getClass(), "get", method -> method.getParameterCount() == 1); - if (getMethod == null) { - throw new IllegalStateException("Unable to resolve OVERWORLD LevelStem from registry."); - } - Object raw = getMethod.invoke(levelStemRegistry, overworldKey); - return extractRegistryValue(raw); - } catch (Throwable e) { - throw new IllegalStateException("Failed to resolve fallback OVERWORLD LevelStem from datapack registry access for world \"" + request.worldName() + "\".", unwrap(e)); - } - } - - static String runtimeLevelStemRegistrySource(WorldLifecycleRequest request) { - if (request.generator() instanceof PlatformChunkGenerator) { - return "full_server_registry"; - } - return "datapack_level_stem_registry"; - } - - static Object extractRegistryValue(Object raw) throws ReflectiveOperationException { - if (raw == null) { - return null; - } - if (raw instanceof Optional optional) { - Object nested = optional.orElse(null); - if (nested == null) { - return null; - } - return extractRegistryValue(nested); - } - Method valueMethod = CapabilityResolution.resolveMethod(raw.getClass(), "value", method -> method.getParameterCount() == 0); - if (valueMethod != null) { - return valueMethod.invoke(raw); - } - return raw; - } - - static void applyWorldDataNameAndModInfo(CapabilitySnapshot capabilities, Object worldDataAndGenSettings, String worldName) throws ReflectiveOperationException { - Method dataMethod = CapabilityResolution.resolveMethod(worldDataAndGenSettings.getClass(), "data", method -> method.getParameterCount() == 0); - if (dataMethod == null) { - return; - } - - Object worldData = dataMethod.invoke(worldDataAndGenSettings); - if (worldData == null) { - return; - } - - Method checkNameMethod = CapabilityResolution.resolveMethod(worldData.getClass(), "checkName", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && String.class.equals(params[0]); - }); - if (checkNameMethod != null) { - checkNameMethod.invoke(worldData, worldName); - } - - Method getModdedStatusMethod = CapabilityResolution.resolveMethod(capabilities.minecraftServer().getClass(), "getModdedStatus", method -> method.getParameterCount() == 0); - Method getServerModNameMethod = CapabilityResolution.resolveMethod(capabilities.minecraftServer().getClass(), "getServerModName", method -> method.getParameterCount() == 0); - if (getModdedStatusMethod == null || getServerModNameMethod == null) { - return; - } - - Object modCheck = getModdedStatusMethod.invoke(capabilities.minecraftServer()); - Method shouldReportAsModifiedMethod = CapabilityResolution.resolveMethod(modCheck.getClass(), "shouldReportAsModified", method -> method.getParameterCount() == 0); - Method setModdedInfoMethod = CapabilityResolution.resolveMethod(worldData.getClass(), "setModdedInfo", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 2 && String.class.equals(params[0]) && boolean.class.equals(params[1]); - }); - if (shouldReportAsModifiedMethod == null || setModdedInfoMethod == null) { - return; - } - - boolean modified = Boolean.TRUE.equals(shouldReportAsModifiedMethod.invoke(modCheck)); - String modName = (String) getServerModNameMethod.invoke(capabilities.minecraftServer()); - setModdedInfoMethod.invoke(worldData, modName, modified); - } - static boolean hasExistingWorldData(NamespacedKey worldKey) { File worldFolder = IrisWorldStorage.dimensionRoot(worldKey); return new File(worldFolder, "region").exists() @@ -266,458 +49,65 @@ final class WorldLifecycleSupport { || new File(worldFolder, "paper-world.yml").exists(); } - static Object applySeedToWorldDataAndGenSettings(Object worldDataAndGenSettings, long seed) throws ReflectiveOperationException { - Method genSettingsMethod = CapabilityResolution.resolveMethod(worldDataAndGenSettings.getClass(), "genSettings", method -> method.getParameterCount() == 0); - Method dataMethod = CapabilityResolution.resolveMethod(worldDataAndGenSettings.getClass(), "data", method -> method.getParameterCount() == 0); - if (genSettingsMethod == null || dataMethod == null) { - throw new IllegalStateException("WorldDataAndGenSettings does not expose data()/genSettings()."); - } - - Object genSettings = genSettingsMethod.invoke(worldDataAndGenSettings); - Method optionsMethod = CapabilityResolution.resolveMethod(genSettings.getClass(), "options", method -> method.getParameterCount() == 0); - Method dimensionsMethod = CapabilityResolution.resolveMethod(genSettings.getClass(), "dimensions", method -> method.getParameterCount() == 0); - if (optionsMethod == null || dimensionsMethod == null) { - throw new IllegalStateException("WorldGenSettings does not expose options()/dimensions()."); - } - - Object options = optionsMethod.invoke(genSettings); - Method seedMethod = CapabilityResolution.resolveMethod(options.getClass(), "seed", method -> method.getParameterCount() == 0 && long.class.equals(method.getReturnType())); - Method withSeedMethod = CapabilityResolution.resolveMethod(options.getClass(), "withSeed", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && OptionalLong.class.equals(params[0]); - }); - if (seedMethod == null || withSeedMethod == null) { - throw new IllegalStateException("WorldOptions does not expose seed()/withSeed(OptionalLong)."); - } - - long currentSeed = (long) seedMethod.invoke(options); - if (currentSeed == seed) { - return worldDataAndGenSettings; - } - - Object newOptions = withSeedMethod.invoke(options, OptionalLong.of(seed)); - Object newGenSettings = construct(genSettings.getClass(), newOptions, dimensionsMethod.invoke(genSettings)); - return construct(worldDataAndGenSettings.getClass(), dataMethod.invoke(worldDataAndGenSettings), newGenSettings); - } - - private static Object construct(Class type, Object first, Object second) throws ReflectiveOperationException { - for (Constructor constructor : type.getDeclaredConstructors()) { - Class[] params = constructor.getParameterTypes(); - if (params.length != 2) { - continue; - } - - if ((first == null || params[0].isInstance(first)) && (second == null || params[1].isInstance(second))) { - constructor.setAccessible(true); - return constructor.newInstance(first, second); - } - } - - throw new IllegalStateException("No compatible two-argument constructor on " + type.getName()); - } - - static Object createCurrentWorldDataAndSettings(CapabilitySnapshot capabilities, String worldName) throws ReflectiveOperationException { - Object settings = read(capabilities.settingsField(), capabilities.minecraftServer()); - Object worldLoaderContext = read(capabilities.worldLoaderContextField(), capabilities.minecraftServer()); - Object levelStemRegistry = getRuntimeLevelStemRegistry(capabilities); - boolean demo = Boolean.TRUE.equals(capabilities.isDemoMethod().invoke(capabilities.minecraftServer())); - Object options = read(capabilities.optionsField(), capabilities.minecraftServer()); - Method hasMethod = CapabilityResolution.resolveMethod(options.getClass(), "has", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && String.class.equals(params[0]); - }); - boolean bonusChest = hasMethod != null && Boolean.TRUE.equals(hasMethod.invoke(options, "bonusChest")); - Object dataLoadOutput = capabilities.createNewWorldDataMethod().invoke(null, settings, worldLoaderContext, levelStemRegistry, demo, bonusChest); - Method cookieMethod = CapabilityResolution.resolveMethod(dataLoadOutput.getClass(), "cookie", method -> method.getParameterCount() == 0); - if (cookieMethod == null) { - throw new IllegalStateException("WorldLoader.DataLoadOutput does not expose cookie()."); - } - Object worldDataAndGenSettings = cookieMethod.invoke(dataLoadOutput); - applyWorldDataNameAndModInfo(capabilities, worldDataAndGenSettings, worldName); - return worldDataAndGenSettings; - } - - static Object createLegacyPrimaryLevelData(CapabilitySnapshot capabilities, Object levelStorageAccess, String worldName) throws ReflectiveOperationException { - Object levelDataResult = capabilities.paperWorldDataMethod().invoke(null, levelStorageAccess); - Method fatalErrorMethod = CapabilityResolution.resolveMethod(levelDataResult.getClass(), "fatalError", method -> method.getParameterCount() == 0); - Method dataTagMethod = CapabilityResolution.resolveMethod(levelDataResult.getClass(), "dataTag", method -> method.getParameterCount() == 0); - if (fatalErrorMethod != null && Boolean.TRUE.equals(fatalErrorMethod.invoke(levelDataResult))) { - throw new IllegalStateException("Paper runtime world-data helper reported a fatal error for \"" + worldName + "\"."); - } - if (dataTagMethod != null && dataTagMethod.invoke(levelDataResult) != null) { - throw new IllegalStateException("Runtime world \"" + worldName + "\" already contains level data."); - } - - Object settings = read(capabilities.settingsField(), capabilities.minecraftServer()); - Object worldLoaderContext = read(capabilities.worldLoaderContextField(), capabilities.minecraftServer()); - Object levelStemRegistry = getRuntimeLevelStemRegistry(capabilities); - boolean demo = Boolean.TRUE.equals(capabilities.isDemoMethod().invoke(capabilities.minecraftServer())); - Object options = read(capabilities.optionsField(), capabilities.minecraftServer()); - Method hasMethod = CapabilityResolution.resolveMethod(options.getClass(), "has", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && String.class.equals(params[0]); - }); - boolean bonusChest = hasMethod != null && Boolean.TRUE.equals(hasMethod.invoke(options, "bonusChest")); - Object dataLoadOutput = capabilities.createNewWorldDataMethod().invoke(null, settings, worldLoaderContext, levelStemRegistry, demo, bonusChest); - Method cookieMethod = CapabilityResolution.resolveMethod(dataLoadOutput.getClass(), "cookie", method -> method.getParameterCount() == 0); - if (cookieMethod == null) { - throw new IllegalStateException("WorldLoader.DataLoadOutput does not expose cookie()."); - } - Object primaryLevelData = cookieMethod.invoke(dataLoadOutput); - - Method checkNameMethod = CapabilityResolution.resolveMethod(primaryLevelData.getClass(), "checkName", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 1 && String.class.equals(params[0]); - }); - if (checkNameMethod != null) { - checkNameMethod.invoke(primaryLevelData, worldName); - } - - Method getModdedStatusMethod = CapabilityResolution.resolveMethod(capabilities.minecraftServer().getClass(), "getModdedStatus", method -> method.getParameterCount() == 0); - Method getServerModNameMethod = CapabilityResolution.resolveMethod(capabilities.minecraftServer().getClass(), "getServerModName", method -> method.getParameterCount() == 0); - if (getModdedStatusMethod != null && getServerModNameMethod != null) { - Object modCheck = getModdedStatusMethod.invoke(capabilities.minecraftServer()); - Method shouldReportAsModifiedMethod = CapabilityResolution.resolveMethod(modCheck.getClass(), "shouldReportAsModified", method -> method.getParameterCount() == 0); - Method setModdedInfoMethod = CapabilityResolution.resolveMethod(primaryLevelData.getClass(), "setModdedInfo", method -> { - Class[] params = method.getParameterTypes(); - return params.length == 2 && String.class.equals(params[0]) && boolean.class.equals(params[1]); - }); - if (shouldReportAsModifiedMethod != null && setModdedInfoMethod != null) { - boolean modified = Boolean.TRUE.equals(shouldReportAsModifiedMethod.invoke(modCheck)); - String modName = (String) getServerModNameMethod.invoke(capabilities.minecraftServer()); - setModdedInfoMethod.invoke(primaryLevelData, modName, modified); - } - } - - return primaryLevelData; - } - - static Object createLegacyStorageAccess(CapabilitySnapshot capabilities) throws ReflectiveOperationException { - Class levelStorageSourceClass = Class.forName("net.minecraft.world.level.storage.LevelStorageSource"); - Method createDefaultMethod = levelStorageSourceClass.getMethod("createDefault", Path.class); - File levelRoot = IrisWorldStorage.levelRoot(); - Object levelStorageSource = createDefaultMethod.invoke(null, levelRoot.getParentFile().toPath()); - Method storageAccessMethod = capabilities.levelStorageAccessMethod(); - if (storageAccessMethod.getParameterCount() == 1) { - return storageAccessMethod.invoke(levelStorageSource, levelRoot.getName()); - } - Object overworldStemKey = Class.forName("net.minecraft.world.level.dimension.LevelStem") - .getField("OVERWORLD") - .get(null); - return storageAccessMethod.invoke(levelStorageSource, levelRoot.getName(), overworldStemKey); - } - - static void closeLevelStorageAccess(Object levelStorageAccess) { - if (levelStorageAccess == null) { - return; - } - try { - Method closeMethod = levelStorageAccess.getClass().getMethod("close"); - closeMethod.invoke(levelStorageAccess); - } catch (Throwable failure) { - IrisLogging.reportError("Failed to close the level storage access; the world session lock may still be held.", - unwrap(failure)); - } - } - static CompletableFuture unloadWorldAsync(CapabilitySnapshot capabilities, World world, boolean save) { if (world == null) { return CompletableFuture.completedFuture(false); } - + if (capabilities.nativeRuntime() != null) { + return capabilities.nativeRuntime().unload(world, save, EXECUTION); + } CompletableFuture result = new CompletableFuture<>(); - Runnable invokeTask = () -> beginUnload(capabilities, world, save, result); - boolean folia = J.isFolia(); - if ((!folia && J.isPrimaryThread()) || (folia && isGlobalTickThread())) { - invokeTask.run(); + EXECUTION.runGlobal(() -> result.complete(Bukkit.unloadWorld(world, save))) + .whenComplete((unused, failure) -> { + if (failure != null) { + result.completeExceptionally(unwrap(failure)); + } + }); + return result; + } + + static boolean isGlobalTickThread(CapabilitySnapshot capabilities) { + return capabilities.nativeRuntime() != null && capabilities.nativeRuntime().isGlobalTickThread(); + } + + private static final class IrisWorldRuntimeExecution implements WorldRuntimeExecution { + public boolean regionized() { + return J.isFolia(); + } + + public boolean primaryThread() { + return J.isPrimaryThread(); + } + + public CompletableFuture runGlobal(Runnable task) { + if (!J.isFolia()) { + return J.sfut(task); + } + CompletableFuture result = new CompletableFuture<>(); + Runnable settlement = () -> { + try { + task.run(); + result.complete(null); + } catch (Throwable failure) { + result.completeExceptionally(unwrap(failure)); + } + }; + try { + if (!FoliaScheduler.runGlobal(BukkitPlatform.plugin(), settlement)) { + result.completeExceptionally(new IllegalStateException("Failed to schedule global world lifecycle task.")); + } + } catch (Throwable failure) { + result.completeExceptionally(unwrap(failure)); + } return result; } - CompletableFuture scheduled = runGlobalAsync(invokeTask); - scheduled.whenComplete((unused, throwable) -> { - if (throwable != null) { - result.completeExceptionally(unwrap(throwable)); - } - }); - return result; - } - - private static void beginUnload( - CapabilitySnapshot capabilities, - World world, - boolean save, - CompletableFuture result - ) { - CompletableFuture operation; - try { - operation = unloadWorldViaAsyncApi(capabilities, world, save); - if (operation == null) { - operation = unloadWorldWithoutAsyncApi(capabilities, world, save); - } - } catch (Throwable e) { - result.completeExceptionally(unwrap(e)); - return; + public CompletableFuture runAsync(Runnable task) { + return J.afut(task); } - operation.whenComplete((unloaded, throwable) -> { - if (throwable == null) { - result.complete(Boolean.TRUE.equals(unloaded)); - } else { - result.completeExceptionally(unwrap(throwable)); - } - }); - } - - private static CompletableFuture unloadWorldWithoutAsyncApi( - CapabilitySnapshot capabilities, - World world, - boolean save - ) { - String worldName = world.getName(); - try { - if (capabilities.minecraftServer() == null || capabilities.removeLevelMethod() == null) { - return CompletableFuture.completedFuture(Bukkit.unloadWorld(world, save)); - } - - if (!announceManualWorldUnload(world)) { - return CompletableFuture.completedFuture(false); - } - - if (save) { - world.save(); - } - Method getHandleMethod = world.getClass().getMethod("getHandle"); - Object serverLevel = getHandleMethod.invoke(world); - CompletableFuture operation = detachServerLevelAsync(capabilities, serverLevel, world) - .thenCompose(unused -> drainChunkTasksAsync(world, serverLevel)) - .thenCompose(unused -> closeServerLevelAsync(world, serverLevel)) - .thenApply(unused -> WorldIdentity.resolve(WorldIdentity.key(world)).isEmpty()); - return contextualizeUnloadFailure(worldName, operation); - } catch (Throwable e) { - return CompletableFuture.failedFuture(new IllegalStateException( - "Failed to unload world \"" + worldName + "\" through the selected world lifecycle backend.", - unwrap(e) - )); + public void reportFailure(String message, Throwable failure) { + IrisLogging.reportError(message, failure); } } - - static boolean announceManualWorldUnload(World world) { - WorldUnloadEvent unloadEvent = new WorldUnloadEvent(world); - Bukkit.getPluginManager().callEvent(unloadEvent); - return !unloadEvent.isCancelled(); - } - - private static CompletableFuture unloadWorldViaAsyncApi(CapabilitySnapshot capabilities, World world, boolean save) { - if (capabilities.unloadWorldAsyncMethod() == null || capabilities.bukkitServer() == null) { - return null; - } - - return invokeAsyncUnload( - capabilities.bukkitServer(), - capabilities.unloadWorldAsyncMethod(), - world, - save - ); - } - - static CompletableFuture invokeAsyncUnload( - Object bukkitServer, - Method unloadWorldAsyncMethod, - World world, - boolean save - ) { - CompletableFuture callbackFuture = new CompletableFuture<>(); - Consumer callback = unloaded -> { - try { - callbackFuture.complete(asyncUnloadSucceeded(unloaded)); - } catch (Throwable failure) { - callbackFuture.completeExceptionally(unwrap(failure)); - } - }; - try { - unloadWorldAsyncMethod.invoke(bukkitServer, world, save, callback); - } catch (Throwable e) { - callbackFuture.completeExceptionally(unwrap(e)); - } - return callbackFuture; - } - - private static boolean asyncUnloadSucceeded(Object result) throws ReflectiveOperationException { - if (result instanceof Boolean unloaded) { - return unloaded; - } - if (result == null) { - return false; - } - Method isSuccessMethod = result.getClass().getMethod("isSuccess"); - return Boolean.TRUE.equals(isSuccessMethod.invoke(result)); - } - - private static CompletableFuture contextualizeUnloadFailure( - String worldName, - CompletableFuture operation - ) { - CompletableFuture result = new CompletableFuture<>(); - operation.whenComplete((unloaded, throwable) -> { - if (throwable == null) { - result.complete(Boolean.TRUE.equals(unloaded)); - } else { - result.completeExceptionally(new IllegalStateException( - "Failed to unload world \"" + worldName + "\" through the selected world lifecycle backend.", - unwrap(throwable) - )); - } - }); - return result; - } - - private static CompletableFuture closeServerLevelAsync(World world, Object serverLevel) { - Method closeMethod; - try { - closeMethod = CapabilityResolution.resolveMethod( - serverLevel.getClass(), - "close", - method -> method.getParameterCount() == 0 - ); - } catch (Throwable e) { - return CompletableFuture.failedFuture(unwrap(e)); - } - if (closeMethod == null) { - return CompletableFuture.completedFuture(null); - } - - Runnable closeTask = () -> { - try { - closeMethod.invoke(serverLevel); - } catch (Throwable e) { - throw new RuntimeException(unwrap(e)); - } - }; - return runGlobalAsync(closeTask).orTimeout(90L, TimeUnit.SECONDS); - } - - private static CompletableFuture drainChunkTasksAsync(World world, Object serverLevel) { - Method schedulerMethod; - try { - schedulerMethod = CapabilityResolution.resolveMethod( - serverLevel.getClass(), - "moonrise$getChunkTaskScheduler", - method -> method.getParameterCount() == 0 - ); - } catch (Throwable e) { - return CompletableFuture.failedFuture(unwrap(e)); - } - if (schedulerMethod == null) { - return CompletableFuture.completedFuture(null); - } - - return J.afut(() -> { - try { - Object scheduler = schedulerMethod.invoke(serverLevel); - Method haltMethod = CapabilityResolution.resolveMethod( - scheduler.getClass(), - "halt", - method -> { - Class[] parameters = method.getParameterTypes(); - return parameters.length == 2 - && boolean.class.equals(parameters[0]) - && long.class.equals(parameters[1]); - } - ); - if (haltMethod == null) { - return; - } - Object halted = haltMethod.invoke( - scheduler, - true, - TimeUnit.SECONDS.toNanos(90L)); - if (halted instanceof Boolean complete && !complete) { - throw new IllegalStateException( - "Chunk scheduler drain timed out for world \"" + world.getName() + "\"."); - } - } catch (Throwable e) { - throw new RuntimeException(unwrap(e)); - } - }).orTimeout(90L, TimeUnit.SECONDS); - } - - @SuppressWarnings({"rawtypes", "unchecked"}) - private static void removeWorldFromCraftServerMap(World world) throws ReflectiveOperationException { - Object bukkitServer = Bukkit.getServer(); - if (bukkitServer == null) { - return; - } - - Field worldsField = CapabilityResolution.resolveField(bukkitServer.getClass(), "worlds"); - Object rawWorlds = worldsField.get(bukkitServer); - if (rawWorlds instanceof Map map) { - boolean removed = map.values().removeIf(candidate -> candidate == world); - if (!removed) { - throw new IllegalStateException( - "CraftServer world registry did not contain \"" + world.getName() + "\"."); - } - } - } - - private static CompletableFuture detachServerLevelAsync( - CapabilitySnapshot capabilities, - Object serverLevel, - World world - ) { - Runnable detachTask = () -> { - try { - capabilities.removeLevelMethod().invoke(capabilities.minecraftServer(), serverLevel); - removeWorldFromCraftServerMap(world); - } catch (Throwable e) { - throw new RuntimeException(e); - } - }; - - if (!J.isFolia() || isGlobalTickThread()) { - try { - detachTask.run(); - return CompletableFuture.completedFuture(null); - } catch (Throwable e) { - return CompletableFuture.failedFuture(unwrap(e)); - } - } - - CompletableFuture detachFuture = runGlobalAsync(detachTask); - return detachFuture.orTimeout(15L, TimeUnit.SECONDS); - } - - private static CompletableFuture runGlobalAsync(Runnable task) { - if (!J.isFolia()) { - return J.sfut(task); - } - - CompletableFuture result = new CompletableFuture<>(); - Runnable settlement = () -> { - try { - task.run(); - result.complete(null); - } catch (Throwable e) { - result.completeExceptionally(unwrap(e)); - } - }; - - try { - if (!FoliaScheduler.runGlobal(BukkitPlatform.plugin(), settlement)) { - result.completeExceptionally(new IllegalStateException("Failed to schedule global world lifecycle task.")); - } - } catch (Throwable e) { - result.completeExceptionally(unwrap(e)); - } - return result; - } - - static boolean isGlobalTickThread() { - Object server = Bukkit.getServer(); - if (server == null) { - return false; - } - return CapabilityProbe.attempt("server#isGlobalTickThread", - () -> Boolean.TRUE.equals(server.getClass().getMethod("isGlobalTickThread").invoke(server)), - Boolean.FALSE); - } } diff --git a/core/src/main/java/art/arcane/iris/world/loot/IrisBlockDrops.java b/core/src/main/java/art/arcane/iris/world/loot/IrisBlockDrops.java index df2e12f63..99744a6a4 100644 --- a/core/src/main/java/art/arcane/iris/world/loot/IrisBlockDrops.java +++ b/core/src/main/java/art/arcane/iris/world/loot/IrisBlockDrops.java @@ -21,7 +21,7 @@ package art.arcane.iris.world.loot; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.structure.placement.LootResolver; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; @@ -32,7 +32,7 @@ import art.arcane.volmlib.util.math.RNG; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import lombok.experimental.Accessors; import org.bukkit.block.data.BlockData; import org.bukkit.inventory.ItemStack; @@ -64,7 +64,7 @@ public class IrisBlockDrops { KList b = new KList<>(); for (IrisBlockData i : getBlocks()) { - PlatformBlockState state = i.getBlockData(rdata); + NativeBlockState state = i.getBlockData(rdata); if (state != null) { b.add((BlockData) state.nativeHandle()); diff --git a/core/src/main/java/art/arcane/iris/world/loot/IrisLoot.java b/core/src/main/java/art/arcane/iris/world/loot/IrisLoot.java index dc44fb690..a3f11b834 100644 --- a/core/src/main/java/art/arcane/iris/world/loot/IrisLoot.java +++ b/core/src/main/java/art/arcane/iris/world/loot/IrisLoot.java @@ -26,7 +26,7 @@ import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.ExternalDataSVC; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.structure.placement.LootResolver; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; diff --git a/core/src/main/java/art/arcane/iris/world/loot/IrisLootReference.java b/core/src/main/java/art/arcane/iris/world/loot/IrisLootReference.java index a6b780a2e..a6920a1a8 100644 --- a/core/src/main/java/art/arcane/iris/world/loot/IrisLootReference.java +++ b/core/src/main/java/art/arcane/iris/world/loot/IrisLootReference.java @@ -21,7 +21,7 @@ package art.arcane.iris.world.loot; import art.arcane.iris.pack.validation.CompatPools; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; import art.arcane.iris.pack.schema.annotation.MaxNumber; diff --git a/core/src/main/java/art/arcane/iris/world/loot/IrisVanillaLootTable.java b/core/src/main/java/art/arcane/iris/world/loot/IrisVanillaLootTable.java index 2139a8f9d..7f8284b08 100644 --- a/core/src/main/java/art/arcane/iris/world/loot/IrisVanillaLootTable.java +++ b/core/src/main/java/art/arcane/iris/world/loot/IrisVanillaLootTable.java @@ -1,7 +1,7 @@ package art.arcane.iris.world.loot; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.structure.placement.LootResolver; import art.arcane.iris.spi.IrisLogging; import art.arcane.volmlib.util.collection.KList; diff --git a/core/src/main/java/art/arcane/iris/world/pregen/AsyncPregenMethod.java b/core/src/main/java/art/arcane/iris/world/pregen/AsyncPregenMethod.java index 5a9190262..f3c8b7247 100644 --- a/core/src/main/java/art/arcane/iris/world/pregen/AsyncPregenMethod.java +++ b/core/src/main/java/art/arcane/iris/world/pregen/AsyncPregenMethod.java @@ -19,6 +19,9 @@ package art.arcane.iris.world.pregen; import art.arcane.iris.spi.IrisLogging; +import art.arcane.iris.world.lifecycle.WorldLifecycleService; +import art.arcane.volmlib.nativelib.terrain.NativeWorkerPool; +import java.util.Objects; import art.arcane.iris.world.IrisPaperLikeBackendMode; import art.arcane.iris.world.IrisRuntimeSchedulerMode; import art.arcane.iris.configuration.IrisSettings; @@ -36,7 +39,6 @@ import io.papermc.lib.PaperLib; import org.bukkit.Chunk; import org.bukkit.World; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -213,15 +215,9 @@ public class AsyncPregenMethod implements PregeneratorMethod { private int resolveWorkerPoolThreads() { try { - Class moonriseCommonClass = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon"); - java.lang.reflect.Field workerPoolField = moonriseCommonClass.getDeclaredField("WORKER_POOL"); - Object workerPool = workerPoolField.get(null); - Object coreThreads = workerPool.getClass().getDeclaredMethod("getCoreThreads").invoke(workerPool); - if (coreThreads instanceof Thread[] threadsArray) { - return threadsArray.length; - } + return workerPool().threadCount(); } catch (Throwable e) { - PregenDiagnostics.probeFailed("moonrise worker pool size", e); + PregenDiagnostics.probeFailed("native worker pool size", e); } return -1; @@ -1014,14 +1010,13 @@ public class AsyncPregenMethod implements PregeneratorMethod { int adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads(); try { - Field field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); - Object pool = field.get(null); - int threads = ((Thread[]) pool.getClass().getDeclaredMethod("getCoreThreads").invoke(pool)).length; + NativeWorkerPool pool = workerPool(); + int threads = pool.threadCount(); if (threads >= adjusted) { return 0; } - pool.getClass().getDeclaredMethod("adjustThreadCount", int.class).invoke(pool, adjusted); + pool.adjustThreadCount(adjusted); return threads; } catch (Throwable e) { IrisLogging.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted); @@ -1041,10 +1036,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { } try { - Field field = Class.forName("ca.spottedleaf.moonrise.common.util.MoonriseCommon").getDeclaredField("WORKER_POOL"); - Object pool = field.get(null); - Method method = pool.getClass().getDeclaredMethod("adjustThreadCount", int.class); - method.invoke(pool, i); + workerPool().adjustThreadCount(i); return 0; } catch (Throwable e) { IrisLogging.reportError(e); @@ -1054,6 +1046,11 @@ public class AsyncPregenMethod implements PregeneratorMethod { }); } + private static NativeWorkerPool workerPool() { + return Objects.requireNonNull(WorldLifecycleService.get().capabilities().nativeRuntime(), + "Native world runtime is unavailable").workers(); + } + private interface ChunkRequestExecutor { void generate(int x, int z, PregenListener listener); default void shutdown() {} diff --git a/core/src/main/java/art/arcane/iris/world/runtime/InPlaceChunkRegenerator.java b/core/src/main/java/art/arcane/iris/world/runtime/InPlaceChunkRegenerator.java index 118f403d6..9c2cc156b 100644 --- a/core/src/main/java/art/arcane/iris/world/runtime/InPlaceChunkRegenerator.java +++ b/core/src/main/java/art/arcane/iris/world/runtime/InPlaceChunkRegenerator.java @@ -28,7 +28,7 @@ import art.arcane.iris.generation.chunk.TerrainChunkBiomeHunkView; import art.arcane.iris.platform.generation.EngineBukkitOps; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.mantle.EngineMantle; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.generation.concurrent.MultiBurst; import art.arcane.iris.platform.bukkit.plugin.VolmitSender; import art.arcane.iris.world.task.J; @@ -177,7 +177,7 @@ public final class InPlaceChunkRegenerator { for (int x = 0; x < 16; x += BIOME_STEP) { for (int z = 0; z < 16; z += BIOME_STEP) { for (int y = minHeight; y < maxHeight; y += BIOME_STEP) { - PlatformBiome biome = buffer.getBiome(x, y, z); + NativeBiome biome = buffer.getBiome(x, y, z); if (biome != null) { Biome bukkitBiome = (Biome) biome.nativeHandle(); if (world.getBiome(baseX + x, y, baseZ + z) != bukkitBiome) { diff --git a/core/src/main/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackend.java b/core/src/main/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackend.java index e8703e4ba..6fe8394cc 100644 --- a/core/src/main/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackend.java +++ b/core/src/main/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackend.java @@ -1,116 +1,57 @@ package art.arcane.iris.world.runtime; import art.arcane.iris.world.lifecycle.CapabilitySnapshot; -import art.arcane.iris.spi.CapabilityProbe; import art.arcane.iris.spi.IrisLogging; +import art.arcane.volmlib.nativelib.terrain.NativeWorldClock; import io.papermc.lib.PaperLib; -import org.bukkit.Bukkit; import org.bukkit.Chunk; import org.bukkit.World; -import java.lang.reflect.Method; import java.util.OptionalLong; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; final class PaperLikeRuntimeControlBackend implements WorldRuntimeControlBackend { private final CapabilitySnapshot capabilities; - private final AtomicReference timeAccessStrategy; + private final NativeWorldClock clock; PaperLikeRuntimeControlBackend(CapabilitySnapshot capabilities) { this.capabilities = capabilities; - this.timeAccessStrategy = new AtomicReference<>(); + clock = capabilities.nativeRuntime() == null ? null : capabilities.nativeRuntime().clock(); } - @Override public String backendName() { return "paper_like_runtime"; } - @Override public String describeCapabilities() { - TimeAccessStrategy strategy = timeAccessStrategy.get(); - String timeAccess = strategy == null ? "deferred" : strategy.description(); String chunkAsync = capabilities.chunkAtAsyncMethod() != null ? "world#getChunkAtAsync" : "paperlib"; - return "time=" + timeAccess + ", chunkAsync=" + chunkAsync + ", teleport=entity_scheduler"; + return "time=" + (clock == null ? "unsupported" : clock.description()) + + ", chunkAsync=" + chunkAsync + ", teleport=entity_scheduler"; } - @Override public OptionalLong readDayTime(World world) { - if (world == null) { + if (clock == null || world == null) { return OptionalLong.empty(); } - - TimeAccessStrategy strategy = resolveTimeAccessStrategy(world); - if (strategy == null || strategy.handleMethod() == null || strategy.readMethod() == null) { - return OptionalLong.empty(); - } - try { - Object handle = strategy.handleMethod().invoke(world); - if (handle == null) { - return OptionalLong.empty(); - } - - Object value = strategy.readMethod().invoke(strategy.readOwner(handle), strategy.readArguments(handle)); - if (value instanceof Long longValue) { - return OptionalLong.of(longValue.longValue()); - } - if (value instanceof Number number) { - return OptionalLong.of(number.longValue()); - } + return clock.readDayTime(world); } catch (Throwable failure) { IrisLogging.reportError("Failed to read the runtime day time of world \"" + world.getName() - + "\" through " + strategy.description() + ".", failure); + + "\" through " + clock.description() + ".", failure); + return OptionalLong.empty(); } - - return OptionalLong.empty(); } - @Override public boolean writeDayTime(World world, long dayTime) throws ReflectiveOperationException { - if (world == null) { - return false; - } - - TimeAccessStrategy strategy = resolveTimeAccessStrategy(world); - if (strategy == null || !strategy.writable()) { - return false; - } - - Object handle = strategy.handleMethod().invoke(world); - if (handle == null) { - return false; - } - - Object writeOwner = strategy.writeOwner(handle); - if (writeOwner == null) { - return false; - } - - strategy.writeMethod().invoke(writeOwner, dayTime); - return true; + return clock != null && clock.writeDayTime(world, dayTime); } - @Override public void syncTime(World world) { - TimeAccessStrategy strategy = timeAccessStrategy.get(); - if (strategy == null || strategy.syncMethod() == null) { + if (clock == null) { return; } - try { - Object craftServer = Bukkit.getServer(); - if (craftServer == null) { - return; - } - - Object serverHandle = strategy.serverHandleMethod() == null ? null : strategy.serverHandleMethod().invoke(craftServer); - if (serverHandle == null) { - return; - } - - strategy.syncMethod().invoke(serverHandle); + clock.syncTime(world); } catch (Throwable failure) { IrisLogging.reportError("Failed to push the runtime day time of world \"" + (world == null ? "unknown" : world.getName()) + "\" to its players.", failure); @@ -144,178 +85,4 @@ final class PaperLikeRuntimeControlBackend implements WorldRuntimeControlBackend return future; } - private TimeAccessStrategy resolveTimeAccessStrategy(World world) { - TimeAccessStrategy current = timeAccessStrategy.get(); - if (current != null) { - return current; - } - - synchronized (timeAccessStrategy) { - current = timeAccessStrategy.get(); - if (current != null) { - return current; - } - - TimeAccessStrategy resolved = probeTimeAccessStrategy(world); - timeAccessStrategy.set(resolved); - return resolved; - } - } - - private TimeAccessStrategy probeTimeAccessStrategy(World world) { - if (world == null) { - return TimeAccessStrategy.unsupported(); - } - - return CapabilityProbe.attempt("runtime world clock", () -> resolveTimeAccess(world), - TimeAccessStrategy.unsupported()); - } - - private TimeAccessStrategy resolveTimeAccess(World world) throws ReflectiveOperationException { - Method handleMethod = resolveZeroArgMethod(world.getClass(), "getHandle"); - if (handleMethod == null) { - return TimeAccessStrategy.unsupported(); - } - - Object handle = handleMethod.invoke(world); - if (handle == null) { - return TimeAccessStrategy.unsupported(); - } - - Method readMethod = resolveZeroArgMethod(handle.getClass(), "getDayTime"); - Method writeMethod = resolveLongArgMethod(handle.getClass(), "setDayTime"); - if (readMethod != null && writeMethod != null) { - return TimeAccessStrategy.forHandle(handleMethod, readMethod, writeMethod, "runtime_handle#setDayTime"); - } - - Method levelDataMethod = resolveZeroArgMethod(handle.getClass(), "serverLevelData"); - if (levelDataMethod == null) { - levelDataMethod = resolveZeroArgMethod(handle.getClass(), "getLevelData"); - } - if (levelDataMethod != null) { - Object levelData = levelDataMethod.invoke(handle); - if (levelData != null) { - Method levelDataReadMethod = resolveZeroArgMethod(levelData.getClass(), "getDayTime"); - Method levelDataWriteMethod = resolveLongArgMethod(levelData.getClass(), "setDayTime"); - if (levelDataReadMethod != null && levelDataWriteMethod != null) { - return TimeAccessStrategy.forLevelData(handleMethod, levelDataMethod, levelDataReadMethod, levelDataWriteMethod, "world_data#setDayTime"); - } - } - } - return TimeAccessStrategy.unsupported(handleMethod); - } - - private static Method resolveZeroArgMethod(Class type, String name) { - Class current = type; - while (current != null) { - try { - Method method = current.getDeclaredMethod(name); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException ignored) { - current = current.getSuperclass(); - } - } - - return null; - } - - private static Method resolveLongArgMethod(Class type, String name) { - Class current = type; - while (current != null) { - try { - Method method = current.getDeclaredMethod(name, long.class); - method.setAccessible(true); - return method; - } catch (NoSuchMethodException ignored) { - current = current.getSuperclass(); - } - } - - return null; - } - - private record TimeAccessStrategy( - Method handleMethod, - Method levelDataMethod, - Method readMethod, - Method writeMethod, - Method serverHandleMethod, - Method syncMethod, - String description - ) { - static TimeAccessStrategy forHandle(Method handleMethod, Method readMethod, Method writeMethod, String description) { - Method serverHandleMethod = resolveCraftServerMethod("getHandle"); - Method syncMethod = resolveServerMethod(serverHandleMethod, "forceTimeSynchronization"); - return new TimeAccessStrategy(handleMethod, null, readMethod, writeMethod, serverHandleMethod, syncMethod, description); - } - - static TimeAccessStrategy forLevelData(Method handleMethod, Method levelDataMethod, Method readMethod, Method writeMethod, String description) { - Method serverHandleMethod = resolveCraftServerMethod("getHandle"); - Method syncMethod = resolveServerMethod(serverHandleMethod, "forceTimeSynchronization"); - return new TimeAccessStrategy(handleMethod, levelDataMethod, readMethod, writeMethod, serverHandleMethod, syncMethod, description); - } - - static TimeAccessStrategy unsupported() { - return new TimeAccessStrategy(null, null, null, null, null, null, "unsupported"); - } - - static TimeAccessStrategy unsupported(Method handleMethod) { - return new TimeAccessStrategy(handleMethod, null, null, null, null, null, "unsupported"); - } - - boolean writable() { - return handleMethod != null && readMethod != null && writeMethod != null; - } - - Object readOwner(Object handle) throws ReflectiveOperationException { - if (levelDataMethod == null) { - return handle; - } - - return levelDataMethod.invoke(handle); - } - - Object[] readArguments(Object handle) { - return new Object[0]; - } - - Object writeOwner(Object handle) throws ReflectiveOperationException { - if (levelDataMethod == null) { - return handle; - } - - return levelDataMethod.invoke(handle); - } - - private static Method resolveCraftServerMethod(String name) { - return CapabilityProbe.attempt("craft server#" + name, () -> { - Method method = Bukkit.getServer().getClass().getMethod(name); - method.setAccessible(true); - return method; - }, null); - } - - private static Method resolveServerMethod(Method serverHandleMethod, String name) { - if (serverHandleMethod == null) { - return null; - } - - return CapabilityProbe.attempt("minecraft server#" + name, () -> { - Object craftServer = Bukkit.getServer(); - if (craftServer == null) { - return null; - } - - Object serverHandle = serverHandleMethod.invoke(craftServer); - if (serverHandle == null) { - return null; - } - - Method method = serverHandle.getClass().getMethod(name); - method.setAccessible(true); - return method; - }, null); - } - } } diff --git a/core/src/main/java/art/arcane/iris/world/runtime/WorldRuntimeControlService.java b/core/src/main/java/art/arcane/iris/world/runtime/WorldRuntimeControlService.java index 75fbc8307..8f22c500c 100644 --- a/core/src/main/java/art/arcane/iris/world/runtime/WorldRuntimeControlService.java +++ b/core/src/main/java/art/arcane/iris/world/runtime/WorldRuntimeControlService.java @@ -31,10 +31,8 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.util.BoundingBox; import org.bukkit.util.VoxelShape; -import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Objects; -import java.util.Optional; import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -437,19 +435,8 @@ public final class WorldRuntimeControlService { public boolean hasMutableClock(World world) { try { - Object handle = invokeNoArg(world, "getHandle"); - if (handle == null) { - return false; - } - - Object dimensionTypeHolder = invokeNoArg(handle, "dimensionTypeRegistration"); - Object dimensionType = unwrapDimensionType(dimensionTypeHolder); - if (dimensionType == null) { - return false; - } - - return !dimensionTypeHasFixedTime(dimensionType); - } catch (Throwable e) { + return capabilities.nativeRuntime() != null && capabilities.nativeRuntime().clock().hasMutableClock(world); + } catch (ReflectiveOperationException | RuntimeException failure) { return false; } } @@ -752,43 +739,6 @@ public final class WorldRuntimeControlService { return builder.toString(); } - private static boolean dimensionTypeHasFixedTime(Object dimensionType) throws ReflectiveOperationException { - Object fixedTimeFlag; - try { - fixedTimeFlag = invokeNoArg(dimensionType, "hasFixedTime"); - } catch (NoSuchMethodException ignored) { - Object fixedTime = invokeNoArg(dimensionType, "fixedTime"); - if (fixedTime instanceof OptionalLong optionalLong) { - return optionalLong.isPresent(); - } - if (fixedTime instanceof Optional optional) { - return optional.isPresent(); - } - return false; - } - - return fixedTimeFlag instanceof Boolean && (Boolean) fixedTimeFlag; - } - - private static Object unwrapDimensionType(Object dimensionTypeHolder) throws ReflectiveOperationException { - if (dimensionTypeHolder == null) { - return null; - } - - Class holderClass = dimensionTypeHolder.getClass(); - if (holderClass.getName().startsWith("net.minecraft.world.level.dimension.")) { - return dimensionTypeHolder; - } - - Method valueMethod = holderClass.getMethod("value"); - return valueMethod.invoke(dimensionTypeHolder); - } - - private static Object invokeNoArg(Object instance, String methodName) throws ReflectiveOperationException { - Method method = instance.getClass().getMethod(methodName); - return method.invoke(instance); - } - @FunctionalInterface interface TeleportExecutor { CompletableFuture teleport(Player player, Location location); diff --git a/core/src/main/java/art/arcane/iris/world/safeguard/Tasks.java b/core/src/main/java/art/arcane/iris/world/safeguard/Tasks.java index 151baef48..c5c3d3868 100644 --- a/core/src/main/java/art/arcane/iris/world/safeguard/Tasks.java +++ b/core/src/main/java/art/arcane/iris/world/safeguard/Tasks.java @@ -1,5 +1,7 @@ package art.arcane.iris.world.safeguard; +import art.arcane.volmlib.nativelib.NativeAdapters; +import art.arcane.volmlib.nativelib.server.NativeServerDiagnostics; import art.arcane.iris.BuildConstants; import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.configuration.IrisSettings; @@ -9,7 +11,6 @@ import art.arcane.iris.world.IrisWorldStorage; import art.arcane.iris.platform.bukkit.nms.INMS; import art.arcane.iris.platform.bukkit.nms.v1X.NMSBinding1X; import art.arcane.iris.diagnostics.splash.IrisSplashComposer; -import art.arcane.iris.spi.CapabilityProbe; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.platform.bootstrap.getHardware; import art.arcane.iris.platform.agent.Agent; @@ -249,8 +250,8 @@ public final class Tasks { private static boolean isCanvasServer() { ClassLoader loader = server().getClass().getClassLoader(); - if (CapabilityProbe.succeeds("io.canvasmc.canvas.region.WorldRegionizer", - () -> Class.forName("io.canvasmc.canvas.region.WorldRegionizer", false, loader))) { + if (NativeAdapters.find(NativeServerDiagnostics.class) + .map(diagnostics -> diagnostics.isCanvas(loader)).orElse(false)) { return true; } return server().getName().toLowerCase(Locale.ROOT).contains("canvas"); diff --git a/core/src/main/java/art/arcane/iris/world/storage/matter/PlatformBlockMatter.java b/core/src/main/java/art/arcane/iris/world/storage/matter/PlatformBlockMatter.java index 1a11c10bd..6f9326df4 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/matter/PlatformBlockMatter.java +++ b/core/src/main/java/art/arcane/iris/world/storage/matter/PlatformBlockMatter.java @@ -1,6 +1,6 @@ package art.arcane.iris.world.storage.matter; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.data.palette.Palette; import art.arcane.volmlib.util.matter.Sliced; @@ -11,29 +11,29 @@ import java.io.DataOutputStream; import java.io.IOException; @Sliced -public class PlatformBlockMatter extends RawMatter { +public class PlatformBlockMatter extends RawMatter { public PlatformBlockMatter() { this(1, 1, 1); } public PlatformBlockMatter(int width, int height, int depth) { - super(width, height, depth, PlatformBlockState.class); + super(width, height, depth, NativeBlockState.class); } @Override - public Palette getGlobalPalette() { + public Palette getGlobalPalette() { return null; } @Override - public void writeNode(PlatformBlockState b, DataOutputStream dos) throws IOException { + public void writeNode(NativeBlockState b, DataOutputStream dos) throws IOException { dos.writeUTF(b.key()); } @Override - public PlatformBlockState readNode(DataInputStream din) throws IOException { - PlatformBlockState state = B.getState(din.readUTF()); + public NativeBlockState readNode(DataInputStream din) throws IOException { + NativeBlockState state = B.getState(din.readUTF()); return state == null ? B.getAirState() : state; } } diff --git a/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatter.java b/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatter.java index 1e85807ad..9283ee214 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatter.java +++ b/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatter.java @@ -1,6 +1,6 @@ package art.arcane.iris.world.storage.matter; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.data.palette.Palette; @@ -114,7 +114,7 @@ public final class PreObjectMatter extends MappedHunk public PreObjectMatterCell readNode(DataInputStream input) throws IOException { int flags = input.readUnsignedByte(); validateFlags(flags); - PlatformBlockState block = (flags & BLOCK_PRESENT) == 0 ? null : B.getState(input.readUTF()); + NativeBlockState block = (flags & BLOCK_PRESENT) == 0 ? null : B.getState(input.readUTF()); String string = (flags & STRING_PRESENT) == 0 ? null : input.readUTF(); MatterCavern cavern = (flags & CAVERN_PRESENT) == 0 ? null diff --git a/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatterCell.java b/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatterCell.java index 903c93d7e..a45bae635 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatterCell.java +++ b/core/src/main/java/art/arcane/iris/world/storage/matter/PreObjectMatterCell.java @@ -1,12 +1,12 @@ package art.arcane.iris.world.storage.matter; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.volmlib.util.matter.MatterCavern; public record PreObjectMatterCell( boolean blockCaptured, - PlatformBlockState block, + NativeBlockState block, boolean stringCaptured, String string, boolean cavernCaptured, @@ -32,7 +32,7 @@ public record PreObjectMatterCell( } } - public static PreObjectMatterCell block(PlatformBlockState value) { + public static PreObjectMatterCell block(NativeBlockState value) { return new PreObjectMatterCell(true, value, false, null, false, null, false, null); } @@ -44,7 +44,7 @@ public record PreObjectMatterCell( return new PreObjectMatterCell(false, null, false, null, true, value, false, null); } - public PreObjectMatterCell captureBlock(PlatformBlockState value) { + public PreObjectMatterCell captureBlock(NativeBlockState value) { if (blockCaptured) { return this; } @@ -78,7 +78,7 @@ public record PreObjectMatterCell( if (type == HydrologyCaveCell.class) { return hydrologyCaptured; } - if (type == PlatformBlockState.class) { + if (type == NativeBlockState.class) { return blockCaptured; } if (type == String.class) { @@ -95,7 +95,7 @@ public record PreObjectMatterCell( if (!captures(type)) { throw new IllegalArgumentException("Unsupported or uncaptured pre-object type " + type.getCanonicalName()); } - if (type == PlatformBlockState.class) { + if (type == NativeBlockState.class) { return (T) block; } if (type == String.class) { diff --git a/core/src/main/java/art/arcane/iris/world/storage/region/NBTWorld.java b/core/src/main/java/art/arcane/iris/world/storage/region/NBTWorld.java index a114d8544..cf6b61ee1 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/region/NBTWorld.java +++ b/core/src/main/java/art/arcane/iris/world/storage/region/NBTWorld.java @@ -20,7 +20,7 @@ package art.arcane.iris.world.storage.region; import art.arcane.iris.platform.bukkit.BukkitBlockResolution; import art.arcane.volmlib.util.nbt.mca.MCABlockStateCodecSupport; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.platform.bukkit.nms.INMS; @@ -133,6 +133,10 @@ public class NBTWorld { ); } + public static NBTWorldSupport.BlockStateCodec blockStateCodec() { + return Holder.BLOCK_STATE_CODEC; + } + public static BlockData getBlockData(Tag tag) { return Holder.BLOCK_STATE_CODEC.decode(tag); } diff --git a/core/src/main/java/art/arcane/iris/world/storage/region/Section.java b/core/src/main/java/art/arcane/iris/world/storage/region/Section.java index 25002b70e..77e67bbba 100644 --- a/core/src/main/java/art/arcane/iris/world/storage/region/Section.java +++ b/core/src/main/java/art/arcane/iris/world/storage/region/Section.java @@ -31,7 +31,7 @@ public class Section implements MCASectionLike { } public Section(CompoundTag sectionRoot, int dataVersion, long loadFlags) { - support = new MCASectionSupport(sectionRoot, loadFlags, () -> INMS.get().createPalette()); + support = new MCASectionSupport(sectionRoot, loadFlags, () -> INMS.get().createPalette(NBTWorld.blockStateCodec())); } private Section(MCASectionSupport support) { @@ -44,7 +44,7 @@ public class Section implements MCASectionLike { * @return An empty Section */ public static Section newSection() { - return new Section(MCASectionSupport.createNew(() -> INMS.get().createPalette())); + return new Section(MCASectionSupport.createNew(() -> INMS.get().createPalette(NBTWorld.blockStateCodec()))); } /** diff --git a/core/src/main/java/art/arcane/iris/world/tree/TreeSVC.java b/core/src/main/java/art/arcane/iris/world/tree/TreeSVC.java index 139cd0036..b3183579f 100644 --- a/core/src/main/java/art/arcane/iris/world/tree/TreeSVC.java +++ b/core/src/main/java/art/arcane/iris/world/tree/TreeSVC.java @@ -38,7 +38,7 @@ import art.arcane.iris.generation.decoration.tree.IrisTreeSize; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.platform.generation.PlatformChunkGenerator; import art.arcane.iris.platform.bukkit.BukkitBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.data.Cuboid; @@ -183,7 +183,7 @@ public class TreeSVC implements IrisService { } @Override - public void set(int x, int y, int z, PlatformBlockState s) { + public void set(int x, int y, int z, NativeBlockState s) { BlockData d = (BlockData) s.nativeHandle(); Block b = event.getWorld().getBlockAt(x, y, z); // Listeners get the post-growth tree, per the StructureGrowEvent contract; @@ -197,7 +197,7 @@ public class TreeSVC implements IrisService { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return BukkitBlockState.of(event.getWorld().getBlockAt(x, y, z).getBlockData()); } diff --git a/core/src/test/java/art/arcane/iris/command/handlers/DataVersionHandlerTest.java b/core/src/test/java/art/arcane/iris/command/handlers/DataVersionHandlerTest.java index 4c7eac63f..dd0f38239 100644 --- a/core/src/test/java/art/arcane/iris/command/handlers/DataVersionHandlerTest.java +++ b/core/src/test/java/art/arcane/iris/command/handlers/DataVersionHandlerTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.command.handlers; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; import org.junit.Test; diff --git a/core/src/test/java/art/arcane/iris/command/specialhandlers/StructureHandlerTest.java b/core/src/test/java/art/arcane/iris/command/specialhandlers/StructureHandlerTest.java index b6c99fe5c..32008c700 100644 --- a/core/src/test/java/art/arcane/iris/command/specialhandlers/StructureHandlerTest.java +++ b/core/src/test/java/art/arcane/iris/command/specialhandlers/StructureHandlerTest.java @@ -16,7 +16,7 @@ import art.arcane.iris.structure.placement.StructureDistribution; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.director.exceptions.DirectorParsingException; @@ -82,7 +82,7 @@ public class StructureHandlerTest { Engine engine = mock(Engine.class); IrisDimension dimension = mock(IrisDimension.class); IrisWorld world = mock(IrisWorld.class); - PlatformWorld platformWorld = mock(PlatformWorld.class); + NativeWorld platformWorld = mock(NativeWorld.class); IrisPlatform platform = mock(IrisPlatform.class); PlatformStructureHooks hooks = mock(PlatformStructureHooks.class); IrisImportedStructureControl control = new IrisImportedStructureControl(); @@ -159,7 +159,7 @@ public class StructureHandlerTest { Engine engine = mock(Engine.class); IrisDimension dimension = mock(IrisDimension.class); IrisWorld world = mock(IrisWorld.class); - PlatformWorld platformWorld = mock(PlatformWorld.class); + NativeWorld platformWorld = mock(NativeWorld.class); IrisPlatform platform = mock(IrisPlatform.class); PlatformStructureHooks hooks = mock(PlatformStructureHooks.class); IrisImportedStructureControl control = new IrisImportedStructureControl(); diff --git a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCeilingLayerTest.java b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCeilingLayerTest.java index 88673afa0..2999f1c88 100644 --- a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCeilingLayerTest.java +++ b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCeilingLayerTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -18,13 +18,13 @@ import static org.mockito.Mockito.mock; public class IrisBiomeCeilingLayerTest { @Test public void emitsEveryCeilingLayerWithoutSurfaceLayers() { - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); - PlatformBlockState third = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); + NativeBlockState third = mock(NativeBlockState.class); IrisBiome biome = new IrisBiome().setLayers(new KList<>()).setCaveCeilingLayers( new KList<>(layer(first, 2), layer(second, 1), layer(third, 2))); - KList result = biome.generateCeilingLayers( + KList result = biome.generateCeilingLayers( new IrisDimension(), 13, -27, new RNG(1), 8, 64, null, null); assertEquals(List.of(first, first, second, third, third), result); @@ -38,9 +38,9 @@ public class IrisBiomeCeilingLayerTest { RNG rng = new RNG(37); second.getLayerHeightGenerators(rng, null); for (int x = -200; x <= 200; x += 7) { - KList firstCeiling = first.generateCeilingLayers( + KList firstCeiling = first.generateCeilingLayers( new IrisDimension(), x, -x, rng, 100, 120, null, null); - KList secondCeiling = second.generateCeilingLayers( + KList secondCeiling = second.generateCeilingLayers( new IrisDimension(), x, -x, rng, 100, 120, null, null); assertEquals(firstCeiling.size(), secondCeiling.size()); } @@ -51,8 +51,8 @@ public class IrisBiomeCeilingLayerTest { @Test public void capsCeilingLayersAtAvailableDepth() { - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); IrisBiome biome = new IrisBiome().setCaveCeilingLayers(new KList<>(layer(first, 2), layer(second, 3))); assertEquals(List.of(first, first, second), biome.generateCeilingLayers( new IrisDimension(), 0, 0, new RNG(1), 3, 64, null, null)); @@ -65,14 +65,14 @@ public class IrisBiomeCeilingLayerTest { assertTrue(new IrisBiome().getCaveCeilingLayers().isEmpty()); } - static IrisBiomePaletteLayer layer(PlatformBlockState block, int thickness) { + static IrisBiomePaletteLayer layer(NativeBlockState block, int thickness) { IrisBiomePaletteLayer layer = new IrisBiomePaletteLayer().setMinHeight(thickness).setMaxHeight(thickness); layer.getBlockData().aquire(() -> new KList<>(block)); return layer; } private static IrisBiome variableBiome(int surfaceMinimum, int surfaceMaximum) { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); return new IrisBiome() .setLayers(new KList<>(layer(block, surfaceMinimum).setMaxHeight(surfaceMaximum))) .setCaveCeilingLayers(new KList<>(layer(block, 1).setMaxHeight(9))); diff --git a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCustomCompatTest.java b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCustomCompatTest.java index f9e81c44e..b0a837c32 100644 --- a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCustomCompatTest.java +++ b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeCustomCompatTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.validation.PackCompatReport; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.json.JSONArray; diff --git a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLayerSlopeSourceTest.java b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLayerSlopeSourceTest.java index 414d8d2ac..fccdf0d74 100644 --- a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLayerSlopeSourceTest.java +++ b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLayerSlopeSourceTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.collection.KList; @@ -56,7 +56,7 @@ public final class IrisBiomeLayerSlopeSourceTest { IrisComplex complex = mock(IrisComplex.class); IrisBiomePaletteLayer grass = mock(IrisBiomePaletteLayer.class); CNG heightGenerator = mock(CNG.class); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); RNG rng = new RNG(19L); IrisBiome biome = new IrisBiome().setLockLayers(lockLayers).setLayers(new KList<>(grass)); when(complex.hasTerrain3D()).thenReturn(true); @@ -70,9 +70,9 @@ public final class IrisBiomeLayerSlopeSourceTest { when(heightGenerator.fit(1, 1, 12D, -8D)).thenReturn(1); when(grass.get(rng, 0, 12D, 0D, -8D, data)).thenReturn(block); - KList ledge = biome.generateLayers( + KList ledge = biome.generateLayers( rockFallbackDimension(), 12D, -8D, rng, 1, 32, data, complex); - KList cap = biome.generateLayers( + KList cap = biome.generateLayers( rockFallbackDimension(), 12D, -8D, rng, 1, 96, data, complex); assertEquals(1, ledge.size()); @@ -90,7 +90,7 @@ public final class IrisBiomeLayerSlopeSourceTest { ProceduralStream sourceSlope = mock(ProceduralStream.class); IrisBiomePaletteLayer paletteLayer = mock(IrisBiomePaletteLayer.class); CNG heightGenerator = mock(CNG.class); - PlatformBlockState surfaceBlock = mock(PlatformBlockState.class); + NativeBlockState surfaceBlock = mock(NativeBlockState.class); RNG rng = new RNG(19L); IrisBiome biome = new IrisBiome(); @@ -110,9 +110,9 @@ public final class IrisBiomeLayerSlopeSourceTest { when(heightGenerator.fit(1, 1, 12D, -8D)).thenReturn(1); when(paletteLayer.get(rng, 0, 12D, 0D, -8D, data)).thenReturn(surfaceBlock); - KList hostLayers = biome.generateLayers( + KList hostLayers = biome.generateLayers( rockFallbackDimension(), 12D, -8D, rng, 1, 32, data, hostComplex); - KList sourceLayers = biome.generateLayersWithSlope( + KList sourceLayers = biome.generateLayersWithSlope( rockFallbackDimension(), 12D, -8D, rng, 1, 32, data, sourceSlope); assertTrue(hostLayers.isEmpty()); diff --git a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLockedLayerTest.java b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLockedLayerTest.java index 86f0aaa7c..0e608e0d7 100644 --- a/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLockedLayerTest.java +++ b/core/src/test/java/art/arcane/iris/generation/biome/IrisBiomeLockedLayerTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.biome; import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import art.arcane.volmlib.util.noise.CNG; @@ -29,18 +29,18 @@ import static org.mockito.Mockito.verify; public class IrisBiomeLockedLayerTest { @Test public void repeatsLockedLayersAcrossTheReferenceHeight() { - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); - PlatformBlockState third = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); + NativeBlockState third = mock(NativeBlockState.class); IrisBiome biome = new IrisBiome().setLockLayers(true).setLockLayersMax(8).setLayers(new KList<>( IrisBiomeCeilingLayerTest.layer(first, 1), IrisBiomeCeilingLayerTest.layer(second, 1), IrisBiomeCeilingLayerTest.layer(third, 1))); - List cycle = List.of(first, second, third); + List cycle = List.of(first, second, third); int[] heights = {1, 509, 512, 513, 1000, Integer.MAX_VALUE}; int[][] expectedCycles = {{1, 0, 2}, {0, 2, 1}, {0, 2, 1}, {2, 1, 0}, {1, 0, 2}, {1, 0, 2}}; for (int index = 0; index < heights.length; index++) { - KList blocks = biome.generateLockedLayers( + KList blocks = biome.generateLockedLayers( 13, -27, new RNG(37), 8, heights[index], null, null); assertEquals(8, blocks.size()); for (int depth = 0; depth < blocks.size(); depth++) { @@ -51,9 +51,9 @@ public class IrisBiomeLockedLayerTest { @Test public void sampledBandsMatchExpandedVariableMixedPalettes() { - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); - PlatformBlockState third = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); + NativeBlockState third = mock(NativeBlockState.class); long[] seeds = {1L, 37L, -291L, Long.MAX_VALUE}; int[] heights = {Integer.MIN_VALUE, -1000, -1, 0, 509, 512, 513, 1000, Integer.MAX_VALUE}; int[] depths = {0, 1, 7, 39}; @@ -80,9 +80,9 @@ public class IrisBiomeLockedLayerTest { @Test public void nullEntriesAndRepeatedCyclesMatchExpandedBands() { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); IrisBiome biome = new IrisBiome().setLockLayers(true).setLockLayersMax(30).setLayers(new KList<>( - palette(1, 1, 1D, block), palette(2, 2, 1D, (PlatformBlockState) null))); + palette(1, 1, 1D, block), palette(2, 2, 1D, (NativeBlockState) null))); RNG rng = new RNG(37L); assertEquals(expandedBands(biome, 3D, -11D, rng, 30, 1000, 0D), biome.generateLockedLayers(3D, -11D, rng, 30, 1000, null, null)); @@ -93,7 +93,7 @@ public class IrisBiomeLockedLayerTest { IrisBiome biome = new IrisBiome().setLockLayers(true).setLayers(new KList<>()); RNG rng = new RNG(37L); assertTrue(biome.generateLockedLayers(0D, 0D, rng, 7, 64, null, null).isEmpty()); - biome.setLayers(new KList<>(palette(0, 0, 1D, mock(PlatformBlockState.class)))); + biome.setLayers(new KList<>(palette(0, 0, 1D, mock(NativeBlockState.class)))); assertTrue(biome.generateLockedLayers(0D, 0D, rng, 7, 64, null, null).isEmpty()); assertTrue(biome.generateLockedLayers(0D, 0D, rng, -1, 64, null, null).isEmpty()); } @@ -101,7 +101,7 @@ public class IrisBiomeLockedLayerTest { @Test public void resolvesOnlyRequestedPositionsOfLongBands() { IrisBiomePaletteLayer layer = spy(palette(1000, 1000, 0.6D, - mock(PlatformBlockState.class), mock(PlatformBlockState.class))); + mock(NativeBlockState.class), mock(NativeBlockState.class))); IrisBiome biome = new IrisBiome().setLockLayers(true).setLayers(new KList<>(layer)); RNG rng = new RNG(37L); assertEquals(7, biome.generateLockedLayers(13D, -27D, rng, 7, 64, null, null).size()); @@ -111,7 +111,7 @@ public class IrisBiomeLockedLayerTest { @Test public void repeatedCyclesResolveEachPositionOnce() { IrisBiomePaletteLayer layer = spy(palette(3, 3, 0.6D, - mock(PlatformBlockState.class), mock(PlatformBlockState.class))); + mock(NativeBlockState.class), mock(NativeBlockState.class))); IrisBiome biome = new IrisBiome().setLockLayers(true).setLockLayersMax(100).setLayers(new KList<>(layer)); RNG rng = new RNG(37L); assertEquals(100, biome.generateLockedLayers(13D, -27D, rng, 100, 64, null, null).size()); @@ -121,9 +121,9 @@ public class IrisBiomeLockedLayerTest { @Test public void slopeFilteredBandsMatchExpandedCycle() { IrisBiome biome = new IrisBiome().setLockLayers(true).setLockLayersMax(30).setLayers(new KList<>( - palette(2, 4, 0.6D, mock(PlatformBlockState.class)), - palette(3, 8, 1D, mock(PlatformBlockState.class)).setSlopeCondition(new IrisSlopeClip(3D, 255D)), - palette(1, 5, 2D, mock(PlatformBlockState.class)).setSlopeCondition(new IrisSlopeClip(0D, 3D)))); + palette(2, 4, 0.6D, mock(NativeBlockState.class)), + palette(3, 8, 1D, mock(NativeBlockState.class)).setSlopeCondition(new IrisSlopeClip(3D, 255D)), + palette(1, 5, 2D, mock(NativeBlockState.class)).setSlopeCondition(new IrisSlopeClip(0D, 3D)))); RNG rng = new RNG(37L); double[] slopes = {0D, 3D, 8D}; for (double slope : slopes) { @@ -135,7 +135,7 @@ public class IrisBiomeLockedLayerTest { @Test public void sampledPaletteFailuresAreReported() { - IrisBiomePaletteLayer layer = spy(palette(1, 1, 1D, mock(PlatformBlockState.class))); + IrisBiomePaletteLayer layer = spy(palette(1, 1, 1D, mock(NativeBlockState.class))); IrisBiome biome = new IrisBiome().setLockLayers(true).setLayers(new KList<>(layer)); RNG rng = new RNG(37L); IllegalStateException failure = new IllegalStateException("Palette unavailable"); @@ -146,17 +146,17 @@ public class IrisBiomeLockedLayerTest { } } - private static IrisBiomePaletteLayer palette(int minimum, int maximum, double zoom, PlatformBlockState... blocks) { + private static IrisBiomePaletteLayer palette(int minimum, int maximum, double zoom, NativeBlockState... blocks) { IrisBiomePaletteLayer layer = new IrisBiomePaletteLayer().setMinHeight(minimum).setMaxHeight(maximum) .setZoom(zoom).setStyle(NoiseStyle.NOWHERE.style()); layer.getBlockData().aquire(() -> new KList<>(blocks)); return layer; } - private static KList expandedBands(IrisBiome biome, double x, double z, RNG rng, + private static KList expandedBands(IrisBiome biome, double x, double z, RNG rng, int maxDepth, int height, double slope) { - KList expanded = new KList<>(); - KList result = new KList<>(); + KList expanded = new KList<>(); + KList result = new KList<>(); KList heights = biome.getLayerHeightGenerators(rng, null); for (int index = 0; index < biome.getLayers().size(); index++) { IrisBiomePaletteLayer layer = biome.getLayers().get(index); diff --git a/core/src/test/java/art/arcane/iris/generation/biome/IrisSurfaceLayerFallbackTest.java b/core/src/test/java/art/arcane/iris/generation/biome/IrisSurfaceLayerFallbackTest.java index 987470015..a3258ac5d 100644 --- a/core/src/test/java/art/arcane/iris/generation/biome/IrisSurfaceLayerFallbackTest.java +++ b/core/src/test/java/art/arcane/iris/generation/biome/IrisSurfaceLayerFallbackTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.collection.KList; @@ -23,8 +23,8 @@ public final class IrisSurfaceLayerFallbackTest { private final IrisData data = mock(IrisData.class); private final IrisBiomePaletteLayer layer = mock(IrisBiomePaletteLayer.class); private final CNG heightGenerator = mock(CNG.class); - private final PlatformBlockState surfaceBlock = mock(PlatformBlockState.class); - private final PlatformBlockState secondBlock = mock(PlatformBlockState.class); + private final NativeBlockState surfaceBlock = mock(NativeBlockState.class); + private final NativeBlockState secondBlock = mock(NativeBlockState.class); private final RNG rng = new RNG(19L); @SuppressWarnings("unchecked") @@ -42,7 +42,7 @@ public final class IrisSurfaceLayerFallbackTest { return new IrisBiome().setLayers(new KList<>(layer)); } - private KList generate(IrisBiome biome, IrisDimension dim) { + private KList generate(IrisBiome biome, IrisDimension dim) { return biome.generateLayersWithSlope(dim, 12D, -8D, rng, 4, 32, data, slopeStream); } @@ -51,7 +51,7 @@ public final class IrisSurfaceLayerFallbackTest { IrisBiome biome = biome(); when(layer.getSlopeCondition()).thenReturn(new IrisSlopeClip(0D, 4D)); - KList blocks = generate(biome, + KList blocks = generate(biome, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER)); assertEquals(1, blocks.size()); @@ -63,7 +63,7 @@ public final class IrisSurfaceLayerFallbackTest { IrisBiome biome = biome(); when(layer.getSlopeCondition()).thenReturn(new IrisSlopeClip(0D, 4D)); - KList blocks = generate(biome, + KList blocks = generate(biome, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.ROCK)); assertTrue(blocks.isEmpty()); @@ -74,9 +74,9 @@ public final class IrisSurfaceLayerFallbackTest { IrisBiome biome = biome(); when(layer.getSlopeCondition()).thenReturn(new IrisSlopeClip()); - KList topLayer = generate(biome, + KList topLayer = generate(biome, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER)); - KList rock = generate(biome, + KList rock = generate(biome, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.ROCK)); assertEquals(2, topLayer.size()); @@ -90,7 +90,7 @@ public final class IrisSurfaceLayerFallbackTest { IrisBiome pinnedToTopLayer = biome().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER); when(layer.getSlopeCondition()).thenReturn(new IrisSlopeClip(0D, 4D)); - KList overRock = generate(pinnedToTopLayer, + KList overRock = generate(pinnedToTopLayer, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.ROCK)); assertEquals(1, overRock.size()); @@ -98,7 +98,7 @@ public final class IrisSurfaceLayerFallbackTest { IrisBiome pinnedToRock = biome().setSurfaceLayerFallback(IrisSurfaceLayerFallback.ROCK); - KList overTopLayer = generate(pinnedToRock, + KList overTopLayer = generate(pinnedToRock, new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER)); assertTrue(overTopLayer.isEmpty()); @@ -110,7 +110,7 @@ public final class IrisSurfaceLayerFallbackTest { when(layer.getSlopeCondition()).thenReturn(new IrisSlopeClip(0D, 4D)); IrisDimension dimension = new IrisDimension().setSurfaceLayerFallback(IrisSurfaceLayerFallback.TOP_LAYER); - KList blocks = generate(biome, dimension); + KList blocks = generate(biome, dimension); assertEquals(1, blocks.size()); assertSame(surfaceBlock, blocks.get(0)); diff --git a/core/src/test/java/art/arcane/iris/generation/block/PlatformStateRebindTest.java b/core/src/test/java/art/arcane/iris/generation/block/PlatformStateRebindTest.java index 47b9f7d3d..a0af9659f 100644 --- a/core/src/test/java/art/arcane/iris/generation/block/PlatformStateRebindTest.java +++ b/core/src/test/java/art/arcane/iris/generation/block/PlatformStateRebindTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.block; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.chunk.ChunkDataHunkHolder; @@ -46,7 +46,7 @@ public class PlatformStateRebindTest { @Test public void chunkDataAirIsResolvedAgainstTheCurrentlyBoundPlatform() { - PlatformBlockState first = bindPlatform("first"); + NativeBlockState first = bindPlatform("first"); ChunkGenerator.ChunkData chunk = mock(ChunkGenerator.ChunkData.class); when(chunk.getMinHeight()).thenReturn(-64); when(chunk.getMaxHeight()).thenReturn(320); @@ -55,26 +55,26 @@ public class PlatformStateRebindTest { assertSame(first, holder.getRaw(0, -1, 0)); IrisPlatforms.unbind(); - PlatformBlockState second = bindPlatform("second"); + NativeBlockState second = bindPlatform("second"); assertSame(second, holder.getRaw(0, -1, 0)); } @Test public void mantleAirIsResolvedAgainstTheCurrentlyBoundPlatform() { - PlatformBlockState first = bindPlatform("first"); + NativeBlockState first = bindPlatform("first"); assertSame(first, EngineMantle.AIR.get()); IrisPlatforms.unbind(); - PlatformBlockState second = bindPlatform("second"); + NativeBlockState second = bindPlatform("second"); assertSame(second, EngineMantle.AIR.get()); } @Test public void aLatchKeepsServingTheLastResolutionWhileNothingIsBound() { - PlatformBlockState first = bindPlatform("first"); + NativeBlockState first = bindPlatform("first"); BoundBlockState state = BoundBlockState.of("AIR"); assertSame(first, state.get()); @@ -105,20 +105,20 @@ public class PlatformStateRebindTest { continue; } Class fieldType = field.getType(); - if (PlatformBlockState.class.isAssignableFrom(fieldType) - || (fieldType.isArray() && PlatformBlockState.class.isAssignableFrom(fieldType.getComponentType()))) { + if (NativeBlockState.class.isAssignableFrom(fieldType) + || (fieldType.isArray() && NativeBlockState.class.isAssignableFrom(fieldType.getComponentType()))) { latched.add(type.getName() + "." + field.getName()); } } } - private static PlatformBlockState bindPlatform(String label) { - Map states = new HashMap<>(); + private static NativeBlockState bindPlatform(String label) { + Map states = new HashMap<>(); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenAnswer(invocation -> states.computeIfAbsent( label + ':' + invocation.getArgument(0), key -> { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); return state; })); diff --git a/core/src/test/java/art/arcane/iris/generation/cache/AtomicCacheTest.java b/core/src/test/java/art/arcane/iris/generation/cache/AtomicCacheTest.java index 1b9bc4758..c6460bf75 100644 --- a/core/src/test/java/art/arcane/iris/generation/cache/AtomicCacheTest.java +++ b/core/src/test/java/art/arcane/iris/generation/cache/AtomicCacheTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.generation.cache; +import art.arcane.volmlib.util.cache.AtomicCache; + import org.junit.Test; import java.util.concurrent.atomic.AtomicInteger; diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/DecoratorCoreTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/DecoratorCoreTest.java index 92365080c..5e0557302 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/DecoratorCoreTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/DecoratorCoreTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.volmlib.util.collection.KList; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.math.RNG; @@ -153,16 +153,16 @@ public class DecoratorCoreTest { public void singleStackTargetsBlockAboveSupport() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState air = mock(PlatformBlockState.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState air = mock(NativeBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); when(decorant.key()).thenReturn("minecraft:stone"); when(decorant.canPlaceOnto(support)).thenReturn(true); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(1); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 4, 1); + Hunk output = Hunk.newArrayHunk(1, 4, 1); output.set(0, 1, 0, support); output.set(0, 2, 0, air); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); @@ -177,15 +177,15 @@ public class DecoratorCoreTest { public void singleStackDoesNotOverwriteOccupiedTarget() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState occupied = mock(PlatformBlockState.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState occupied = mock(NativeBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); when(occupied.isAir()).thenReturn(false); when(occupied.isFluid()).thenReturn(false); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(1); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 4, 1); + Hunk output = Hunk.newArrayHunk(1, 4, 1); output.set(0, 1, 0, support); output.set(0, 2, 0, occupied); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); @@ -200,10 +200,10 @@ public class DecoratorCoreTest { public void multiStackStopsBeforeOccupiedTarget() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState air = mock(PlatformBlockState.class); - PlatformBlockState occupied = mock(PlatformBlockState.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState air = mock(NativeBlockState.class); + NativeBlockState occupied = mock(NativeBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); when(occupied.isAir()).thenReturn(false); when(occupied.isFluid()).thenReturn(false); @@ -214,7 +214,7 @@ public class DecoratorCoreTest { when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 5, 1); + Hunk output = Hunk.newArrayHunk(1, 5, 1); output.set(0, 1, 0, support); output.set(0, 2, 0, air); output.set(0, 3, 0, occupied); @@ -232,14 +232,14 @@ public class DecoratorCoreTest { public void descendingStackStopsAtLocalWorldFloor() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); when(decorant.key()).thenReturn("minecraft:stone"); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(2); when(decorator.getTopThreshold()).thenReturn(1.0); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 2, 1); + Hunk output = Hunk.newArrayHunk(1, 2, 1); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); DecoratorCore.placeStackDown( @@ -252,10 +252,10 @@ public class DecoratorCoreTest { public void singleDescendingStackPreservesTargetWhenPickIsNull() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState existing = mock(PlatformBlockState.class); + NativeBlockState existing = mock(NativeBlockState.class); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(1); - Hunk output = Hunk.newArrayHunk(1, 2, 1); + Hunk output = Hunk.newArrayHunk(1, 2, 1); output.set(0, 0, 0, existing); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); @@ -269,12 +269,12 @@ public class DecoratorCoreTest { public void multiDescendingStackStopsWhenPickIsNull() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState lowerExisting = mock(PlatformBlockState.class); - PlatformBlockState upperExisting = mock(PlatformBlockState.class); + NativeBlockState lowerExisting = mock(NativeBlockState.class); + NativeBlockState upperExisting = mock(NativeBlockState.class); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(2); when(decorator.getTopThreshold()).thenReturn(1.0); - Hunk output = Hunk.newArrayHunk(1, 2, 1); + Hunk output = Hunk.newArrayHunk(1, 2, 1); output.set(0, 0, 0, lowerExisting); output.set(0, 1, 0, upperExisting); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); @@ -290,14 +290,14 @@ public class DecoratorCoreTest { public void floatingStackStopsAtHunkCeiling() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); when(decorant.key()).thenReturn("minecraft:stone"); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(3); when(decorator.getTopThreshold()).thenReturn(1.0); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 2, 1); + Hunk output = Hunk.newArrayHunk(1, 2, 1); int placed = DecoratorCore.placeFloatingStacked( decorator, 0, 0, 0, 0, 0, 3, output, new RNG(1L), data, null); @@ -309,13 +309,13 @@ public class DecoratorCoreTest { public void tallSurfacePlantDoesNotPlaceWhenUpperTargetIsOccupied() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState air = airState(); - PlatformBlockState occupied = mock(PlatformBlockState.class); - PlatformBlockState plant = tallPlantState(); + NativeBlockState support = sturdyState(); + NativeBlockState air = airState(); + NativeBlockState occupied = mock(NativeBlockState.class); + NativeBlockState plant = tallPlantState(); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(plant); - Hunk output = Hunk.newArrayHunk(1, 4, 1); + Hunk output = Hunk.newArrayHunk(1, 4, 1); output.set(0, 0, 0, support); output.set(0, 1, 0, air); output.set(0, 2, 0, occupied); @@ -331,12 +331,12 @@ public class DecoratorCoreTest { public void tallFloatingPlantDoesNotPlaceWhenUpperTargetIsOccupied() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState air = airState(); - PlatformBlockState occupied = mock(PlatformBlockState.class); - PlatformBlockState plant = tallPlantState(); + NativeBlockState air = airState(); + NativeBlockState occupied = mock(NativeBlockState.class); + NativeBlockState plant = tallPlantState(); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(plant); - Hunk output = Hunk.newArrayHunk(1, 4, 1); + Hunk output = Hunk.newArrayHunk(1, 4, 1); output.set(0, 1, 0, air); output.set(0, 2, 0, occupied); @@ -351,16 +351,16 @@ public class DecoratorCoreTest { public void tallSurfacePlantPlacesBothHalvesTogether() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState lowerAir = airState(); - PlatformBlockState upperAir = airState(); - PlatformBlockState lower = mock(PlatformBlockState.class); - PlatformBlockState upper = mock(PlatformBlockState.class); - PlatformBlockState plant = tallPlantState(lower, upper); + NativeBlockState support = sturdyState(); + NativeBlockState lowerAir = airState(); + NativeBlockState upperAir = airState(); + NativeBlockState lower = mock(NativeBlockState.class); + NativeBlockState upper = mock(NativeBlockState.class); + NativeBlockState plant = tallPlantState(lower, upper); when(plant.canPlaceOnto(support)).thenReturn(true); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(plant); - Hunk output = Hunk.newArrayHunk(1, 4, 1); + Hunk output = Hunk.newArrayHunk(1, 4, 1); output.set(0, 0, 0, support); output.set(0, 1, 0, lowerAir); output.set(0, 2, 0, upperAir); @@ -376,13 +376,13 @@ public class DecoratorCoreTest { public void surfaceDecorantRejectsInvalidNativeSupport() { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState air = airState(); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState air = airState(); + NativeBlockState decorant = mock(NativeBlockState.class); when(decorant.canPlaceOnto(support)).thenReturn(false); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(decorant); - Hunk output = Hunk.newArrayHunk(1, 3, 1); + Hunk output = Hunk.newArrayHunk(1, 3, 1); output.set(0, 0, 0, support); output.set(0, 1, 0, air); @@ -397,16 +397,16 @@ public class DecoratorCoreTest { IrisDecorator decorator = mock(IrisDecorator.class); IrisBlockData forcedBlock = mock(IrisBlockData.class); IrisData data = mock(IrisData.class); - PlatformBlockState support = sturdyState(); - PlatformBlockState farmland = sturdyState(); - PlatformBlockState air = airState(); - PlatformBlockState wheat = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState farmland = sturdyState(); + NativeBlockState air = airState(); + NativeBlockState wheat = mock(NativeBlockState.class); when(wheat.key()).thenReturn("minecraft:wheat[age=7]"); when(decorator.getForceBlock()).thenReturn(forcedBlock); when(forcedBlock.getBlockData(data)).thenReturn(farmland); when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(wheat); - Hunk output = Hunk.newArrayHunk(1, 3, 1); + Hunk output = Hunk.newArrayHunk(1, 3, 1); output.set(0, 0, 0, support); output.set(0, 1, 0, air); @@ -419,7 +419,7 @@ public class DecoratorCoreTest { @Test public void descendingWeepingVinesUsePlantBodiesAndOneFreeEndTip() { - PlatformBlockState vine = mock(PlatformBlockState.class); + NativeBlockState vine = mock(NativeBlockState.class); when(vine.key()).thenReturn("minecraft:weeping_vines"); assertEquals("minecraft:weeping_vines_plant", DecoratorCore.stackedVineKey(vine, 3, 0)); @@ -429,7 +429,7 @@ public class DecoratorCoreTest { @Test public void ascendingTwistingVinesUsePlantBodiesAndOneFreeEndTip() { - PlatformBlockState vine = mock(PlatformBlockState.class); + NativeBlockState vine = mock(NativeBlockState.class); when(vine.key()).thenReturn("minecraft:twisting_vines_plant"); assertEquals("minecraft:twisting_vines_plant", DecoratorCore.stackedVineKey(vine, 3, 0)); @@ -439,7 +439,7 @@ public class DecoratorCoreTest { @Test public void sulfurStalagmiteKeepsSulfurMaterialAndTapersToOneTip() { - Hunk output = placeSpikeColumn("minecraft:sulfur_spike", 5, true, false); + Hunk output = placeSpikeColumn("minecraft:sulfur_spike", 5, true, false); assertSpikeColumn(output, "minecraft:sulfur_spike", true, false, "base", "middle", "middle", "frustum", "tip"); @@ -447,7 +447,7 @@ public class DecoratorCoreTest { @Test public void sulfurStalactiteKeepsWaterloggingAndPointsDown() { - Hunk output = placeSpikeColumn("minecraft:sulfur_spike", 5, false, true); + Hunk output = placeSpikeColumn("minecraft:sulfur_spike", 5, false, true); assertSpikeColumn(output, "minecraft:sulfur_spike", false, true, "base", "middle", "middle", "frustum", "tip"); @@ -456,8 +456,8 @@ public class DecoratorCoreTest { @Test public void shortSulfurSpikesHaveCorrectTipsInBothDirections() { for (boolean upward : new boolean[]{true, false}) { - Hunk single = placeSpikeColumn("minecraft:sulfur_spike", 1, upward, false); - Hunk pair = placeSpikeColumn("minecraft:sulfur_spike", 2, upward, false); + Hunk single = placeSpikeColumn("minecraft:sulfur_spike", 1, upward, false); + Hunk pair = placeSpikeColumn("minecraft:sulfur_spike", 2, upward, false); assertSpikeColumn(single, "minecraft:sulfur_spike", upward, false, "tip"); assertSpikeColumn(pair, "minecraft:sulfur_spike", upward, false, "frustum", "tip"); @@ -466,7 +466,7 @@ public class DecoratorCoreTest { @Test public void pointedDripstoneRetainsWaterloggingWhenStacked() { - Hunk output = placeSpikeColumn("minecraft:pointed_dripstone", 4, true, true); + Hunk output = placeSpikeColumn("minecraft:pointed_dripstone", 4, true, true); assertSpikeColumn(output, "minecraft:pointed_dripstone", true, true, "base", "middle", "frustum", "tip"); @@ -476,7 +476,7 @@ public class DecoratorCoreTest { public void clippedSpikesRebuildTheirFreeEndWithoutOverwritingObstructions() { for (boolean upward : new boolean[]{true, false}) { SpikeFixture fixture = new SpikeFixture(5, upward); - PlatformBlockState obstruction = sturdyState(); + NativeBlockState obstruction = sturdyState(); fixture.output.set(0, upward ? 4 : 2, 0, obstruction); fixture.place(); @@ -501,19 +501,19 @@ public class DecoratorCoreTest { public void spikeStacksHonorSurfacePalettesInBothDirections() { for (boolean upward : new boolean[]{true, false}) { SpikeFixture fixture = new SpikeFixture(3, upward); - PlatformBlockState allowed = sturdyState(); + NativeBlockState allowed = sturdyState(); when(fixture.decorator.getWhitelist()).thenReturn(new KList<>()); - when(fixture.decorator.getWhitelistArray(fixture.data)).thenReturn(new PlatformBlockState[]{allowed}); + when(fixture.decorator.getWhitelistArray(fixture.data)).thenReturn(new NativeBlockState[]{allowed}); fixture.place(); assertTrue(fixture.output.get(0, upward ? 1 : 3, 0).isAir()); - PlatformBlockState support = fixture.output.get(0, upward ? 0 : 4, 0); + NativeBlockState support = fixture.output.get(0, upward ? 0 : 4, 0); when(support.matches(allowed)).thenReturn(true); fixture.place(); assertEquals("tip", fixture.property(upward ? 3 : 1, "thickness")); when(fixture.decorator.getBlacklist()).thenReturn(new KList<>()); - when(fixture.decorator.getBlacklistArray(fixture.data)).thenReturn(new PlatformBlockState[]{allowed}); + when(fixture.decorator.getBlacklistArray(fixture.data)).thenReturn(new NativeBlockState[]{allowed}); fixture.output.set(0, upward ? 1 : 3, 0, airState()); fixture.place(); assertTrue(fixture.output.get(0, upward ? 1 : 3, 0).isAir()); @@ -524,8 +524,8 @@ public class DecoratorCoreTest { public void submergedSpikesWaterlogAndStopAtLava() { for (boolean upward : new boolean[]{true, false}) { SpikeFixture fixture = new SpikeFixture(4, upward); - PlatformBlockState water = mock(PlatformBlockState.class); - PlatformBlockState lava = mock(PlatformBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); + NativeBlockState lava = mock(NativeBlockState.class); when(water.isFluid()).thenReturn(true); when(water.isWater()).thenReturn(true); when(lava.isFluid()).thenReturn(true); @@ -548,7 +548,7 @@ public class DecoratorCoreTest { for (boolean upward : new boolean[]{true, false}) { SpikeFixture fixture = new SpikeFixture(5, upward); int oppositeY = 3; - PlatformBlockState opposite = spikeState("minecraft:sulfur_spike", Map.of( + NativeBlockState opposite = spikeState("minecraft:sulfur_spike", Map.of( "thickness", "tip", "vertical_direction", upward ? "down" : "up", "waterlogged", "false")); fixture.output.set(0, oppositeY, 0, opposite); fixture.place(); @@ -563,7 +563,7 @@ public class DecoratorCoreTest { @Test public void sulfurTipsDoNotMergeWithPointedDripstone() { SpikeFixture fixture = new SpikeFixture(4, true); - PlatformBlockState dripstone = spikeState("minecraft:pointed_dripstone", Map.of( + NativeBlockState dripstone = spikeState("minecraft:pointed_dripstone", Map.of( "thickness", "tip", "vertical_direction", "down", "waterlogged", "false")); fixture.output.set(0, 3, 0, dripstone); fixture.place(); @@ -591,7 +591,7 @@ public class DecoratorCoreTest { @Test public void extendingSpikeRepairsTheExistingTipBehindIt() { SpikeFixture fixture = new SpikeFixture(3, true); - PlatformBlockState spike = spikeState("minecraft:sulfur_spike", Map.of( + NativeBlockState spike = spikeState("minecraft:sulfur_spike", Map.of( "thickness", "tip", "vertical_direction", "up", "waterlogged", "false")); fixture.output.set(0, 0, 0, spike); fixture.place(); @@ -605,13 +605,13 @@ public class DecoratorCoreTest { @Test public void singleSpikeChecksPaletteBeforeReplacingItsSupport() { SpikeFixture fixture = new SpikeFixture(1, true); - PlatformBlockState original = fixture.output.get(0, 0, 0); - PlatformBlockState replacement = sturdyState(); + NativeBlockState original = fixture.output.get(0, 0, 0); + NativeBlockState replacement = sturdyState(); IrisBlockData forced = mock(IrisBlockData.class); when(forced.getBlockData(fixture.data)).thenReturn(replacement); when(fixture.decorator.getForceBlock()).thenReturn(forced); when(fixture.decorator.getWhitelist()).thenReturn(new KList<>()); - when(fixture.decorator.getWhitelistArray(fixture.data)).thenReturn(new PlatformBlockState[]{original}); + when(fixture.decorator.getWhitelistArray(fixture.data)).thenReturn(new NativeBlockState[]{original}); DecoratorCore.placeSurfaceSingle(fixture.decorator, 0, 0, 0, 0, 0, fixture.output, new RNG(1L), fixture.data, false, false, null); @@ -623,9 +623,9 @@ public class DecoratorCoreTest { @Test public void clippedCeilingVinesRetainTheirGrowingTip() { SpikeFixture fixture = new SpikeFixture(5, false); - PlatformBlockState plant = mock(PlatformBlockState.class); - PlatformBlockState tip = mock(PlatformBlockState.class); - PlatformBlockState obstruction = sturdyState(); + NativeBlockState plant = mock(NativeBlockState.class); + NativeBlockState tip = mock(NativeBlockState.class); + NativeBlockState obstruction = sturdyState(); when(plant.key()).thenReturn("minecraft:weeping_vines_plant"); when(tip.key()).thenReturn("minecraft:weeping_vines"); when(fixture.decorator.pickBlockData(any(RNG.class), eq(fixture.data), anyDouble(), anyDouble())).thenReturn(tip); @@ -644,11 +644,11 @@ public class DecoratorCoreTest { @Test public void boundDecoratorHooksWinWhenBukkitClassesArePresent() { - PlatformBlockState vine = mock(PlatformBlockState.class); - PlatformBlockState fixed = mock(PlatformBlockState.class); - PlatformBlockState decorator = mock(PlatformBlockState.class); - PlatformBlockState surface = mock(PlatformBlockState.class); - Hunk output = Hunk.newArrayHunk(1, 1, 1); + NativeBlockState vine = mock(NativeBlockState.class); + NativeBlockState fixed = mock(NativeBlockState.class); + NativeBlockState decorator = mock(NativeBlockState.class); + NativeBlockState surface = mock(NativeBlockState.class); + Hunk output = Hunk.newArrayHunk(1, 1, 1); DecoratorPlatformHooks.FaceFixer faceFixer = mock(DecoratorPlatformHooks.FaceFixer.class); DecoratorPlatformHooks.SurfaceSturdiness sturdiness = mock(DecoratorPlatformHooks.SurfaceSturdiness.class); when(vine.isVineBlock()).thenReturn(true); @@ -664,16 +664,16 @@ public class DecoratorCoreTest { } } - private PlatformBlockState airState() { - PlatformBlockState air = mock(PlatformBlockState.class); + private NativeBlockState airState() { + NativeBlockState air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); return air; } - private Hunk placeSpikeColumn(String material, int height, boolean upward, boolean waterlogged) { + private Hunk placeSpikeColumn(String material, int height, boolean upward, boolean waterlogged) { IrisDecorator decorator = mock(IrisDecorator.class); IrisData data = mock(IrisData.class); - PlatformBlockState spike = spikeState(material, Map.of( + NativeBlockState spike = spikeState(material, Map.of( "thickness", "tip_merge", "vertical_direction", upward ? "down" : "up", "waterlogged", Boolean.toString(waterlogged))); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(height); @@ -681,11 +681,11 @@ public class DecoratorCoreTest { when(decorator.pickBlockData(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(spike); when(decorator.pickBlockDataTop(any(RNG.class), eq(data), anyDouble(), anyDouble())).thenReturn(spike); - Hunk output = Hunk.newArrayHunk(1, height + 2, 1); + Hunk output = Hunk.newArrayHunk(1, height + 2, 1); for (int y = 1; y <= height; y++) { output.set(0, y, 0, airState()); } - PlatformBlockState support = sturdyState(); + NativeBlockState support = sturdyState(); output.set(0, upward ? 0 : height + 1, 0, support); DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); if (upward) { @@ -697,7 +697,7 @@ public class DecoratorCoreTest { return output; } - private void assertSpikeColumn(Hunk output, String material, + private void assertSpikeColumn(Hunk output, String material, boolean upward, boolean waterlogged, String... thicknesses) { for (int index = 0; index < thicknesses.length; index++) { int y = upward ? index + 1 : thicknesses.length - index; @@ -708,12 +708,12 @@ public class DecoratorCoreTest { } } - private PlatformBlockState spikeState(String material, Map properties) { - PlatformBlockState state = mock(PlatformBlockState.class); + private NativeBlockState spikeState(String material, Map properties) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(material + "[thickness=" + properties.get("thickness") + ",vertical_direction=" + properties.get("vertical_direction") + ",waterlogged=" + properties.get("waterlogged") + "]"); - when(state.canPlaceOnto(any(PlatformBlockState.class))).thenReturn(true); + when(state.canPlaceOnto(any(NativeBlockState.class))).thenReturn(true); when(state.withProperty(anyString(), anyString())).thenAnswer(invocation -> { Map updated = new LinkedHashMap<>(properties); updated.put(invocation.getArgument(0), invocation.getArgument(1)); @@ -722,20 +722,20 @@ public class DecoratorCoreTest { return state; } - private PlatformBlockState tallPlantState() { - return tallPlantState(mock(PlatformBlockState.class), mock(PlatformBlockState.class)); + private NativeBlockState tallPlantState() { + return tallPlantState(mock(NativeBlockState.class), mock(NativeBlockState.class)); } - private PlatformBlockState tallPlantState(PlatformBlockState lower, PlatformBlockState upper) { - PlatformBlockState plant = mock(PlatformBlockState.class); + private NativeBlockState tallPlantState(NativeBlockState lower, NativeBlockState upper) { + NativeBlockState plant = mock(NativeBlockState.class); when(plant.key()).thenReturn("minecraft:tall_grass[half=lower]"); when(plant.withProperty("half", "lower")).thenReturn(lower); when(plant.withProperty("half", "upper")).thenReturn(upper); return plant; } - private PlatformBlockState sturdyState() { - PlatformBlockState support = mock(PlatformBlockState.class); + private NativeBlockState sturdyState() { + NativeBlockState support = mock(NativeBlockState.class); BlockData blockData = mock(BlockData.class); when(support.nativeHandle()).thenReturn(blockData); when(blockData.isFaceSturdy(any(), eq(BlockSupport.FULL))).thenReturn(true); @@ -745,7 +745,7 @@ public class DecoratorCoreTest { private final IrisDecorator decorator = mock(IrisDecorator.class); private final IrisData data = mock(IrisData.class); private final DecoratorCore.PlaceOpts opts = new DecoratorCore.PlaceOpts(); - private final Hunk output; + private final Hunk output; private final int height; private final boolean upward; @@ -757,7 +757,7 @@ public class DecoratorCoreTest { output.set(0, y, 0, airState()); } output.set(0, upward ? 0 : height + 1, 0, sturdyState()); - PlatformBlockState spike = spikeState("minecraft:sulfur_spike", Map.of( + NativeBlockState spike = spikeState("minecraft:sulfur_spike", Map.of( "thickness", "tip_merge", "vertical_direction", upward ? "down" : "up", "waterlogged", "false")); when(decorator.getHeight(any(RNG.class), anyDouble(), anyDouble(), eq(data))).thenReturn(height); when(decorator.getTopThreshold()).thenReturn(0.75); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisDecoratorCaveContextTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisDecoratorCaveContextTest.java index d0e138a21..75e716150 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisDecoratorCaveContextTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisDecoratorCaveContextTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.runtime.SeedManager; import art.arcane.iris.generation.terrain.InferredType; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformRegistries; @@ -37,7 +37,7 @@ public class IrisDecoratorCaveContextTest { @BeforeClass public static void bindPlatform() { IrisPlatforms.unbind(); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); doReturn(true).when(air).isAir(); doReturn("minecraft:air").when(air).key(); PlatformRegistries registries = mock(PlatformRegistries.class); @@ -80,9 +80,9 @@ public class IrisDecoratorCaveContextTest { doReturn(new IrisDecorator[]{decorator}).when(biome).getDecoratorBucket(IrisDecorationPart.NONE); doReturn(new IrisDecorator[]{decorator}).when(biome).getDecoratorBucket(IrisDecorationPart.CEILING); - PlatformBlockState fluid = mock(PlatformBlockState.class); + NativeBlockState fluid = mock(NativeBlockState.class); doReturn(true).when(fluid).isFluid(); - Hunk output = mock(Hunk.class); + Hunk output = mock(Hunk.class); doReturn(128).when(output).getHeight(); doReturn(fluid).when(output).get(0, 10, 0); @@ -108,9 +108,9 @@ public class IrisDecoratorCaveContextTest { @Test @SuppressWarnings("unchecked") public void underwaterSurfaceDecorationRequiresActualFluidAboveTheTerrain() { - Hunk output = mock(Hunk.class); - PlatformBlockState dry = mock(PlatformBlockState.class); - PlatformBlockState fluid = mock(PlatformBlockState.class); + Hunk output = mock(Hunk.class); + NativeBlockState dry = mock(NativeBlockState.class); + NativeBlockState fluid = mock(NativeBlockState.class); doReturn(128).when(output).getHeight(); doReturn(dry).when(output).get(0, 11, 0); doReturn(false).when(dry).isFluid(); @@ -132,7 +132,7 @@ public class IrisDecoratorCaveContextTest { assertTrue(IrisSurfaceDecorator.isAquaticPlacement(state("minecraft:sea_pickle[pickles=2]"))); assertTrue(IrisSurfaceDecorator.isAquaticPlacement(state("minecraft:fire_coral_fan"))); - PlatformBlockState waterlogged = state("minecraft:oak_fence[waterlogged=true]"); + NativeBlockState waterlogged = state("minecraft:oak_fence[waterlogged=true]"); doReturn(true).when(waterlogged).isWaterLogged(); assertTrue(IrisSurfaceDecorator.isAquaticPlacement(waterlogged)); @@ -142,10 +142,10 @@ public class IrisDecoratorCaveContextTest { @Test public void compatibleWaterMustBeHorizontallyConnected() { - Hunk output = Hunk.newArrayHunk(3, 3, 1); - PlatformBlockState centerWater = waterState(); - PlatformBlockState neighborWater = waterState(); - PlatformBlockState lava = fluidState(); + Hunk output = Hunk.newArrayHunk(3, 3, 1); + NativeBlockState centerWater = waterState(); + NativeBlockState neighborWater = waterState(); + NativeBlockState lava = fluidState(); output.set(1, 1, 0, centerWater); assertFalse(IrisSurfaceDecorator.hasConnectedWater(output, 1, 1, 0)); @@ -160,10 +160,10 @@ public class IrisDecoratorCaveContextTest { @Test public void aquaticPlacementAtFluidHeightBoundaryRestoresDryBank() { IrisData data = mock(IrisData.class); - PlatformBlockState air = state("minecraft:air"); - PlatformBlockState seagrass = state("minecraft:seagrass"); + NativeBlockState air = state("minecraft:air"); + NativeBlockState seagrass = state("minecraft:seagrass"); IrisDecorator decorator = aquaticDecorator(data, seagrass, false, 1); - Hunk output = Hunk.newArrayHunk(3, 4, 1); + Hunk output = Hunk.newArrayHunk(3, 4, 1); output.set(1, 1, 0, air); IrisSurfaceDecorator.AquaticPlacementSnapshot snapshot = IrisSurfaceDecorator.captureAquaticPlacement( @@ -177,11 +177,11 @@ public class IrisDecoratorCaveContextTest { @Test public void legitimateUnderwaterAquaticPlacementIsPreserved() { IrisData data = mock(IrisData.class); - PlatformBlockState sourceWater = waterState(); - PlatformBlockState adjacentWater = waterState(); - PlatformBlockState kelp = state("minecraft:kelp_plant"); + NativeBlockState sourceWater = waterState(); + NativeBlockState adjacentWater = waterState(); + NativeBlockState kelp = state("minecraft:kelp_plant"); IrisDecorator decorator = aquaticDecorator(data, kelp, true, 1); - Hunk output = Hunk.newArrayHunk(3, 4, 1); + Hunk output = Hunk.newArrayHunk(3, 4, 1); output.set(1, 1, 0, sourceWater); output.set(0, 1, 0, adjacentWater); @@ -196,12 +196,12 @@ public class IrisDecoratorCaveContextTest { @Test public void unsupportedUpperStackRestoresTheWholeAquaticPlacement() { IrisData data = mock(IrisData.class); - PlatformBlockState lowerWater = waterState(); - PlatformBlockState upperWater = waterState(); - PlatformBlockState adjacentWater = waterState(); - PlatformBlockState kelp = state("minecraft:kelp_plant"); + NativeBlockState lowerWater = waterState(); + NativeBlockState upperWater = waterState(); + NativeBlockState adjacentWater = waterState(); + NativeBlockState kelp = state("minecraft:kelp_plant"); IrisDecorator decorator = aquaticDecorator(data, kelp, true, 2); - Hunk output = Hunk.newArrayHunk(3, 4, 1); + Hunk output = Hunk.newArrayHunk(3, 4, 1); output.set(1, 1, 0, lowerWater); output.set(1, 2, 0, upperWater); output.set(0, 1, 0, adjacentWater); @@ -219,12 +219,12 @@ public class IrisDecoratorCaveContextTest { @Test public void dryWaterloggedAndTwoBlockAquaticPlacementsAreRestoredAtomically() { IrisData data = mock(IrisData.class); - PlatformBlockState lowerAir = state("minecraft:air"); - PlatformBlockState upperAir = state("minecraft:air"); - PlatformBlockState lower = state("minecraft:tall_seagrass[half=lower]"); - PlatformBlockState upper = state("minecraft:tall_seagrass[half=upper]"); + NativeBlockState lowerAir = state("minecraft:air"); + NativeBlockState upperAir = state("minecraft:air"); + NativeBlockState lower = state("minecraft:tall_seagrass[half=lower]"); + NativeBlockState upper = state("minecraft:tall_seagrass[half=upper]"); IrisDecorator decorator = aquaticDecorator(data, lower, false, 1); - Hunk output = Hunk.newArrayHunk(3, 4, 1); + Hunk output = Hunk.newArrayHunk(3, 4, 1); output.set(1, 1, 0, lowerAir); output.set(1, 2, 0, upperAir); @@ -237,7 +237,7 @@ public class IrisDecoratorCaveContextTest { assertSame(lowerAir, output.get(1, 1, 0)); assertSame(upperAir, output.get(1, 2, 0)); - PlatformBlockState dryWaterlogged = state("minecraft:mangrove_roots[waterlogged=true]"); + NativeBlockState dryWaterlogged = state("minecraft:mangrove_roots[waterlogged=true]"); doReturn(true).when(dryWaterlogged).isWaterLogged(); IrisDecorator waterloggedDecorator = aquaticDecorator(data, dryWaterlogged, false, 1); IrisSurfaceDecorator.AquaticPlacementSnapshot waterloggedSnapshot = IrisSurfaceDecorator.captureAquaticPlacement( @@ -250,32 +250,32 @@ public class IrisDecoratorCaveContextTest { private IrisDecorator aquaticDecorator( IrisData data, - PlatformBlockState aquatic, + NativeBlockState aquatic, boolean stacking, int stackMaximum ) { IrisDecorator decorator = mock(IrisDecorator.class); - doReturn(new PlatformBlockState[]{aquatic}).when(decorator).getBlockDataArray(data); - doReturn(new PlatformBlockState[0]).when(decorator).getBlockDataTopsArray(data); + doReturn(new NativeBlockState[]{aquatic}).when(decorator).getBlockDataArray(data); + doReturn(new NativeBlockState[0]).when(decorator).getBlockDataTopsArray(data); doReturn(stacking).when(decorator).isStacking(); doReturn(stackMaximum).when(decorator).getStackMax(); return decorator; } - private PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); doReturn(key).when(state).key(); return state; } - private PlatformBlockState waterState() { - PlatformBlockState water = fluidState(); + private NativeBlockState waterState() { + NativeBlockState water = fluidState(); doReturn(true).when(water).isWater(); return water; } - private PlatformBlockState fluidState() { - PlatformBlockState fluid = state("minecraft:lava"); + private NativeBlockState fluidState() { + NativeBlockState fluid = state("minecraft:lava"); doReturn(true).when(fluid).isFluid(); return fluid; } diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisDepositTuningTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisDepositTuningTest.java index f097d5c7e..4221c7b59 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisDepositTuningTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisDepositTuningTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.pack.loading.IrisData; import art.arcane.volmlib.util.documentation.Description; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -70,15 +70,15 @@ public class IrisDepositTuningTest { IrisDepositGenerator generator = new IrisDepositGenerator(); assertTrue(generator.getSurfaceReplaceableBlocks().isEmpty()); - assertTrue(generator.canReplaceSurface(mock(PlatformBlockState.class))); + assertTrue(generator.canReplaceSurface(mock(NativeBlockState.class))); } @Test public void surfaceHostFilterAllowsOnlyConfiguredMaterials() { IrisDepositGenerator generator = new IrisDepositGenerator(); generator.setSurfaceReplaceableBlocks(new KList().qadd("minecraft:stone")); - PlatformBlockState stone = mock(PlatformBlockState.class); - PlatformBlockState dirt = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); + NativeBlockState dirt = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); when(dirt.key()).thenReturn("minecraft:dirt"); @@ -105,8 +105,8 @@ public class IrisDepositTuningTest { new KList().qadd("minecraft:stone").qadd("minecraft:sand")); IrisBiome disabled = new IrisBiome(); disabled.setSurfaceOreReplaceableBlocks(new KList<>()); - PlatformBlockState stone = mock(PlatformBlockState.class); - PlatformBlockState sand = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); + NativeBlockState sand = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); when(sand.key()).thenReturn("minecraft:sand"); @@ -139,8 +139,8 @@ public class IrisDepositTuningTest { @Test public void biomeSurfaceHostPolicySetterInvalidatesResolvedHosts() { IrisBiome biome = new IrisBiome(); - PlatformBlockState stone = mock(PlatformBlockState.class); - PlatformBlockState sand = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); + NativeBlockState sand = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); when(sand.key()).thenReturn("minecraft:sand"); @@ -273,7 +273,7 @@ public class IrisDepositTuningTest { private IrisDepositGenerator generatorWithState(IrisData data, boolean ore, String key) { IrisBlockData block = mock(IrisBlockData.class); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(block.getBlockData(data)).thenReturn(state); when(state.isOre()).thenReturn(ore); when(state.key()).thenReturn(key); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisOreGeneratorTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisOreGeneratorTest.java index 60b781b06..8fa663482 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisOreGeneratorTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisOreGeneratorTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.generation.noise.IrisGeneratorStyle; import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -52,7 +52,7 @@ public class IrisOreGeneratorTest { private static final class Fixture { private final RNG rng = new RNG(34L); - private final PlatformBlockState ore = mock(PlatformBlockState.class); + private final NativeBlockState ore = mock(NativeBlockState.class); private final CNG noise = mock(CNG.class); private final IrisGeneratorStyle style = mock(IrisGeneratorStyle.class); private final IrisOreGenerator generator = new IrisOreGenerator(); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSeaDecoratorBoundsTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSeaDecoratorBoundsTest.java index 80ef32b5e..5959fbe71 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSeaDecoratorBoundsTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSeaDecoratorBoundsTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.decoration; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.Test; @@ -65,7 +65,7 @@ public class IrisSeaDecoratorBoundsTest { public void unresolvedFloorPalettePreservesExistingWater() { Fixture fixture = new Fixture(8, false); fixture.stacking(100, 30); - PlatformBlockState water = mock(PlatformBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); fixture.output.set(0, 3, 0, water); when(fixture.decorator.getBlockData100(any(), any(), anyDouble(), anyDouble(), anyDouble(), any())).thenReturn(null); @@ -113,10 +113,10 @@ public class IrisSeaDecoratorBoundsTest { private static final class Fixture { private final IrisDecorator decorator = mock(IrisDecorator.class); private final IrisBiome biome = mock(IrisBiome.class); - private final PlatformBlockState body = mock(PlatformBlockState.class); - private final PlatformBlockState top = mock(PlatformBlockState.class); + private final NativeBlockState body = mock(NativeBlockState.class); + private final NativeBlockState top = mock(NativeBlockState.class); private final Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS); - private final Hunk output; + private final Hunk output; private final IrisEngineDecorator placement; private Fixture(int height, boolean surface) { diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisShoreLineDecoratorTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisShoreLineDecoratorTest.java index b3306e382..a8f24f7fa 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisShoreLineDecoratorTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisShoreLineDecoratorTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.runtime.SeedManager; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisSlopeClip; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformRegistries; @@ -43,7 +43,7 @@ public class IrisShoreLineDecoratorTest { @BeforeClass public static void bindPlatform() { IrisPlatforms.unbind(); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); doReturn(true).when(air).isAir(); doReturn("minecraft:air").when(air).key(); PlatformRegistries registries = mock(PlatformRegistries.class); @@ -62,9 +62,9 @@ public class IrisShoreLineDecoratorTest { @Test public void carvedSurfaceRejectsShorelineDecoration() { Fixture fixture = createFixture(false); - PlatformBlockState carvedAir = airState(); - PlatformBlockState targetAir = airState(); - Hunk output = output(carvedAir, targetAir); + NativeBlockState carvedAir = airState(); + NativeBlockState targetAir = airState(); + Hunk output = output(carvedAir, targetAir); fixture.shoreline.decorate(0, 0, 0, 1, -1, 0, 1, -1, output, fixture.biome, FLUID_HEIGHT, output.getHeight()); @@ -78,10 +78,10 @@ public class IrisShoreLineDecoratorTest { @Test public void preservedFluidRejectsShorelineDecoration() { Fixture fixture = createFixture(false); - PlatformBlockState fluid = mock(PlatformBlockState.class); - PlatformBlockState targetAir = airState(); + NativeBlockState fluid = mock(NativeBlockState.class); + NativeBlockState targetAir = airState(); when(fluid.isFluid()).thenReturn(true); - Hunk output = output(fluid, targetAir); + Hunk output = output(fluid, targetAir); fixture.shoreline.decorate(0, 0, 0, 1, -1, 0, 1, -1, output, fixture.biome, FLUID_HEIGHT, output.getHeight()); @@ -94,10 +94,10 @@ public class IrisShoreLineDecoratorTest { @Test public void sturdySurfacePlacesShorelineDecoration() { Fixture fixture = createFixture(false); - PlatformBlockState support = sturdyState(); - PlatformBlockState targetAir = airState(); + NativeBlockState support = sturdyState(); + NativeBlockState targetAir = airState(); when(fixture.decorant.canPlaceOnto(support)).thenReturn(true); - Hunk output = output(support, targetAir); + Hunk output = output(support, targetAir); fixture.shoreline.decorate(0, 0, 0, 1, -1, 0, 1, -1, output, fixture.biome, FLUID_HEIGHT, output.getHeight()); @@ -108,10 +108,10 @@ public class IrisShoreLineDecoratorTest { @Test public void acceptedShoreUsesPublishedGeometry() { Fixture fixture = createFixture(false, false); - PlatformBlockState support = sturdyState(); - PlatformBlockState targetAir = airState(); + NativeBlockState support = sturdyState(); + NativeBlockState targetAir = airState(); when(fixture.decorant.canPlaceOnto(support)).thenReturn(true); - Hunk output = output(support, targetAir); + Hunk output = output(support, targetAir); fixture.shoreline.decorate(0, 0, 0, 1, -1, 0, 1, -1, output, fixture.biome, FLUID_HEIGHT, output.getHeight()); @@ -135,8 +135,8 @@ public class IrisShoreLineDecoratorTest { @Test public void forcePlaceStillRejectsMissingSurface() { Fixture fixture = createFixture(true); - PlatformBlockState targetAir = airState(); - Hunk output = output(airState(), targetAir); + NativeBlockState targetAir = airState(); + Hunk output = output(airState(), targetAir); fixture.shoreline.decorateAcceptedShore( 0, @@ -155,13 +155,13 @@ public class IrisShoreLineDecoratorTest { @Test public void acceptedShoreCaneRequiresWaterBesideTheActualSubstrate() { Fixture fixture = createFixture(false, false); - PlatformBlockState support = sturdyState(); - PlatformBlockState air = airState(); - PlatformBlockState water = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState air = airState(); + NativeBlockState water = mock(NativeBlockState.class); when(water.isWater()).thenReturn(true); when(fixture.decorant.key()).thenReturn("minecraft:sugar_cane[age=0]"); when(fixture.decorant.canPlaceOnto(support)).thenReturn(true); - Hunk output = Hunk.newArrayHunk(3, FLUID_HEIGHT + 3, 3); + Hunk output = Hunk.newArrayHunk(3, FLUID_HEIGHT + 3, 3); output.set(1, FLUID_HEIGHT, 1, support); output.set(1, FLUID_HEIGHT + 1, 1, air); @@ -175,11 +175,11 @@ public class IrisShoreLineDecoratorTest { @Test public void forcedShoreCaneCannotUseStone() { Fixture fixture = createFixture(true, false); - PlatformBlockState stone = sturdyState(); - PlatformBlockState air = airState(); + NativeBlockState stone = sturdyState(); + NativeBlockState air = airState(); when(fixture.decorant.key()).thenReturn("minecraft:sugar_cane[age=0]"); when(fixture.decorant.canPlaceOnto(stone)).thenReturn(false); - Hunk output = output(stone, air); + Hunk output = output(stone, air); fixture.shoreline.decorateAcceptedShore(0, 0, 0, 0, output, fixture.biome, FLUID_HEIGHT, output.getHeight()); @@ -189,10 +189,10 @@ public class IrisShoreLineDecoratorTest { @Test public void unsupportedWaterloggedStackRestoresEveryOriginalBlock() { Fixture fixture = createFixture(false, false); - PlatformBlockState support = sturdyState(); - PlatformBlockState lowerOriginal = airState(); - PlatformBlockState upperOriginal = airState(); - PlatformBlockState waterloggedTop = mock(PlatformBlockState.class); + NativeBlockState support = sturdyState(); + NativeBlockState lowerOriginal = airState(); + NativeBlockState upperOriginal = airState(); + NativeBlockState waterloggedTop = mock(NativeBlockState.class); when(fixture.decorant.key()).thenReturn("minecraft:grass"); when(fixture.decorant.canPlaceOnto(support)).thenReturn(true); when(waterloggedTop.isWaterLogged()).thenReturn(true); @@ -201,14 +201,14 @@ public class IrisShoreLineDecoratorTest { when(fixture.decorator.getStackMax()).thenReturn(2); when(fixture.decorator.getHeight(any(), anyDouble(), anyDouble(), eq(fixture.data))).thenReturn(2); when(fixture.decorator.getTopThreshold()).thenReturn(1D); - when(fixture.decorator.getBlockDataArray(fixture.data)).thenReturn(new PlatformBlockState[0]); + when(fixture.decorator.getBlockDataArray(fixture.data)).thenReturn(new NativeBlockState[0]); when(fixture.decorator.getBlockDataTopsArray(fixture.data)) - .thenReturn(new PlatformBlockState[]{waterloggedTop}); + .thenReturn(new NativeBlockState[]{waterloggedTop}); when(fixture.decorator.getBlockDataForTop( eq(fixture.biome), any(), anyDouble(), anyDouble(), anyDouble(), eq(fixture.data))) .thenReturn(waterloggedTop); - Hunk output = Hunk.newArrayHunk(1, FLUID_HEIGHT + 3, 1); + Hunk output = Hunk.newArrayHunk(1, FLUID_HEIGHT + 3, 1); output.set(0, FLUID_HEIGHT, 0, support); output.set(0, FLUID_HEIGHT + 1, 0, lowerOriginal); output.set(0, FLUID_HEIGHT + 2, 0, upperOriginal); @@ -243,7 +243,7 @@ public class IrisShoreLineDecoratorTest { IrisBiome biome = mock(IrisBiome.class); IrisDecorator decorator = mock(IrisDecorator.class); IrisSlopeClip slope = mock(IrisSlopeClip.class); - PlatformBlockState decorant = mock(PlatformBlockState.class); + NativeBlockState decorant = mock(NativeBlockState.class); ProceduralStream heightStream = mock(ProceduralStream.class); EngineMantle mantle = mock(EngineMantle.class); @@ -273,21 +273,21 @@ public class IrisShoreLineDecoratorTest { return new Fixture(new IrisShoreLineDecorator(engine), data, biome, decorator, decorant); } - private Hunk output(PlatformBlockState support, PlatformBlockState target) { - Hunk output = Hunk.newArrayHunk(1, FLUID_HEIGHT + 3, 1); + private Hunk output(NativeBlockState support, NativeBlockState target) { + Hunk output = Hunk.newArrayHunk(1, FLUID_HEIGHT + 3, 1); output.set(0, FLUID_HEIGHT, 0, support); output.set(0, FLUID_HEIGHT + 1, 0, target); return output; } - private PlatformBlockState airState() { - PlatformBlockState air = mock(PlatformBlockState.class); + private NativeBlockState airState() { + NativeBlockState air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); return air; } - private PlatformBlockState sturdyState() { - PlatformBlockState support = mock(PlatformBlockState.class); + private NativeBlockState sturdyState() { + NativeBlockState support = mock(NativeBlockState.class); BlockData blockData = mock(BlockData.class); when(support.isSolid()).thenReturn(true); when(support.nativeHandle()).thenReturn(blockData); @@ -300,7 +300,7 @@ public class IrisShoreLineDecoratorTest { IrisData data, IrisBiome biome, IrisDecorator decorator, - PlatformBlockState decorant + NativeBlockState decorant ) { } } diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCanePlacementTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCanePlacementTest.java index 3cac6f569..0727e7ad8 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCanePlacementTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCanePlacementTest.java @@ -7,7 +7,7 @@ import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.hydrology.HydrologyFeatureRef; import art.arcane.iris.generation.hydrology.HydrologyFeatureType; import art.arcane.iris.generation.mantle.EngineMantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -80,8 +80,8 @@ public class IrisSugarCanePlacementTest { assertSame(fixture.cane, fixture.output.get(1, 4, 1)); } - private static PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); return state; } @@ -94,15 +94,15 @@ public class IrisSugarCanePlacementTest { } private static final class Fixture { - private final PlatformBlockState air = state("minecraft:air"); - private final PlatformBlockState stone = state("minecraft:stone"); - private final PlatformBlockState soil = state("minecraft:dirt"); - private final PlatformBlockState water = state("minecraft:water"); - private final PlatformBlockState cane = state("minecraft:sugar_cane[age=0]"); + private final NativeBlockState air = state("minecraft:air"); + private final NativeBlockState stone = state("minecraft:stone"); + private final NativeBlockState soil = state("minecraft:dirt"); + private final NativeBlockState water = state("minecraft:water"); + private final NativeBlockState cane = state("minecraft:sugar_cane[age=0]"); private final IrisDecorator decorator = mock(IrisDecorator.class); private final Engine engine = mock(Engine.class); private final EngineMantle mantle = mock(EngineMantle.class); - private final Hunk output; + private final Hunk output; private final int local; private Fixture(int size) { diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCaneTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCaneTest.java index ffad61cea..4ac0a4498 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCaneTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/IrisSugarCaneTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.hydrology.HydrologyFeatureRef; import art.arcane.iris.generation.hydrology.HydrologyFeatureType; import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.stream.interpolation.Interpolated; @@ -53,7 +53,7 @@ public class IrisSugarCaneTest { } fixture.output.set(0, 3, 1, state("minecraft:frosted_ice[age=0]")); assertTrue(fixture.place(1, 1)); - PlatformBlockState waterlogged = state("minecraft:oak_stairs[waterlogged=true]"); + NativeBlockState waterlogged = state("minecraft:oak_stairs[waterlogged=true]"); when(waterlogged.isWaterLogged()).thenReturn(true); fixture.output.set(0, 3, 1, waterlogged); assertTrue(fixture.place(1, 1)); @@ -83,7 +83,7 @@ public class IrisSugarCaneTest { when(fixture.complex.resolveHydrologyFluid("water", -17, -16)).thenReturn(fixture.water); assertTrue(fixture.place(0, 0)); - PlatformBlockState lava = state("minecraft:lava"); + NativeBlockState lava = state("minecraft:lava"); when(fixture.complex.resolveHydrologyFluid("water", -17, -16)).thenReturn(lava); assertFalse(fixture.place(0, 0)); when(fixture.complex.sampleHydrologyColumn(-17, -16)).thenReturn(new HydrologyColumnSample( @@ -104,8 +104,8 @@ public class IrisSugarCaneTest { public void forcedSurfaceDecorationStillRejectsCaneOnStone() { Fixture fixture = new Fixture(3); IrisDecorator decorator = mock(IrisDecorator.class); - PlatformBlockState stone = state("minecraft:stone"); - PlatformBlockState air = state("minecraft:air"); + NativeBlockState stone = state("minecraft:stone"); + NativeBlockState air = state("minecraft:air"); when(air.isAir()).thenReturn(true); when(decorator.isForcePlace()).thenReturn(true); when(decorator.pickBlockData(any(), any(), anyDouble(), anyDouble())).thenReturn(fixture.cane); @@ -130,7 +130,7 @@ public class IrisSugarCaneTest { when(fixture.complex.getTrueBiomeStream()).thenReturn( ProceduralStream.of((x, z) -> biome, Interpolated.of(value -> 0D, value -> biome))); when(fixture.engine.getSeedManager()).thenReturn(mock(SeedManager.class)); - PlatformBlockState ice = state("minecraft:ice"); + NativeBlockState ice = state("minecraft:ice"); when(biome.generateSeaLayers(anyDouble(), anyDouble(), any(), anyInt(), any())) .thenReturn(new KList<>(List.of(ice))); assertFalse(fixture.place(0, 0)); @@ -147,8 +147,8 @@ public class IrisSugarCaneTest { "water", "river", "mouth", "shore", "bank", "cave"); } - private static PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); return state; } @@ -156,10 +156,10 @@ public class IrisSugarCaneTest { private static final class Fixture { private final Engine engine = mock(Engine.class); private final IrisComplex complex = mock(IrisComplex.class); - private final PlatformBlockState cane = state("minecraft:sugar_cane[age=0]"); - private final PlatformBlockState soil = state("minecraft:dirt"); - private final PlatformBlockState water = state("minecraft:water"); - private final Hunk output; + private final NativeBlockState cane = state("minecraft:sugar_cane[age=0]"); + private final NativeBlockState soil = state("minecraft:dirt"); + private final NativeBlockState water = state("minecraft:water"); + private final Hunk output; private Fixture(int size) { output = Hunk.newArrayHunk(size, 6, size); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/coral/CoralGeneratorTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/coral/CoralGeneratorTest.java index 5acb826da..06cba8ad4 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/coral/CoralGeneratorTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/coral/CoralGeneratorTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.decoration.coral; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.structure.object.IrisObject; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -22,8 +22,8 @@ import static org.mockito.Mockito.when; public class CoralGeneratorTest { @Test public void fanTipsFollowTheTopOfEachOccupiedColumn() { - PlatformBlockState structure = mock(PlatformBlockState.class); - PlatformBlockState tip = mock(PlatformBlockState.class); + NativeBlockState structure = mock(NativeBlockState.class); + NativeBlockState tip = mock(NativeBlockState.class); for (int height : new int[]{2, 7, 14}) { for (int width : new int[]{1, 3, 11}) { IrisCoral coral = new IrisCoral().setForm(IrisCoralForm.FAN) @@ -61,7 +61,7 @@ public class CoralGeneratorTest { } } - private static IrisMaterialPalette palette(PlatformBlockState state) { + private static IrisMaterialPalette palette(NativeBlockState state) { IrisBlockData block = mock(IrisBlockData.class); when(block.getWeight()).thenReturn(1); when(block.getBlockData(null)).thenReturn(state); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolverTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolverTest.java index 827e0920c..edf506ec1 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolverTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/formation/FormationBlockResolverTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.decoration.formation; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -50,7 +50,7 @@ public class FormationBlockResolverTest { ? Math.floorDiv(position.getBlockY(), formation.getStrataThickness()) : position.getBlockY(); long sampleSeed = formation.getSeed() + (expectedPalette == strata ? sampleY * 31L : 0L); RNG fresh = new RNG(sampleSeed); - PlatformBlockState expected = expectedPalette.get(fresh, + NativeBlockState expected = expectedPalette.get(fresh, position.getBlockX(), sampleY, position.getBlockZ(), null); assertSame(expected, resolver.resolve(role, position)); assertEquals(new RNG(sampleSeed).nextLong(), fresh.nextLong()); @@ -68,7 +68,7 @@ public class FormationBlockResolverTest { } private static IrisBlockData block(String material) { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn("minecraft:" + material); IrisBlockData block = mock(IrisBlockData.class); when(block.getWeight()).thenReturn(1); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGeneratorTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGeneratorTest.java index dac9860ef..9e22e868f 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGeneratorTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/tree/ProceduralTreeGeneratorTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.decoration.tree; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.Test; import java.util.HashSet; @@ -273,13 +273,13 @@ public class ProceduralTreeGeneratorTest { IrisProceduralTree tree = new IrisProceduralTree(); tree.setPlausible(false); TreeBlockCanvas.Vec position = new TreeBlockCanvas.Vec(40, 20, -10); - PlatformBlockState leaf = mock(PlatformBlockState.class); - PlatformBlockState persistent = mock(PlatformBlockState.class); - PlatformBlockState supported = mock(PlatformBlockState.class); + NativeBlockState leaf = mock(NativeBlockState.class); + NativeBlockState persistent = mock(NativeBlockState.class); + NativeBlockState supported = mock(NativeBlockState.class); when(leaf.key()).thenReturn("minecraft:oak_leaves[persistent=false,distance=7]"); when(leaf.withProperty("persistent", "true")).thenReturn(persistent); when(persistent.withProperty("distance", "1")).thenReturn(supported); - Map resolved = new HashMap<>(); + Map resolved = new HashMap<>(); resolved.put(position, leaf); TreePlausibility.apply(resolved, Set.of(new TreeBlockCanvas.Vec(0, 0, 0)), Set.of(position), tree); diff --git a/core/src/test/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolverTest.java b/core/src/test/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolverTest.java index 5602adc36..6819fb448 100644 --- a/core/src/test/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolverTest.java +++ b/core/src/test/java/art/arcane/iris/generation/decoration/tree/TreeBlockResolverTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.decoration.tree; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; @@ -26,10 +26,10 @@ public class TreeBlockResolverTest { IrisProceduralTree tree = new IrisProceduralTree(); tree.setSeed(91874L); tree.setLeavesPalette(palette("oak_leaves", "birch_leaves")); - PlatformBlockState blossom = mock(PlatformBlockState.class); - PlatformBlockState light = mock(PlatformBlockState.class); - List normal = new ArrayList<>(); - List large = new ArrayList<>(); + NativeBlockState blossom = mock(NativeBlockState.class); + NativeBlockState light = mock(NativeBlockState.class); + List normal = new ArrayList<>(); + List large = new ArrayList<>(); TreeBlockCanvas.Cell cell = new TreeBlockCanvas.Cell( TreeBlockCanvas.Role.SECONDARY_LEAF, TreeBlockCanvas.Axis.NONE, false, -1, null); try (MockedStatic blocks = mockStatic(B.class)) { @@ -39,7 +39,7 @@ public class TreeBlockResolverTest { tree.setWeightedSecondaryLeaves(new KList<>( new IrisTreeSecondaryLeaf("minecraft:cherry_leaves", weight), new IrisTreeSecondaryLeaf("minecraft:shroomlight", weight))); - List resolved = weight == 1 ? normal : large; + List resolved = weight == 1 ? normal : large; for (int x = -16; x <= 16; x++) { for (int y = 0; y < 16; y++) { resolved.add(TreeBlockResolver.resolve(tree, null, cell, @@ -74,8 +74,8 @@ public class TreeBlockResolverTest { TreeBlockCanvas.Vec position = new TreeBlockCanvas.Vec(x, y, x - y); TreeBlockCanvas.Cell cell = new TreeBlockCanvas.Cell( roles[Math.floorMod(x + y, roles.length)], TreeBlockCanvas.Axis.NONE, false, -1, null); - PlatformBlockState expected = TreeBlockResolver.resolve(tree, null, cell, position, new RNG(tree.getSeed())); - PlatformBlockState actual = TreeBlockResolver.resolve(tree, null, cell, position, shared); + NativeBlockState expected = TreeBlockResolver.resolve(tree, null, cell, position, new RNG(tree.getSeed())); + NativeBlockState actual = TreeBlockResolver.resolve(tree, null, cell, position, shared); freshBlocks.add(position + ":" + expected.key()); sharedBlocks.add(position + ":" + actual.key()); materials.add(actual.key()); @@ -94,7 +94,7 @@ public class TreeBlockResolverTest { } private static IrisBlockData block(String material) { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn("minecraft:" + material); IrisBlockData block = mock(IrisBlockData.class); when(block.getWeight()).thenReturn(1); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransactionTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransactionTest.java index c60b35a4d..068ae8b02 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransactionTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/CaveObjectPlacementTransactionTest.java @@ -24,7 +24,7 @@ import art.arcane.iris.structure.object.IObjectPlacer; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -42,7 +42,7 @@ public class CaveObjectPlacementTransactionTest { @Test public void validPlacementCommitsBlocksTilesAndMetadataTogether() { IObjectPlacer delegate = createPlacer(128, 80, 20, 60); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); TileData tile = mock(TileData.class); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); @@ -74,7 +74,7 @@ public class CaveObjectPlacementTransactionTest { when(delegate.isCarved(anyInt(), anyInt(), anyInt())).thenReturn(true); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); - transaction.set(4, 30, 7, mock(PlatformBlockState.class)); + transaction.set(4, 30, 7, mock(NativeBlockState.class)); transaction.setData(5, 26, 7, "ghost"); assertEquals(CaveObjectPlacementTransaction.CommitResult.REJECTED_BOUNDS, transaction.commit()); @@ -93,7 +93,7 @@ public class CaveObjectPlacementTransactionTest { when(delegate.isCarved(eq(5), anyInt(), eq(7))).thenAnswer(invocation -> invocation.getArgument(1) <= 24); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); - transaction.set(4, 30, 7, mock(PlatformBlockState.class)); + transaction.set(4, 30, 7, mock(NativeBlockState.class)); transaction.setData(5, 25, 7, "outside-cave"); assertEquals(CaveObjectPlacementTransaction.CommitResult.REJECTED_BOUNDS, transaction.commit()); @@ -105,7 +105,7 @@ public class CaveObjectPlacementTransactionTest { public void exactBurialBoundaryIsAllowedAndShallowerAnchorsAreExcluded() { IObjectPlacer delegate = createPlacer(128, 80, 20, 90); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); - transaction.set(4, 70, 7, mock(PlatformBlockState.class)); + transaction.set(4, 70, 7, mock(NativeBlockState.class)); assertEquals(CaveObjectPlacementTransaction.CommitResult.COMMITTED, transaction.commit()); assertEquals(71, MantleObjectComponent.caveAnchorScanUpperBound(128, 80, 10)); @@ -119,7 +119,7 @@ public class CaveObjectPlacementTransactionTest { .thenReturn(HydrologyCaveCell.of(HydrologyCaveAction.DRY_AIR)); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); - transaction.set(4, 30, 7, mock(PlatformBlockState.class)); + transaction.set(4, 30, 7, mock(NativeBlockState.class)); transaction.setData(5, 30, 7, "object@1"); assertEquals(CaveObjectPlacementTransaction.CommitResult.REJECTED_HYDROLOGY, transaction.commit()); @@ -138,7 +138,7 @@ public class CaveObjectPlacementTransactionTest { when(delegate.getEngine()).thenReturn(engine); CaveObjectPlacementTransaction transaction = new CaveObjectPlacementTransaction(delegate, 20, 10); - transaction.set(4, 30, 7, mock(PlatformBlockState.class)); + transaction.set(4, 30, 7, mock(NativeBlockState.class)); transaction.setData(4, 30, 7, "object@1"); assertEquals(CaveObjectPlacementTransaction.CommitResult.REJECTED_TRANSITION, transaction.commit()); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCleanupTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCleanupTest.java index 255ba9a70..437097950 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCleanupTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCleanupTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.integration.Identifier; import art.arcane.iris.generation.decoration.tree.TreeBlockMaterial; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.iris.world.storage.matter.PreObjectMatterCell; import art.arcane.iris.world.storage.matter.TileWrapper; @@ -52,7 +52,7 @@ public class EngineMantleCleanupTest { fixture.engineMantle().cleanupChunk(7, -4); verify(fixture.chunk(), never()).deleteSlices(MatterCavern.class); - verify(fixture.chunk()).deleteSlices(PlatformBlockState.class); + verify(fixture.chunk()).deleteSlices(NativeBlockState.class); } @Test @@ -66,7 +66,7 @@ public class EngineMantleCleanupTest { verify(fixture.chunk(), never()).deleteSlices(TreeBlockMaterial.class); verify(fixture.chunk(), never()).deleteSlices(String.class); verify(fixture.chunk()).deleteSlices(MatterCavern.class); - verify(fixture.chunk()).deleteSlices(PlatformBlockState.class); + verify(fixture.chunk()).deleteSlices(NativeBlockState.class); } @Test @@ -85,12 +85,12 @@ public class EngineMantleCleanupTest { @Test public void blockStateSliceIsNeverRetainable() { - MantleSliceRetention.retain(PlatformBlockState.class.getCanonicalName()); + MantleSliceRetention.retain(NativeBlockState.class.getCanonicalName()); CleanupFixture fixture = cleanupFixture(); fixture.engineMantle().forceCleanupChunk(7, -4); - verify(fixture.chunk()).deleteSlices(PlatformBlockState.class); + verify(fixture.chunk()).deleteSlices(NativeBlockState.class); } @Test @@ -180,7 +180,7 @@ public class EngineMantleCleanupTest { } private void verifyCleanup(MantleChunk chunk) { - verify(chunk).deleteSlices(PlatformBlockState.class); + verify(chunk).deleteSlices(NativeBlockState.class); verify(chunk).deleteSlices(MatterCavern.class); verify(chunk).deleteSlices(PreObjectMatterCell.class); verify(chunk, never()).deleteSlices(TileWrapper.class); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCoverageTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCoverageTest.java index 781fbb0ba..aa6bd234b 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCoverageTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/EngineMantleCoverageTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.generation.runtime.IrisEngineMantle; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.mantle.flag.MantleFlag; @@ -42,12 +42,12 @@ public class EngineMantleCoverageTest { @Before public void bindPlatform() { IrisPlatforms.unbind(); - Map states = new HashMap<>(); + Map states = new HashMap<>(); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenAnswer(invocation -> states.computeIfAbsent( invocation.getArgument(0), key -> { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); return state; })); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransactionTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransactionTest.java index 55a3a39e3..3c51f939c 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransactionTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/FloatingObjectPlacementTransactionTest.java @@ -20,7 +20,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -38,8 +38,8 @@ public class FloatingObjectPlacementTransactionTest { public void unsupportedFinalTerrainRejectsBlocksTilesAndMarkersTogether() { IslandObjectPlacer delegate = createPlacer(false); FloatingObjectPlacementTransaction transaction = new FloatingObjectPlacementTransaction(delegate); - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); TileData tile = mock(TileData.class); transaction.set(15, 101, 8, first); @@ -64,8 +64,8 @@ public class FloatingObjectPlacementTransactionTest { public void supportedNeighborChunkCommitsBlocksTilesAndMarkers() { IslandObjectPlacer delegate = createPlacer(true); FloatingObjectPlacementTransaction transaction = new FloatingObjectPlacementTransaction(delegate); - PlatformBlockState first = mock(PlatformBlockState.class); - PlatformBlockState second = mock(PlatformBlockState.class); + NativeBlockState first = mock(NativeBlockState.class); + NativeBlockState second = mock(NativeBlockState.class); TileData tile = mock(TileData.class); transaction.set(15, 101, 8, first); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureComponentMarkerTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureComponentMarkerTest.java index 95c419ad2..ae78e8959 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureComponentMarkerTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureComponentMarkerTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.structure.placement.PlacedStructurePiece; import art.arcane.iris.structure.placement.IrisStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.nativegen.NativeStructureSuppression; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -21,8 +21,8 @@ import static org.mockito.Mockito.when; public class IrisStructureComponentMarkerTest { @Test public void markerFilterAcceptsOnlyStorageContainers() { - PlatformBlockState storage = mock(PlatformBlockState.class); - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState storage = mock(NativeBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(storage.isStorageChest()).thenReturn(true); when(solid.isStorageChest()).thenReturn(false); @@ -43,8 +43,8 @@ public class IrisStructureComponentMarkerTest { @Test public void treeCollisionUsesOnlyNonAirStructureBlocks() { - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(solid.isAir()).thenReturn(false); when(air.isAir()).thenReturn(true); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureFoundationWriteTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureFoundationWriteTest.java index 79fe9a19c..d7bff947e 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureFoundationWriteTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/IrisStructureFoundationWriteTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.terrain.IrisMaterialPalette; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.matter.MatterCavern; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -21,7 +21,7 @@ public class IrisStructureFoundationWriteTest { IrisMaterialPalette palette = mock(IrisMaterialPalette.class); RNG rng = new RNG(1337L); IrisData data = mock(IrisData.class); - PlatformBlockState deepslateBricks = mock(PlatformBlockState.class); + NativeBlockState deepslateBricks = mock(NativeBlockState.class); when(palette.get(rng, 12, -247, -18, data)).thenReturn(deepslateBricks); IrisStructureComponent.writeFoundationSupport( diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleCarvingComponentScratchTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleCarvingComponentScratchTest.java index c9e2b890b..123d3d1cc 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleCarvingComponentScratchTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleCarvingComponentScratchTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.cave.IrisCaveProfile; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.world.IrisWorld; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.mantle.runtime.Mantle; @@ -62,7 +62,7 @@ public class MantleCarvingComponentScratchTest { when(complex.getNaturalHeightStream()).thenReturn(heights); ProceduralStream regions = mock(ProceduralStream.class); ProceduralStream biomes = mock(ProceduralStream.class); - ProceduralStream fluids = mock(ProceduralStream.class); + ProceduralStream fluids = mock(ProceduralStream.class); when(regions.get(anyDouble(), anyDouble())).thenReturn(new IrisRegion()); when(biomes.get(anyDouble(), anyDouble())).thenReturn(new IrisBiome()); when(complex.getRegionStream()).thenReturn(regions); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelViewTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelViewTest.java index 12cb103c5..9c1b81112 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelViewTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyCaveVoxelViewTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.hydrology.RiverFootprint; import art.arcane.iris.generation.hydrology.cave.CavePosition; import art.arcane.iris.generation.hydrology.cave.CaveVoxel; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.mantle.flag.ReservedFlag; import art.arcane.volmlib.util.mantle.runtime.Mantle; @@ -58,15 +58,15 @@ public class MantleHydrologyCaveVoxelViewTest { MatterCavern air = new MatterCavern(true, "", (byte) 0); MatterCavern water = new MatterCavern(true, "", (byte) 1); MatterCavern lava = new MatterCavern(true, "", (byte) 2); - PlatformBlockState incompatibleFluid = mock(PlatformBlockState.class); - PlatformBlockState lavaBlock = mock(PlatformBlockState.class); - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState incompatibleFluid = mock(NativeBlockState.class); + NativeBlockState lavaBlock = mock(NativeBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); doReturn(air).when(mantle).get(0, 20, 0, MatterCavern.class); doReturn(water).when(mantle).get(1, 20, 0, MatterCavern.class); doReturn(lava).when(mantle).get(2, 20, 0, MatterCavern.class); - doReturn(incompatibleFluid).when(mantle).get(3, 20, 0, PlatformBlockState.class); - doReturn(lavaBlock).when(mantle).get(4, 20, 0, PlatformBlockState.class); - doReturn(solid).when(mantle).get(5, 20, 0, PlatformBlockState.class); + doReturn(incompatibleFluid).when(mantle).get(3, 20, 0, NativeBlockState.class); + doReturn(lavaBlock).when(mantle).get(4, 20, 0, NativeBlockState.class); + doReturn(solid).when(mantle).get(5, 20, 0, NativeBlockState.class); when(incompatibleFluid.isFluid()).thenReturn(true); when(incompatibleFluid.materialKey()).thenReturn("minecraft:water"); when(lavaBlock.isFluid()).thenReturn(true); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyComponentTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyComponentTest.java index db17f9561..88d8c385e 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyComponentTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleHydrologyComponentTest.java @@ -35,7 +35,7 @@ import art.arcane.iris.generation.hydrology.cave.HydrologyCaveRejection; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveSource; import art.arcane.iris.generation.hydrology.IrisDeepFluidConfig; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.mantle.flag.ReservedFlag; import art.arcane.volmlib.util.matter.slices.UpdateMatter; @@ -71,8 +71,8 @@ public class MantleHydrologyComponentTest { @Test public void fallingPublicationWritesLevelEightFluidAndUpdateMarker() { CavePosition position = new CavePosition(4, 20, 6); - PlatformBlockState source = mock(PlatformBlockState.class); - PlatformBlockState falling = mock(PlatformBlockState.class); + NativeBlockState source = mock(NativeBlockState.class); + NativeBlockState falling = mock(NativeBlockState.class); when(source.key()).thenReturn("minecraft:water[level=0]"); when(source.withProperty("level", "8")).thenReturn(falling); IrisComplex complex = mock(IrisComplex.class); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleObjectComponentBoundaryRadiusTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleObjectComponentBoundaryRadiusTest.java index 4e2eb395b..de7d20e53 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleObjectComponentBoundaryRadiusTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleObjectComponentBoundaryRadiusTest.java @@ -21,7 +21,7 @@ import art.arcane.iris.generation.noise.NoiseStyle; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -68,7 +68,7 @@ public class MantleObjectComponentBoundaryRadiusTest { PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenAnswer(invocation -> { String key = invocation.getArgument(0); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn(key); when(block.materialKey()).thenReturn(key); when(block.isSolid()).thenReturn(!key.toLowerCase().contains("air")); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterOverlayTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterOverlayTest.java index a0dfedfec..fd16fc029 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterOverlayTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterOverlayTest.java @@ -7,7 +7,7 @@ import art.arcane.iris.world.history.TransitionGenerationPlan; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.volmlib.util.mantle.runtime.Mantle; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; @@ -40,7 +40,7 @@ public class MantleWriterOverlayTest { private MantleWriter writer; private Matter matter; private MantleChunk chunk; - private MatterSlice blockSlice; + private MatterSlice blockSlice; private MatterSlice identifierSlice; private MatterSlice cavernSlice; private MatterSlice hydrologySlice; @@ -71,13 +71,13 @@ public class MantleWriterOverlayTest { when(chunk.getOrCreate(0)).thenReturn(matter); when(chunk.exists(0)).thenReturn(true); when(chunk.get(0)).thenReturn(matter); - when(matter.hasSlice(PlatformBlockState.class)).thenReturn(true); + when(matter.hasSlice(NativeBlockState.class)).thenReturn(true); when(matter.hasSlice(Identifier.class)).thenReturn(true); when(matter.hasSlice(MatterCavern.class)).thenReturn(true); - when(matter.getSlice(PlatformBlockState.class)).thenReturn(blockSlice); + when(matter.getSlice(NativeBlockState.class)).thenReturn(blockSlice); when(matter.getSlice(Identifier.class)).thenReturn(identifierSlice); when(matter.getSlice(MatterCavern.class)).thenReturn(cavernSlice); - doReturn(blockSlice).when(matter).slice(PlatformBlockState.class); + doReturn(blockSlice).when(matter).slice(NativeBlockState.class); doReturn(identifierSlice).when(matter).slice(Identifier.class); doReturn(cavernSlice).when(matter).slice(MatterCavern.class); doReturn(hydrologySlice).when(matter).getSlice(HydrologyCaveCell.class); @@ -111,7 +111,7 @@ public class MantleWriterOverlayTest { @Test public void normalBlockReplacementClearsDeferredPlacement() { - PlatformBlockState replacement = mock(PlatformBlockState.class); + NativeBlockState replacement = mock(NativeBlockState.class); writer.setData(X, Y, Z, replacement); @@ -121,8 +121,8 @@ public class MantleWriterOverlayTest { @Test public void customBlockWritesBaseAndIdentifierTogether() { - PlatformBlockState base = mock(PlatformBlockState.class); - PlatformBlockState custom = customState(base); + NativeBlockState base = mock(NativeBlockState.class); + NativeBlockState custom = customState(base); Identifier identifier = Identifier.fromString("iris:custom_block"); writer.set(X, Y, Z, custom); @@ -133,8 +133,8 @@ public class MantleWriterOverlayTest { @Test public void protectedHydrologyRejectsBothPartsOfCustomBlock() { - PlatformBlockState base = mock(PlatformBlockState.class); - PlatformBlockState custom = customState(base); + NativeBlockState base = mock(NativeBlockState.class); + NativeBlockState custom = customState(base); when(matter.hasSlice(HydrologyCaveCell.class)).thenReturn(true); when(hydrologySlice.get(X, Y, Z)) .thenReturn(HydrologyCaveCell.of(HydrologyCaveAction.SEAL_GUARD)); @@ -147,20 +147,20 @@ public class MantleWriterOverlayTest { @Test public void bedrockAndWorldBoundsRejectBothPartsOfCustomBlock() { - PlatformBlockState base = mock(PlatformBlockState.class); - PlatformBlockState custom = customState(base); + NativeBlockState base = mock(NativeBlockState.class); + NativeBlockState custom = customState(base); when(dimension.isBedrock()).thenReturn(true); writer.set(X, 0, Z, custom); writer.set(X, 64, Z, custom); - verify(blockSlice, never()).set(anyInt(), anyInt(), anyInt(), any(PlatformBlockState.class)); + verify(blockSlice, never()).set(anyInt(), anyInt(), anyInt(), any(NativeBlockState.class)); verify(identifierSlice, never()).set(anyInt(), anyInt(), anyInt(), any(Identifier.class)); } @Test public void protectedHydrologyRejectsLaterBlockAndCavernWrites() { - PlatformBlockState replacement = mock(PlatformBlockState.class); + NativeBlockState replacement = mock(NativeBlockState.class); MatterCavern cavern = new MatterCavern(true, "", (byte) 3); when(matter.hasSlice(HydrologyCaveCell.class)).thenReturn(true); when(hydrologySlice.get(X, Y, Z)) @@ -185,7 +185,7 @@ public class MantleWriterOverlayTest { when(complex.getTransitionGenerationPlan()).thenReturn(transition); when(transition.isHistoricalBlock(X, Z)).thenReturn(true); writer = new MantleWriter(engineMantle, mantle, 0, 0, 0, false); - PlatformBlockState replacement = mock(PlatformBlockState.class); + NativeBlockState replacement = mock(NativeBlockState.class); MatterCavern cavern = new MatterCavern(true, "", (byte) 3); writer.setData(X, Y, Z, replacement); @@ -193,7 +193,7 @@ public class MantleWriterOverlayTest { assertFalse(writer.carveDataIfAbsent(X, Y, Z, cavern)); writer.setForcedCarve(X, Y, Z, cavern); writer.clearBlock(X, Y, Z); - writer.clearData(X, Y, Z, PlatformBlockState.class); + writer.clearData(X, Y, Z, NativeBlockState.class); verify(blockSlice, never()).set(anyInt(), anyInt(), anyInt(), any()); verify(cavernSlice, never()).set(anyInt(), anyInt(), anyInt(), any()); @@ -218,8 +218,8 @@ public class MantleWriterOverlayTest { assertEquals(0, baseline.getLiquid()); } - private static PlatformBlockState customState(PlatformBlockState base) { - PlatformBlockState custom = mock(PlatformBlockState.class); + private static NativeBlockState customState(NativeBlockState base) { + NativeBlockState custom = mock(NativeBlockState.class); when(custom.isCustom()).thenReturn(true); when(custom.deferredPlacementKey()).thenReturn("iris:custom_block"); when(custom.placementBaseState()).thenReturn(base); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterPreObjectJournalTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterPreObjectJournalTest.java index acf693ae5..fea89db36 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterPreObjectJournalTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/MantleWriterPreObjectJournalTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.BukkitTestServer; import art.arcane.iris.testsupport.PlatformLeakGuard; @@ -59,7 +59,7 @@ public class MantleWriterPreObjectJournalTest { @SuppressWarnings("unchecked") public void setUp() { IrisPlatforms.unbind(); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); PlatformRegistries registries = mock(PlatformRegistries.class); IrisPlatform platform = mock(IrisPlatform.class); when(registries.air()).thenReturn(air); @@ -92,12 +92,12 @@ public class MantleWriterPreObjectJournalTest { @Test public void firstMutationCapturesEachSupportedOriginalIndependently() { - PlatformBlockState originalBlock = mock(PlatformBlockState.class); - PlatformBlockState firstBlock = mock(PlatformBlockState.class); - PlatformBlockState secondBlock = mock(PlatformBlockState.class); + NativeBlockState originalBlock = mock(NativeBlockState.class); + NativeBlockState firstBlock = mock(NativeBlockState.class); + NativeBlockState secondBlock = mock(NativeBlockState.class); MatterCavern originalCavern = new MatterCavern(true, "old", (byte) 1); MatterCavern replacementCavern = new MatterCavern(true, "new", (byte) 2); - matter.slice(PlatformBlockState.class).set(X, Y, Z, originalBlock); + matter.slice(NativeBlockState.class).set(X, Y, Z, originalBlock); matter.slice(MatterCavern.class).set(X, Y, Z, originalCavern); writer.withComponentPriority(2, () -> { @@ -108,10 +108,10 @@ public class MantleWriterPreObjectJournalTest { writer.setData(X, Y, Z, "newer-marker"); }); - assertSame(originalBlock, writer.getPrerequisiteDataIfPresent(X, Y, Z, PlatformBlockState.class)); + assertSame(originalBlock, writer.getPrerequisiteDataIfPresent(X, Y, Z, NativeBlockState.class)); assertNull(writer.getPrerequisiteDataIfPresent(X, Y, Z, String.class)); assertEquals(originalCavern, writer.getPrerequisiteDataIfPresent(X, Y, Z, MatterCavern.class)); - assertSame(secondBlock, writer.getDataIfPresent(X, Y, Z, PlatformBlockState.class)); + assertSame(secondBlock, writer.getDataIfPresent(X, Y, Z, NativeBlockState.class)); assertEquals("newer-marker", writer.getDataIfPresent(X, Y, Z, String.class)); assertEquals(replacementCavern, writer.getDataIfPresent(X, Y, Z, MatterCavern.class)); @@ -186,11 +186,11 @@ public class MantleWriterPreObjectJournalTest { @Test public void restorationWritesCapturedValuesAndNullsWithoutRecapture() { - PlatformBlockState originalBlock = mock(PlatformBlockState.class); - PlatformBlockState replacementBlock = mock(PlatformBlockState.class); + NativeBlockState originalBlock = mock(NativeBlockState.class); + NativeBlockState replacementBlock = mock(NativeBlockState.class); MatterCavern originalCavern = new MatterCavern(true, "old", (byte) 1); MatterCavern replacementCavern = new MatterCavern(true, "new", (byte) 2); - matter.slice(PlatformBlockState.class).set(X, Y, Z, originalBlock); + matter.slice(NativeBlockState.class).set(X, Y, Z, originalBlock); matter.slice(MatterCavern.class).set(X, Y, Z, originalCavern); writer.withComponentPriority(2, () -> { writer.setData(X, Y, Z, replacementBlock); @@ -201,7 +201,7 @@ public class MantleWriterPreObjectJournalTest { assertTrue(writer.restorePrerequisiteData(X, Y, Z, String.class)); assertNull(writer.getDataIfPresent(X, Y, Z, String.class)); assertTrue(writer.restorePrerequisiteCell(X, Y, Z)); - assertSame(originalBlock, writer.getDataIfPresent(X, Y, Z, PlatformBlockState.class)); + assertSame(originalBlock, writer.getDataIfPresent(X, Y, Z, NativeBlockState.class)); assertEquals(originalCavern, writer.getDataIfPresent(X, Y, Z, MatterCavern.class)); assertFalse(writer.restorePrerequisiteData(X, Y, Z, Integer.class)); assertFalse(writer.restorePrerequisiteCell(X + 1, Y, Z)); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/ObjectDestinationTransactionTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/ObjectDestinationTransactionTest.java index 3b6bface6..4c83823f2 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/ObjectDestinationTransactionTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/ObjectDestinationTransactionTest.java @@ -11,7 +11,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.Terrain3DColumnFixtures; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.BukkitTestServer; import art.arcane.iris.world.storage.matter.IrisMatterSupport; @@ -160,7 +160,7 @@ public class ObjectDestinationTransactionTest { assertFalse(transaction.isCarved(0, 6, 0)); assertTrue(transaction.isCarved(0, 12, 0)); - transaction.setData(0, 5, 0, mock(PlatformBlockState.class)); + transaction.setData(0, 5, 0, mock(NativeBlockState.class)); assertFalse(transaction.isCarved(0, 5, 0)); assertEquals(0, transaction.getCarvedColumn(0, 0, 16)[5]); @@ -232,8 +232,8 @@ public class ObjectDestinationTransactionTest { @Test public void rejectedWriterMutationsNeverEnterTheOverlay() { MantleWriter writer = writer(); - PlatformBlockState prerequisite = mock(PlatformBlockState.class); - PlatformBlockState rejected = mock(PlatformBlockState.class); + NativeBlockState prerequisite = mock(NativeBlockState.class); + NativeBlockState rejected = mock(NativeBlockState.class); MatterCavern cavern = new MatterCavern(true, "", (byte) 0); when(writer.getPrerequisiteBlock(0, 0, 0)).thenReturn(prerequisite); when(writer.getEngine().getDimension().isBedrock()).thenReturn(true); @@ -254,8 +254,8 @@ public class ObjectDestinationTransactionTest { @Test public void acceptedCustomPlacementPublishesBlockAndIdentifierAsOneMutation() { MantleWriter writer = writer(); - PlatformBlockState custom = mock(PlatformBlockState.class); - PlatformBlockState base = mock(PlatformBlockState.class); + NativeBlockState custom = mock(NativeBlockState.class); + NativeBlockState base = mock(NativeBlockState.class); Identifier identifier = Identifier.fromString("iris:custom_block"); when(custom.isCustom()).thenReturn(true); when(custom.deferredPlacementKey()).thenReturn(identifier.toString()); @@ -275,8 +275,8 @@ public class ObjectDestinationTransactionTest { @Test public void rejectedCustomPlacementStagesNeitherBlockNorIdentifier() { MantleWriter writer = writer(); - PlatformBlockState custom = mock(PlatformBlockState.class); - PlatformBlockState base = mock(PlatformBlockState.class); + NativeBlockState custom = mock(NativeBlockState.class); + NativeBlockState base = mock(NativeBlockState.class); when(custom.isCustom()).thenReturn(true); when(custom.deferredPlacementKey()).thenReturn("iris:custom_block"); when(custom.placementBaseState()).thenReturn(base); @@ -293,14 +293,14 @@ public class ObjectDestinationTransactionTest { assertNull(transaction.getDataIfPresent(1, 4, 0, Identifier.class)); assertNull(transaction.getDataIfPresent(2, 64, 0, Identifier.class)); transaction.commit(); - verify(writer, never()).set(anyInt(), anyInt(), anyInt(), any(PlatformBlockState.class)); + verify(writer, never()).set(anyInt(), anyInt(), anyInt(), any(NativeBlockState.class)); verify(writer, never()).setData(anyInt(), anyInt(), anyInt(), any(Identifier.class)); } @Test public void blockReplacementClearsOverlayIdentifierAndRestoresItAfterFailure() { MantleWriter writer = writer(); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); Marker failure = new Marker("failure"); Identifier originalIdentifier = Identifier.fromString("iris:deferred"); when(writer.getPrerequisiteDataIfPresent(0, 4, 0, Identifier.class)).thenReturn(originalIdentifier); @@ -351,7 +351,7 @@ public class ObjectDestinationTransactionTest { BukkitTestServer.install(); previousPlatform = IrisPlatforms.isBound() ? IrisPlatforms.get() : null; IrisPlatforms.unbind(); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); PlatformRegistries registries = mock(PlatformRegistries.class); IrisPlatform platform = mock(IrisPlatform.class); when(air.isAir()).thenReturn(true); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/ResolvedTerrainPlacementTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/ResolvedTerrainPlacementTest.java index 07858f38f..182664541 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/ResolvedTerrainPlacementTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/ResolvedTerrainPlacementTest.java @@ -6,7 +6,7 @@ import art.arcane.iris.world.history.BoundaryColumnGeometry; import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.world.storage.matter.IrisMatterSupport; @@ -46,8 +46,8 @@ public class ResolvedTerrainPlacementTest { private EngineMantle engineMantle; private Mantle mantle; - private PlatformBlockState stone; - private PlatformBlockState air; + private NativeBlockState stone; + private NativeBlockState air; private Matter matter; @Before @@ -57,8 +57,8 @@ public class ResolvedTerrainPlacementTest { IrisPlatforms.unbind(); IrisPlatform platform = mock(IrisPlatform.class); PlatformRegistries registries = mock(PlatformRegistries.class); - stone = mock(PlatformBlockState.class); - air = mock(PlatformBlockState.class); + stone = mock(NativeBlockState.class); + air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); when(registries.block(anyString())).thenReturn(air); when(registries.blockOrNull("minecraft:stone")).thenReturn(stone); @@ -130,7 +130,7 @@ public class ResolvedTerrainPlacementTest { @Test public void writerPrerequisitesIgnoreContentWhileLiveOccupancyIncludesBlockOnlyFills() { - matter.slice(PlatformBlockState.class).set(0, 1, 0, stone); + matter.slice(NativeBlockState.class).set(0, 1, 0, stone); matter.slice(PreObjectMatterCell.class).set(0, 1, 0, PreObjectMatterCell.block(null)); try (MantleWriter writer = new MantleWriter(engineMantle, mantle, 0, 0, 0, false)) { assertSame(air, writer.getPrerequisiteBlock(0, 1, 0)); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarveEnvelopeTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarveEnvelopeTest.java index 77debb38d..2b6cf6608 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarveEnvelopeTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarveEnvelopeTest.java @@ -1,6 +1,8 @@ package art.arcane.iris.generation.mantle; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; +import art.arcane.volmlib.util.structure.StructureCarveEnvelope; + +import art.arcane.volmlib.util.structure.StructureCarveShape; import art.arcane.volmlib.util.noise.CNG; import art.arcane.volmlib.util.noise.NoiseType; import art.arcane.volmlib.util.math.RNG; @@ -14,31 +16,31 @@ public class StructureCarveEnvelopeTest { @Test public void exactStructureFootprintAlwaysCarves() { assertTrue(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ERODED, 0D, 0D, 1D)); + StructureCarveShape.ERODED, 0D, 0D, 1D)); assertTrue(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ROUNDED, 0D, 0D, 1D)); + StructureCarveShape.ROUNDED, 0D, 0D, 1D)); } @Test public void boxModeKeepsStraightCandidateVolume() { assertTrue(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.BOX, 100D, 0D, 1D)); + StructureCarveShape.BOX, 100D, 0D, 1D)); } @Test public void roundedModeStopsAtConfiguredReach() { assertTrue(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ROUNDED, 1D, 0D, 1D)); + StructureCarveShape.ROUNDED, 1D, 0D, 1D)); assertFalse(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ROUNDED, 1.000001D, 1D, 0D)); + StructureCarveShape.ROUNDED, 1.000001D, 1D, 0D)); } @Test public void zeroErosionStrengthMatchesRoundedMode() { assertTrue(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ERODED, 1D, 0D, 0D)); + StructureCarveShape.ERODED, 1D, 0D, 0D)); assertFalse(StructureCarveEnvelope.shouldCarveOverboreCell( - IrisStructureCarveShape.ERODED, 1.000001D, 1D, 0D)); + StructureCarveShape.ERODED, 1.000001D, 1D, 0D)); } @Test @@ -69,7 +71,7 @@ public class StructureCarveEnvelopeTest { CNG roll = CNG.signature(new RNG(4242L)); for (double strength : new double[]{0.25D, 0.5D, 1D}) { - int limit = IrisStructureCarveShape.ERODED.maximumCeilingExtension(10, strength); + int limit = StructureCarveShape.ERODED.maximumCeilingExtension(10, strength); for (int x = -32; x <= 32; x++) { for (int z = -32; z <= 32; z++) { double reach = StructureCarveEnvelope.erodedUpReach(roll, 0.05D, strength, x, z, 10); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarvingFootprintTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarvingFootprintTest.java index 4fb5b5da4..2d004e858 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarvingFootprintTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/StructureCarvingFootprintTest.java @@ -1,9 +1,11 @@ package art.arcane.iris.generation.mantle; +import art.arcane.volmlib.util.structure.StructureCarvingFootprint; + import art.arcane.iris.structure.placement.PlacedStructurePiece; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KList; import org.junit.Test; @@ -24,7 +26,7 @@ public class StructureCarvingFootprintTest { block(0, 0, 0), block(1, 0, 0), block(2, 0, 0), block(0, 0, 1), block(0, 0, 2)); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 0); + StructureCarvingFootprint footprint = StructurePieceColumns.from(pieces(piece), 0); assertNotNull(footprint); assertEquals(10, footprint.minX()); @@ -43,7 +45,7 @@ public class StructureCarvingFootprintTest { block(0, 10, 0), block(0, 12, 0), block(4, 30, 0), block(4, 35, 0)); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 2); + StructureCarvingFootprint footprint = StructurePieceColumns.from(pieces(piece), 2); StructureCarvingFootprint.Column nearestRight = footprint.sample(3, 2); StructureCarvingFootprint.Column tied = footprint.sample(2, 0); @@ -64,7 +66,7 @@ public class StructureCarvingFootprintTest { PlacedStructurePiece overlapping = piece(10, 5, 28, IrisObjectRotation.of(0, 0, 0), block(0, 3, 0), block(0, 9, 0)); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from( + StructureCarvingFootprint footprint = StructurePieceColumns.from( pieces(rotated, overlapping), 0); assertNotNull(footprint); @@ -85,7 +87,7 @@ public class StructureCarvingFootprintTest { PlacedStructurePiece piece = piece( -8, 30, 11, IrisObjectRotation.of(0, 0, 0), sourceBlocks); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 3); + StructureCarvingFootprint footprint = StructurePieceColumns.from(pieces(piece), 3); assertNotNull(footprint); for (int z = footprint.minZ(); z <= footprint.maxZ(); z++) { @@ -103,23 +105,23 @@ public class StructureCarvingFootprintTest { @Test public void returnsNullForEmptyInvalidOrOversizedInputs() { - assertNull(StructureCarvingFootprint.from(null, 0)); - assertNull(StructureCarvingFootprint.from(new KList<>(), 0)); + assertNull(StructurePieceColumns.from(null, 0)); + assertNull(StructurePieceColumns.from(new KList<>(), 0)); KList invalidPieces = new KList<>(); invalidPieces.add((PlacedStructurePiece) null); invalidPieces.add(new PlacedStructurePiece( null, null, 0, 0, 0, null, 0, 0, 0, 0, 0, 0)); - assertNull(StructureCarvingFootprint.from(invalidPieces, 0)); + assertNull(StructurePieceColumns.from(invalidPieces, 0)); PlacedStructurePiece piece = piece(0, 0, 0, IrisObjectRotation.of(0, 0, 0), block(0, 0, 0)); - assertNull(StructureCarvingFootprint.from(pieces(piece), 2, 24)); + assertNull(StructurePieceColumns.from(pieces(piece), 2, 24)); PlacedStructurePiece overflowing = piece( Integer.MAX_VALUE, 0, 0, IrisObjectRotation.of(0, 0, 0), block(1, 0, 0)); - assertNull(StructureCarvingFootprint.from(pieces(overflowing), 0)); + assertNull(StructurePieceColumns.from(pieces(overflowing), 0)); try { - StructureCarvingFootprint.from(pieces(piece), -1); + StructurePieceColumns.from(pieces(piece), -1); fail("Expected negative padding to fail"); } catch (IllegalArgumentException e) { assertEquals("padding must be non-negative", e.getMessage()); @@ -130,7 +132,7 @@ public class StructureCarvingFootprintTest { public void includesTheEntirePaddingBoundary() { PlacedStructurePiece piece = piece(7, 14, -4, IrisObjectRotation.of(0, 0, 0), block(0, 0, 0)); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 2, 25); + StructureCarvingFootprint footprint = StructurePieceColumns.from(pieces(piece), 2, 25); assertNotNull(footprint); assertEquals(5, footprint.minX()); @@ -149,8 +151,8 @@ public class StructureCarvingFootprintTest { @Test public void explicitAirDoesNotExpandTheExteriorFootprint() { IrisObject object = new IrisObject(1, 1, 1); - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); object.getBlocks().put(new IrisBlockVector(0, 0, 0), solid); object.getBlocks().put(new IrisBlockVector(8, 4, 8), air); @@ -158,7 +160,7 @@ public class StructureCarvingFootprintTest { null, object, 10, 20, 30, IrisObjectRotation.of(0, 0, 0), 10, 20, 30, 18, 24, 38); - StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 0); + StructureCarvingFootprint footprint = StructurePieceColumns.from(pieces(piece), 0); assertNotNull(footprint); assertEquals(10, footprint.minX()); @@ -170,7 +172,7 @@ public class StructureCarvingFootprintTest { private static PlacedStructurePiece piece(int x, int y, int z, IrisObjectRotation rotation, int[]... blocks) { IrisObject object = new IrisObject(1, 1, 1); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); for (int[] block : blocks) { object.getBlocks().put(new IrisBlockVector(block[0], block[1], block[2]), state); } diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/StructureFoundationPlannerTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/StructureFoundationPlannerTest.java index a9922feca..9850f25da 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/StructureFoundationPlannerTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/StructureFoundationPlannerTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.mantle; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import org.junit.Test; @@ -16,13 +16,13 @@ import static org.mockito.Mockito.when; public class StructureFoundationPlannerTest { @Test public void recordsLowestPlacedOccludingCellByDefault() { - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(solid.isOccluding()).thenReturn(true); when(solid.isSolid()).thenReturn(true); - PlatformBlockState nonOccludingSolid = mock(PlatformBlockState.class); + NativeBlockState nonOccludingSolid = mock(NativeBlockState.class); when(nonOccludingSolid.isSolid()).thenReturn(true); when(nonOccludingSolid.isOccluding()).thenReturn(false); - PlatformBlockState decoration = mock(PlatformBlockState.class); + NativeBlockState decoration = mock(NativeBlockState.class); when(decoration.isOccluding()).thenReturn(false); Long2IntOpenHashMap columns = new Long2IntOpenHashMap(); @@ -44,10 +44,10 @@ public class StructureFoundationPlannerTest { @Test public void recordsSolidNonOccludingBaseWhenEnabled() { - PlatformBlockState partialStructureBlock = mock(PlatformBlockState.class); + NativeBlockState partialStructureBlock = mock(NativeBlockState.class); when(partialStructureBlock.isSolid()).thenReturn(true); when(partialStructureBlock.isOccluding()).thenReturn(false); - PlatformBlockState fullStructureBlock = mock(PlatformBlockState.class); + NativeBlockState fullStructureBlock = mock(NativeBlockState.class); when(fullStructureBlock.isSolid()).thenReturn(true); when(fullStructureBlock.isOccluding()).thenReturn(true); @@ -72,9 +72,9 @@ public class StructureFoundationPlannerTest { @Test public void compositeGroundUsesOverlayThenCarvingThenTerrain() { - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(solid.isSolid()).thenReturn(true); - PlatformBlockState fluid = mock(PlatformBlockState.class); + NativeBlockState fluid = mock(NativeBlockState.class); when(fluid.isSolid()).thenReturn(false); assertTrue(StructureFoundationPlanner.isGroundSolid(solid, true, 12, 20)); @@ -87,9 +87,9 @@ public class StructureFoundationPlannerTest { @Test public void surfaceSupportStopsAtCarvedCaveBoundary() { - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(solid.isSolid()).thenReturn(true); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(air.isSolid()).thenReturn(false); assertTrue(StructureFoundationPlanner.isSurfaceSupportBoundary(null, true, 18, 24)); diff --git a/core/src/test/java/art/arcane/iris/generation/mantle/TerrainMatterViewTest.java b/core/src/test/java/art/arcane/iris/generation/mantle/TerrainMatterViewTest.java index 9f6713f35..2a7a543c4 100644 --- a/core/src/test/java/art/arcane/iris/generation/mantle/TerrainMatterViewTest.java +++ b/core/src/test/java/art/arcane/iris/generation/mantle/TerrainMatterViewTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.mantle; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.world.storage.matter.IrisMatterSupport; import art.arcane.iris.world.storage.matter.PreObjectMatterCell; import art.arcane.iris.world.storage.matter.PreObjectMatterTest; @@ -81,8 +81,8 @@ public class TerrainMatterViewTest { assertJournalIteration(HydrologyCaveCell.class, HydrologyCaveCell.of(HydrologyCaveAction.DRY_AIR), HydrologyCaveCell.of(HydrologyCaveAction.WET_SOURCE), PreObjectMatterCell::hydrology); - assertJournalIteration(PlatformBlockState.class, - mock(PlatformBlockState.class), mock(PlatformBlockState.class), PreObjectMatterCell::block); + assertJournalIteration(NativeBlockState.class, + mock(NativeBlockState.class), mock(NativeBlockState.class), PreObjectMatterCell::block); } @Test diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrain3DCompositionTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrain3DCompositionTest.java index 8bab798f2..641670bf2 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrain3DCompositionTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrain3DCompositionTest.java @@ -12,7 +12,7 @@ import art.arcane.iris.generation.terrain.Terrain3DColumn; import art.arcane.iris.generation.terrain.Terrain3DColumnFixtures; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.ChunkedDataCache; @@ -43,7 +43,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; public class DimensionTerrain3DCompositionTest { - private final Map states = new HashMap<>(); + private final Map states = new HashMap<>(); private IrisPlatform previousPlatform; @Before @@ -70,7 +70,7 @@ public class DimensionTerrain3DCompositionTest { Terrain3DColumn terrain = Terrain3DColumnFixtures.spans(6, 0, 3, 7, 9); DimensionStackLayout layout = DimensionStackLayout.create(32, 0, List.of(input(10, 0, null), input(9, 0, terrain)), new int[]{0}); - Hunk blocks = render(layout, 32); + Hunk blocks = render(layout, 32); assertSame(state("surface"), blocks.getRaw(0, 14, 0)); assertSame(state("rock"), blocks.getRaw(0, 13, 0)); assertTrue(blocks.getRaw(0, 15, 0).isAir()); @@ -98,7 +98,7 @@ public class DimensionTerrain3DCompositionTest { Terrain3DColumn terrain = Terrain3DColumnFixtures.spans(6, 0, 2, 8, 10); DimensionStackLayout layout = DimensionStackLayout.create(32, 0, List.of(input(20, 0, null), input(10, 0, terrain)), new int[]{-11}); - Hunk blocks = render(layout, 32); + Hunk blocks = render(layout, 32); assertFalse(layout.isSolid(15)); assertTrue(blocks.getRaw(0, 15, 0).isAir()); assertTrue(layout.isHostFeatureProtectedY(15)); @@ -112,7 +112,7 @@ public class DimensionTerrain3DCompositionTest { DimensionStackLayout.LayerInput upper = materialInput(terrain, biome, 5D); DimensionStackLayout layout = DimensionStackLayout.create(64, 0, List.of(input(10, 0, null), upper), new int[]{0}); - Hunk blocks = render(layout, 64); + Hunk blocks = render(layout, 64); assertSame(state("grass"), blocks.getRaw(0, 16, 0)); assertSame(state("rock"), blocks.getRaw(0, 32, 0)); } @@ -123,7 +123,7 @@ public class DimensionTerrain3DCompositionTest { IrisBiome biome = paletteBiome(true, new IrisSlopeClip(), "a", "b", "c"); DimensionStackLayout layout = DimensionStackLayout.create(64, 0, List.of(input(10, 0, null), materialInput(terrain, biome, 0D)), new int[]{0}); - Hunk blocks = render(layout, 64); + Hunk blocks = render(layout, 64); assertSame(state("a"), blocks.getRaw(0, 16, 0)); assertSame(state("c"), blocks.getRaw(0, 15, 0)); assertSame(state("c"), blocks.getRaw(0, 32, 0)); @@ -134,12 +134,12 @@ public class DimensionTerrain3DCompositionTest { Terrain3DColumn terrain = Terrain3DColumnFixtures.spans(15, 0, 5, 10, 21); UpperDimensionContext upper = materialUpper(terrain, paletteBiome(false, new IrisSlopeClip(0D, 1D), "grass"), 5D); - Hunk slopeBlocks = renderUpper(upper); + Hunk slopeBlocks = renderUpper(upper); assertSame(state("grass"), slopeBlocks.getRaw(0, 58, 0)); assertSame(state("rock"), slopeBlocks.getRaw(0, 42, 0)); upper = materialUpper(terrain, paletteBiome(true, new IrisSlopeClip(), "a", "b", "c"), 0D); - Hunk lockedBlocks = renderUpper(upper); + Hunk lockedBlocks = renderUpper(upper); assertSame(state("a"), lockedBlocks.getRaw(0, 58, 0)); assertSame(state("c"), lockedBlocks.getRaw(0, 59, 0)); assertSame(state("c"), lockedBlocks.getRaw(0, 42, 0)); @@ -148,15 +148,15 @@ public class DimensionTerrain3DCompositionTest { @Test @SuppressWarnings("unchecked") public void actualUpperPassMirrorsGapsAndPaintsEveryInvertedFace() { - PlatformBlockState upperRock = state("upper-rock"); - PlatformBlockState upperSurface = state("upper-surface"); + NativeBlockState upperRock = state("upper-rock"); + NativeBlockState upperSurface = state("upper-surface"); Terrain3DColumn terrain = Terrain3DColumnFixtures.spans(15, 0, 5, 10, 20); UpperDimensionContext.Column upperColumn = new UpperDimensionContext.Column(64, 43, terrain); UpperDimensionContext upper = mock(UpperDimensionContext.class); when(upper.sampleColumn(0, 0)).thenReturn(upperColumn); when(upper.getRockBlock(0, 0)).thenReturn(upperRock); when(upper.getSurfaceBlock(0, 0)).thenReturn(upperSurface); - Hunk blocks = renderUpper(upper); + Hunk blocks = renderUpper(upper); assertSame(state("upper-surface"), blocks.getRaw(0, 43, 0)); assertSame(state("upper-rock"), blocks.getRaw(0, 44, 0)); @@ -262,8 +262,8 @@ public class DimensionTerrain3DCompositionTest { } @SuppressWarnings("unchecked") - private Hunk renderUpper(UpperDimensionContext upper) { - PlatformBlockState rootRock = state("root"); + private Hunk renderUpper(UpperDimensionContext upper) { + NativeBlockState rootRock = state("root"); Engine engine = mock(Engine.class); IrisDimension dimension = new IrisDimension().setBedrock(false).setHideOresForHiddenOre(true); when(engine.getDimension()).thenReturn(dimension); @@ -279,26 +279,26 @@ public class DimensionTerrain3DCompositionTest { ChunkedDataCache biomes = mock(ChunkedDataCache.class); when(biomes.get(0, 0)).thenReturn(biome); ChunkedDataCache regions = mock(ChunkedDataCache.class); - ChunkedDataCache rocks = mock(ChunkedDataCache.class); + ChunkedDataCache rocks = mock(ChunkedDataCache.class); when(rocks.get(0, 0)).thenReturn(rootRock); ChunkContext context = mock(ChunkContext.class); when(context.getBiome()).thenReturn(biomes); when(context.getRegion()).thenReturn(regions); when(context.getRock()).thenReturn(rocks); when(context.getRoundedHeight(0, 0)).thenReturn(8); - Hunk blocks = Hunk.newArrayHunk(1, 64, 1); + Hunk blocks = Hunk.newArrayHunk(1, 64, 1); new IrisTerrainNormalActuator(engine).terrainSliver(0, 0, 0, blocks, context); return blocks; } - private Hunk render(DimensionStackLayout layout, int height) { + private Hunk render(DimensionStackLayout layout, int height) { Engine engine = mock(Engine.class); when(engine.getSeedManager()).thenReturn(mock(SeedManager.class)); when(engine.getDimensionStackContext()).thenReturn(mock(DimensionStackContext.class)); ChunkContext context = mock(ChunkContext.class); when(context.isSpeculativeTerrain()).thenReturn(true); when(context.getDimensionStackLayout(0, 0)).thenReturn(layout); - Hunk output = Hunk.newArrayHunk(1, height, 1); + Hunk output = Hunk.newArrayHunk(1, height, 1); for (int y = 0; y < height; y++) { output.setRaw(0, y, 0, state("root")); } @@ -323,7 +323,7 @@ public class DimensionTerrain3DCompositionTest { } private UpperDimensionContext materialUpper(Terrain3DColumn terrain, IrisBiome biome, double capSlope) { - PlatformBlockState rock = state("rock"); + NativeBlockState rock = state("rock"); UpperDimensionContext upper = mock(UpperDimensionContext.class); when(upper.sampleColumn(0, 0)).thenReturn(new UpperDimensionContext.Column(64, 63 - terrain.topY(), terrain)); when(upper.getRockBlock(0, 0)).thenReturn(rock); @@ -337,7 +337,7 @@ public class DimensionTerrain3DCompositionTest { private IrisBiome paletteBiome(boolean locked, IrisSlopeClip slope, String... keys) { KList layers = new KList<>(); for (String key : keys) { - PlatformBlockState block = state(key); + NativeBlockState block = state(key); IrisBiomePaletteLayer layer = mock(IrisBiomePaletteLayer.class); CNG height = mock(CNG.class); when(layer.getZoom()).thenReturn(1D); @@ -352,9 +352,9 @@ public class DimensionTerrain3DCompositionTest { return new IrisBiome().setLockLayers(locked).setLayers(layers); } - private PlatformBlockState state(String key) { + private NativeBlockState state(String key) { return states.computeIfAbsent(key, value -> { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn(value); when(block.isAir()).thenReturn(value.equalsIgnoreCase("air") || value.equals("minecraft:air")); return block; diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrainContextFallbackTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrainContextFallbackTest.java index cd4ad0a03..80dbb9abd 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrainContextFallbackTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/DimensionTerrainContextFallbackTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.image.IrisImageMapRuntime; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.iris.generation.terrain.Terrain3DColumn; import art.arcane.iris.generation.terrain.Terrain3DColumnFixtures; @@ -76,13 +76,13 @@ public class DimensionTerrainContextFallbackTest { ProceduralStream naturalBiome = mock(ProceduralStream.class); ProceduralStream resolvedBiome = mock(ProceduralStream.class); ProceduralStream region = mock(ProceduralStream.class); - ProceduralStream rock = mock(ProceduralStream.class); - ProceduralStream configuredFluid = mock(ProceduralStream.class); + ProceduralStream rock = mock(ProceduralStream.class); + ProceduralStream configuredFluid = mock(ProceduralStream.class); IrisImageMapRuntime imageMapRuntime = mock(IrisImageMapRuntime.class); IrisBiome naturalBiomeValue = mock(IrisBiome.class); IrisBiome resolvedBiomeValue = mock(IrisBiome.class); - PlatformBlockState naturalFluid = mock(PlatformBlockState.class); - PlatformBlockState resolvedFluid = mock(PlatformBlockState.class); + NativeBlockState naturalFluid = mock(NativeBlockState.class); + NativeBlockState resolvedFluid = mock(NativeBlockState.class); when(engine.getDimension()).thenReturn(dimension); when(engine.getComplex()).thenReturn(complex); diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/EngineHostHeightTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/EngineHostHeightTest.java index e4d2ece49..943eadc79 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/EngineHostHeightTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/EngineHostHeightTest.java @@ -5,9 +5,9 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import org.junit.After; @@ -35,8 +35,8 @@ public class EngineHostHeightTest { @Before public void bindPlatform() { IrisPlatforms.unbind(); - PlatformBiome biome = mock(PlatformBiome.class); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBiome biome = mock(NativeBiome.class); + NativeBlockState block = mock(NativeBlockState.class); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.biome(anyString())).thenReturn(biome); when(registries.block(anyString())).thenReturn(block); diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/EngineShutdownDrainTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/EngineShutdownDrainTest.java index 40aa6e440..b2907aad4 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/EngineShutdownDrainTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/EngineShutdownDrainTest.java @@ -15,7 +15,7 @@ import art.arcane.iris.world.IrisWorld; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.DurabilityMode; import art.arcane.iris.testsupport.PlatformLeakGuard; @@ -85,7 +85,7 @@ public class EngineShutdownDrainTest { when(platform.dataFile("iris.json")).thenReturn(PLATFORM_FILES.getRoot().toPath().resolve("iris.json").toFile()); PlatformRegistries registries = mock(PlatformRegistries.class); when(platform.registries()).thenReturn(registries); - when(registries.block(anyString())).thenReturn(mock(PlatformBlockState.class)); + when(registries.block(anyString())).thenReturn(mock(NativeBlockState.class)); IrisPlatforms.bind(platform); } diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/IrisCarveWallPaletteScopeTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/IrisCarveWallPaletteScopeTest.java index 2f5848d5c..26827c91f 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/IrisCarveWallPaletteScopeTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/IrisCarveWallPaletteScopeTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.generation.biome.IrisBiomePaletteLayer; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; import art.arcane.iris.world.IrisWorld; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.IrisContext; @@ -74,13 +74,13 @@ public class IrisCarveWallPaletteScopeTest { } private static void verifyWalls(Mode mode) throws Exception { - PlatformBlockState air = block("minecraft:cave_air", false); - PlatformBlockState stone = block("minecraft:stone", true); - PlatformBlockState wall = block("minecraft:calcite", true); + NativeBlockState air = block("minecraft:cave_air", false); + NativeBlockState stone = block("minecraft:stone", true); + NativeBlockState wall = block("minecraft:calcite", true); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState(anyString())).thenReturn(air); blocks.when(() -> B.isSolid(any())).thenAnswer(call -> - call.getArgument(0).isSolid()); + call.getArgument(0).isSolid()); Fixture fixture = new Fixture(air, wall); GenerationRuntime selected = mode == Mode.DETACHED ? fixture.detached : fixture.active; IrisData expectedData = mode == Mode.ASSEMBLY ? fixture.assemblyData : selected.data(); @@ -88,7 +88,7 @@ public class IrisCarveWallPaletteScopeTest { fixture.expectedData = expectedData; doReturn(expectedComplex).when(fixture.context).getComplex(); doReturn(mode == Mode.NATURAL).when(fixture.context).isNaturalTerrain(); - Hunk output = Hunk.newArrayHunk(16, 64, 16); + Hunk output = Hunk.newArrayHunk(16, 64, 16); for (int x = 0; x < 16; x++) { for (int y = 0; y < 64; y++) { for (int z = 0; z < 16; z++) { @@ -120,7 +120,7 @@ public class IrisCarveWallPaletteScopeTest { for (int x = 0; x < 16; x++) { for (int y = 0; y < 64; y++) { for (int z = 0; z < 16; z++) { - PlatformBlockState expected = stone; + NativeBlockState expected = stone; for (int coordinate : new int[]{4, 8, 12}) { if (y == 10 && x == coordinate && z == coordinate) { expected = air; @@ -142,8 +142,8 @@ public class IrisCarveWallPaletteScopeTest { } } - private static PlatformBlockState block(String key, boolean solid) { - PlatformBlockState block = mock(PlatformBlockState.class); + private static NativeBlockState block(String key, boolean solid) { + NativeBlockState block = mock(NativeBlockState.class); doReturn(key).when(block).key(); doReturn(solid).when(block).isSolid(); return block; @@ -186,7 +186,7 @@ public class IrisCarveWallPaletteScopeTest { private IrisData expectedData; @SuppressWarnings("unchecked") - private Fixture(PlatformBlockState air, PlatformBlockState wall) throws Exception { + private Fixture(NativeBlockState air, NativeBlockState wall) throws Exception { Mantle mantle = mock(Mantle.class); doReturn(new KMap<>()).when(mantle).getLoadedRegions(); chunk = mock(MantleChunk.class); diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexBiomeBuffetTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexBiomeBuffetTest.java index 666fcf50b..1888580dc 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexBiomeBuffetTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/IrisComplexBiomeBuffetTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.studio.generation.BiomeBuffetLayout; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.block.B; import org.junit.ClassRule; @@ -66,7 +66,7 @@ public class IrisComplexBiomeBuffetTest { Path pack = fixture(); IrisData data = IrisData.openRuntime(pack.toFile()); try (MockedStatic blocks = mockStatic(B.class)) { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.isFluid()).thenReturn(true); blocks.when(() -> B.getState(anyString())).thenReturn(block); blocks.when(() -> B.getStateOrNull(anyString(), eq(false))).thenReturn(block); @@ -157,7 +157,7 @@ public class IrisComplexBiomeBuffetTest { "{\"composite\":[{\"enabled\":false,\"offsetY\":1}]}"); IrisData data = IrisData.openRuntime(pack.toFile()); try (MockedStatic blocks = mockStatic(B.class)) { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.isFluid()).thenReturn(true); blocks.when(() -> B.getStateOrNull(anyString(), eq(false))).thenReturn(block); IrisDimension dimension = data.getDimensionLoader().load("main"); diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/ResolvedTerrainProviderTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/ResolvedTerrainProviderTest.java index 9a3bd66bb..872a6ac5b 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/ResolvedTerrainProviderTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/ResolvedTerrainProviderTest.java @@ -9,11 +9,11 @@ import art.arcane.iris.world.history.SavedTerrainChunk; import art.arcane.iris.world.history.TerrainBoundarySignature; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformRegistries; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.IrisContext; @@ -65,7 +65,7 @@ public final class ResolvedTerrainProviderTest { public void cachedTransitionTerrainRetainsDeferredBlocksWhenCopied() { Fixture fixture = new Fixture(); String key = "itemsadder:rocks/ruby_ore"; - PlatformBlockState custom = Fixture.block(key, false); + NativeBlockState custom = Fixture.block(key, false); when(custom.isCustom()).thenReturn(true); when(custom.placementBaseState()).thenReturn(fixture.stone); when(custom.deferredPlacementKey()).thenReturn(key); @@ -95,7 +95,7 @@ public final class ResolvedTerrainProviderTest { when(registries.biome("minecraft:plains")).thenReturn(fixture.biome); ChunkContext context = new ChunkContext(0, 0, fixture.complex, false, ChunkContext.PrefillPlan.NONE, null); AtomicInteger customWrites = new AtomicInteger(); - Hunk blocks = Hunk.newArrayHunk(16, 16, 16).listen((x, y, z, state) -> { + Hunk blocks = Hunk.newArrayHunk(16, 16, 16).listen((x, y, z, state) -> { if (state.isCustom()) { customWrites.incrementAndGet(); } @@ -325,8 +325,8 @@ public final class ResolvedTerrainProviderTest { }; when(fixture.mode.getStages()).thenReturn(new KList<>(content)); doCallRealMethod().when(fixture.mode).generate(anyInt(), anyInt(), any(), any(), anyBoolean(), anyLong()); - Hunk blocks = Hunk.newArrayHunk(16, 16, 16); - Hunk biomes = Hunk.newArrayHunk(16, 16, 16); + Hunk blocks = Hunk.newArrayHunk(16, 16, 16); + Hunk biomes = Hunk.newArrayHunk(16, 16, 16); fixture.mode.generate(0, 0, blocks, biomes, false, 41L); @@ -406,7 +406,7 @@ public final class ResolvedTerrainProviderTest { when(plan.isHistoricalBlock(anyInt(), anyInt())).thenAnswer(invocation -> (int) invocation.getArgument(0) < 16); when(plan.newEpochWeightAt(anyInt(), anyInt())).thenReturn(0.5D); when(fixture.complex.getTransitionGenerationPlan()).thenReturn(plan); - PlatformBlockState water = Fixture.block("minecraft:water[level=0]", false); + NativeBlockState water = Fixture.block("minecraft:water[level=0]", false); when(water.isFluid()).thenReturn(true); fixture.terrainOverride = (x, z, blocks, biomes, multicore, context) -> { for (int localX = 0; localX < 16; localX++) { @@ -433,9 +433,9 @@ public final class ResolvedTerrainProviderTest { private final IrisComplex complex = mock(IrisComplex.class); private final EngineMode mode = mock(EngineMode.class); private final GenerationHistoryRuntimeRouter router = mock(GenerationHistoryRuntimeRouter.class); - private final PlatformBlockState air = block("minecraft:air", true); - private final PlatformBlockState stone = block("minecraft:stone", false); - private final PlatformBiome biome = mock(PlatformBiome.class); + private final NativeBlockState air = block("minecraft:air", true); + private final NativeBlockState stone = block("minecraft:stone", false); + private final NativeBiome biome = mock(NativeBiome.class); private final Map computations = new HashMap<>(); private final List events = new ArrayList<>(); private final ResolvedTerrainProvider provider; @@ -459,7 +459,7 @@ public final class ResolvedTerrainProviderTest { when(height.getDouble(anyDouble(), anyDouble())).thenReturn(3D); ProceduralStream biomes = mock(ProceduralStream.class); ProceduralStream regions = mock(ProceduralStream.class); - ProceduralStream materials = mock(ProceduralStream.class); + ProceduralStream materials = mock(ProceduralStream.class); when(materials.get(anyDouble(), anyDouble())).thenReturn(stone); when(complex.getRawHeightStream()).thenReturn(height); when(complex.getTrueBiomeStream()).thenReturn(biomes); @@ -472,16 +472,16 @@ public final class ResolvedTerrainProviderTest { doAnswer(invocation -> { int x = invocation.getArgument(0); int z = invocation.getArgument(1); - Hunk blocks = invocation.getArgument(2); - Hunk physicalBiomes = invocation.getArgument(3); + Hunk blocks = invocation.getArgument(2); + Hunk physicalBiomes = invocation.getArgument(3); ChunkContext context = invocation.getArgument(5); fill(x, z, blocks, physicalBiomes, context); return null; }).when(mode).generateTerrain(anyInt(), anyInt(), any(), any(), anyBoolean(), any()); } - private void fill(int x, int z, Hunk blocks, - Hunk biomes, ChunkContext context) { + private void fill(int x, int z, Hunk blocks, + Hunk biomes, ChunkContext context) { assertTrue(context.isNaturalTerrain()); assertSame(context, IrisContext.require().getChunkContext()); lastSessionId = IrisContext.require().getGenerationSessionId(); @@ -507,8 +507,8 @@ public final class ResolvedTerrainProviderTest { } } - private static PlatformBlockState block(String key, boolean air) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState block(String key, boolean air) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.isAir()).thenReturn(air); return state; diff --git a/core/src/test/java/art/arcane/iris/generation/runtime/UpperDimensionImageMapRuntimeTest.java b/core/src/test/java/art/arcane/iris/generation/runtime/UpperDimensionImageMapRuntimeTest.java index 83fa9708c..5116f005e 100644 --- a/core/src/test/java/art/arcane/iris/generation/runtime/UpperDimensionImageMapRuntimeTest.java +++ b/core/src/test/java/art/arcane/iris/generation/runtime/UpperDimensionImageMapRuntimeTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.runtime; import art.arcane.iris.generation.image.IrisImageMapRuntime; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisRegion; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -33,7 +33,7 @@ public class UpperDimensionImageMapRuntimeTest { IrisImageMapRuntime runtime = mock(IrisImageMapRuntime.class); IrisRegion proceduralRegion = new IrisRegion(); IrisBiome proceduralBiome = new IrisBiome(); - PlatformBlockState proceduralBlock = mock(PlatformBlockState.class); + NativeBlockState proceduralBlock = mock(NativeBlockState.class); assertSame(proceduralRegion, UpperDimensionContext.mappedRegion( runtime, proceduralRegion, -2D, 4D)); @@ -46,8 +46,8 @@ public class UpperDimensionImageMapRuntimeTest { @Test public void mappedHeightAndSurfaceBlockAreAuthoritative() { IrisImageMapRuntime runtime = mock(IrisImageMapRuntime.class); - PlatformBlockState proceduralBlock = mock(PlatformBlockState.class); - PlatformBlockState mappedBlock = mock(PlatformBlockState.class); + NativeBlockState proceduralBlock = mock(NativeBlockState.class); + NativeBlockState mappedBlock = mock(NativeBlockState.class); when(runtime.sampleTerrainHeight(3D, 9D, 80D)).thenReturn(144D); when(runtime.sampleSurfaceBlock(3D, 9D)).thenReturn(mappedBlock); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisBiomeActuatorCoordinateTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisBiomeActuatorCoordinateTest.java index e4dfc6c1e..6027f8040 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisBiomeActuatorCoordinateTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisBiomeActuatorCoordinateTest.java @@ -9,8 +9,8 @@ import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; @@ -73,7 +73,7 @@ public class IrisBiomeActuatorCoordinateTest { when(complex.allowsNewDiscreteContentAt(anyInt(), anyInt())).thenReturn(true); Engine engine = engine(complex); IrisBiomeActuator actuator = new IrisBiomeActuator(engine); - Hunk output = mock(Hunk.class); + Hunk output = mock(Hunk.class); when(output.getWidth()).thenReturn(2); when(output.getDepth()).thenReturn(2); when(output.getHeight()).thenReturn(1); @@ -126,7 +126,7 @@ public class IrisBiomeActuatorCoordinateTest { .thenReturn(Optional.of("iris:old-physical")); Engine engine = engine(complex); IrisBiomeActuator actuator = new IrisBiomeActuator(engine); - Hunk output = mock(Hunk.class); + Hunk output = mock(Hunk.class); when(output.getWidth()).thenReturn(1); when(output.getDepth()).thenReturn(1); when(output.getHeight()).thenReturn(1); @@ -164,7 +164,7 @@ public class IrisBiomeActuatorCoordinateTest { Engine engine = engine(complex); when(engine.getMinHeight()).thenReturn(-64); IrisBiomeActuator actuator = new IrisBiomeActuator(engine); - Hunk output = mock(Hunk.class); + Hunk output = mock(Hunk.class); when(output.getWidth()).thenReturn(1); when(output.getDepth()).thenReturn(1); when(output.getHeight()).thenReturn(8); @@ -178,8 +178,8 @@ public class IrisBiomeActuatorCoordinateTest { verify(transitionPlan, times(1)).terrainSampleAt(100, -200); verify(transitionPlan, never()).newEpochWeightAt(anyInt(), anyInt()); verify(transitionPlan, never()).historicalPhysicalBiomeKeyAt(anyInt(), anyInt(), anyInt()); - verify(output).set(eq(0), eq(0), eq(0), eq(0), eq(3), eq(0), any(PlatformBiome.class)); - verify(output).set(eq(0), eq(4), eq(0), eq(0), eq(7), eq(0), any(PlatformBiome.class)); + verify(output).set(eq(0), eq(0), eq(0), eq(0), eq(3), eq(0), any(NativeBiome.class)); + verify(output).set(eq(0), eq(4), eq(0), eq(0), eq(7), eq(0), any(NativeBiome.class)); Mantle mantle = engine.getMantle().getMantle(); verify(mantle).set(eq(100), eq(0), eq(-200), argThat(value -> { MatterBiomeInject injection = (MatterBiomeInject) value; @@ -219,8 +219,8 @@ public class IrisBiomeActuatorCoordinateTest { private void bindPlatform() { IrisPlatforms.unbind(); - PlatformBiome biome = mock(PlatformBiome.class); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBiome biome = mock(NativeBiome.class); + NativeBlockState block = mock(NativeBlockState.class); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.biome(anyString())).thenReturn(biome); when(registries.block(anyString())).thenReturn(block); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierBoundarySupportTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierBoundarySupportTest.java index 38b929841..560e3f27f 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierBoundarySupportTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierBoundarySupportTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; @@ -106,11 +106,11 @@ public class IrisCarveModifierBoundarySupportTest { @Test @SuppressWarnings("unchecked") public void gravityFloorLayerRequiresSolidSupportBelowItsTarget() { - Hunk output = mock(Hunk.class); - PlatformBlockState air = state("minecraft:cave_air", false); - PlatformBlockState solid = state("minecraft:stone", true); - PlatformBlockState sand = state("minecraft:sand", true); - PlatformBlockState stone = state("minecraft:stone", true); + Hunk output = mock(Hunk.class); + NativeBlockState air = state("minecraft:cave_air", false); + NativeBlockState solid = state("minecraft:stone", true); + NativeBlockState sand = state("minecraft:sand", true); + NativeBlockState stone = state("minecraft:stone", true); doReturn(air).when(output).getRaw(0, 4, 0); assertFalse(IrisCarveModifier.canReplaceCaveFloorLayer(output, 0, 5, 0, sand)); @@ -123,10 +123,10 @@ public class IrisCarveModifierBoundarySupportTest { @Test @SuppressWarnings("unchecked") public void gravityFloorDoesNotReceiveDecoratorsOverLowerCaveAir() { - Hunk output = mock(Hunk.class); - PlatformBlockState air = state("minecraft:cave_air", false); - PlatformBlockState sand = state("minecraft:sand", true); - PlatformBlockState stone = state("minecraft:stone", true); + Hunk output = mock(Hunk.class); + NativeBlockState air = state("minecraft:cave_air", false); + NativeBlockState sand = state("minecraft:sand", true); + NativeBlockState stone = state("minecraft:stone", true); doReturn(sand).when(output).getRaw(0, 5, 0); doReturn(air).when(output).getRaw(0, 4, 0); @@ -139,9 +139,9 @@ public class IrisCarveModifierBoundarySupportTest { @Test public void hydrologyGuardOnlyAcceptsStableSolidBoundaryLayers() { HydrologyCaveCell guard = HydrologyCaveCell.of(HydrologyCaveAction.SEAL_GUARD); - PlatformBlockState stone = state("minecraft:stone", true); - PlatformBlockState sand = state("minecraft:sand", true); - PlatformBlockState water = state("minecraft:water", false, true); + NativeBlockState stone = state("minecraft:stone", true); + NativeBlockState sand = state("minecraft:sand", true); + NativeBlockState water = state("minecraft:water", false, true); assertTrue(IrisCarveModifier.canReplaceHydrologyGuard(guard, stone, false)); assertTrue(IrisCarveModifier.canReplaceHydrologyGuard(guard, stone, true)); @@ -153,10 +153,10 @@ public class IrisCarveModifierBoundarySupportTest { @Test @SuppressWarnings("unchecked") public void submergedCaveFloorUsesBuriedSubstrateInsteadOfVegetatedSurface() { - Hunk output = mock(Hunk.class); - PlatformBlockState grass = state("minecraft:grass_block", true); - PlatformBlockState dirt = state("minecraft:dirt", true); - PlatformBlockState moss = state("minecraft:moss_block", true); + Hunk output = mock(Hunk.class); + NativeBlockState grass = state("minecraft:grass_block", true); + NativeBlockState dirt = state("minecraft:dirt", true); + NativeBlockState moss = state("minecraft:moss_block", true); HydrologyCaveCell wet = HydrologyCaveCell.of(HydrologyCaveAction.WET_SOURCE); doReturn(dirt).when(output).getRaw(0, 4, 0); @@ -169,12 +169,12 @@ public class IrisCarveModifierBoundarySupportTest { output, 0, 5, 0, grass, HydrologyCaveCell.of(HydrologyCaveAction.DRY_AIR))); } - private PlatformBlockState state(String key, boolean solid) { + private NativeBlockState state(String key, boolean solid) { return state(key, solid, false); } - private PlatformBlockState state(String key, boolean solid, boolean fluid) { - PlatformBlockState state = mock(PlatformBlockState.class); + private NativeBlockState state(String key, boolean solid, boolean fluid) { + NativeBlockState state = mock(NativeBlockState.class); doReturn(key).when(state).key(); doReturn(solid).when(state).isSolid(); doReturn(fluid).when(state).isFluid(); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierFluidIntentTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierFluidIntentTest.java index 02dd4132e..1b53d29a2 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierFluidIntentTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierFluidIntentTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.generation.stage; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.matter.MatterCavern; import org.junit.Test; @@ -18,10 +18,10 @@ public class IrisCarveModifierFluidIntentTest { MatterCavern fluidIntent = new MatterCavern(true, "", (byte) 1); MatterCavern lavaIntent = new MatterCavern(true, "", (byte) 2); MatterCavern forcedAirIntent = new MatterCavern(true, "", (byte) 3); - PlatformBlockState existingFluid = mock(PlatformBlockState.class); - PlatformBlockState fluid = mock(PlatformBlockState.class); - PlatformBlockState lava = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState existingFluid = mock(NativeBlockState.class); + NativeBlockState fluid = mock(NativeBlockState.class); + NativeBlockState lava = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(existingFluid.isFluid()).thenReturn(true); assertFalse(IrisCarveModifier.hasExplicitCarveIntent(null)); @@ -46,10 +46,10 @@ public class IrisCarveModifierFluidIntentTest { @Test public void unsupportedSurfaceOreIsRemovedOnlyOverCarvedAir() { - PlatformBlockState ore = mock(PlatformBlockState.class); - PlatformBlockState ordinarySurface = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState ore = mock(NativeBlockState.class); + NativeBlockState ordinarySurface = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(ore.isOre()).thenReturn(true); when(ordinarySurface.isOre()).thenReturn(false); when(air.isSolid()).thenReturn(false); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyMaterialTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyMaterialTest.java index 3c5f1bf07..00c576810 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyMaterialTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyMaterialTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.generation.hydrology.IrisRiverMaterialConfig; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -20,8 +20,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class IrisCarveModifierHydrologyMaterialTest { - private final PlatformBlockState biomeLayer = mock(PlatformBlockState.class); - private final PlatformBlockState painted = mock(PlatformBlockState.class); + private final NativeBlockState biomeLayer = mock(NativeBlockState.class); + private final NativeBlockState painted = mock(NativeBlockState.class); private final IrisMaterialPalette palette = mock(IrisMaterialPalette.class); private final IrisData data = mock(IrisData.class); private final RNG rng = new RNG(13L); @@ -67,7 +67,7 @@ public class IrisCarveModifierHydrologyMaterialTest { return new IrisRiverMaterialConfig().setEnabled(true).setPalette(palette).setDepth(depth); } - private PlatformBlockState paint(IrisRiverMaterialConfig material, HydrologyCaveCell floorHydrology, int index) { + private NativeBlockState paint(IrisRiverMaterialConfig material, HydrologyCaveCell floorHydrology, int index) { return IrisCarveModifier.paintUndergroundBedMaterial( biomeLayer, material, floorHydrology, index, rng, 48, 31, -9, data); } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyTest.java index 60a804424..856480ad6 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisCarveModifierHydrologyTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.stage; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.matter.MatterCavern; import org.junit.Test; @@ -19,7 +19,7 @@ import static org.mockito.Mockito.when; public class IrisCarveModifierHydrologyTest { @Test public void explicitHydrologyCanMaterializeIntoAnUninitializedCell() { - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); assertFalse(IrisCarveModifier.shouldSkipEmptyCarve(null, true)); @@ -54,10 +54,10 @@ public class IrisCarveModifierHydrologyTest { @Test public void hydrologyActionsResolveDistinctFluidStates() { - PlatformBlockState current = mock(PlatformBlockState.class); - PlatformBlockState source = mock(PlatformBlockState.class); - PlatformBlockState falling = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState current = mock(NativeBlockState.class); + NativeBlockState source = mock(NativeBlockState.class); + NativeBlockState falling = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(source.key()).thenReturn("minecraft:water[level=0]"); when(source.withProperty("level", "8")).thenReturn(falling); @@ -71,10 +71,10 @@ public class IrisCarveModifierHydrologyTest { @Test public void waterloggingFollowsOnlyExplicitResultingWater() { - PlatformBlockState waterlogged = mock(PlatformBlockState.class); - PlatformBlockState dry = mock(PlatformBlockState.class); - PlatformBlockState water = mock(PlatformBlockState.class); - PlatformBlockState lava = mock(PlatformBlockState.class); + NativeBlockState waterlogged = mock(NativeBlockState.class); + NativeBlockState dry = mock(NativeBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); + NativeBlockState lava = mock(NativeBlockState.class); when(waterlogged.key()).thenReturn("minecraft:seagrass[waterlogged=true]"); when(waterlogged.withProperty("waterlogged", "false")).thenReturn(dry); when(water.isWater()).thenReturn(true); @@ -88,8 +88,8 @@ public class IrisCarveModifierHydrologyTest { @Test public void noOverlayLeavesBaselineWaterloggingUntouched() { - PlatformBlockState waterlogged = mock(PlatformBlockState.class); - PlatformBlockState water = mock(PlatformBlockState.class); + NativeBlockState waterlogged = mock(NativeBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); MatterCavern flooded = new MatterCavern(true, "", (byte) 1); assertSame(waterlogged, IrisCarveModifier.normalizeHydrologyWaterlogging( @@ -103,9 +103,9 @@ public class IrisCarveModifierHydrologyTest { @Test public void explicitOverlayUsesComposedFluidIntentForWaterlogging() { - PlatformBlockState waterlogged = mock(PlatformBlockState.class); - PlatformBlockState dry = mock(PlatformBlockState.class); - PlatformBlockState water = mock(PlatformBlockState.class); + NativeBlockState waterlogged = mock(NativeBlockState.class); + NativeBlockState dry = mock(NativeBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); MatterCavern flooded = new MatterCavern(true, "", (byte) 1); when(waterlogged.key()).thenReturn("minecraft:seagrass[waterlogged=true]"); when(waterlogged.withProperty("waterlogged", "false")).thenReturn(dry); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDecorantActuatorRiverTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDecorantActuatorRiverTest.java index eea6dfc72..fc05feffe 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDecorantActuatorRiverTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDecorantActuatorRiverTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.hydrology.HydrologyColumnLayer; import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.hydrology.HydrologyFeatureRef; import art.arcane.iris.generation.hydrology.HydrologyFeatureType; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.Test; @@ -86,9 +86,9 @@ public class IrisDecorantActuatorRiverTest { @Test public void seaDecoratorsRequireConnectedWaterBelowTheConfiguredSurface() { - Hunk output = Hunk.newArrayHunk(3, 4, 1); - PlatformBlockState water = mock(PlatformBlockState.class); - PlatformBlockState adjacentWater = mock(PlatformBlockState.class); + Hunk output = Hunk.newArrayHunk(3, 4, 1); + NativeBlockState water = mock(NativeBlockState.class); + NativeBlockState adjacentWater = mock(NativeBlockState.class); doReturn(true).when(water).isWater(); doReturn(true).when(adjacentWater).isWater(); output.set(1, 1, 0, water); @@ -102,10 +102,10 @@ public class IrisDecorantActuatorRiverTest { @Test public void seaDecoratorStacksRequireConnectedWaterAtEveryPlacedLevel() { - Hunk output = Hunk.newArrayHunk(3, 4, 1); - PlatformBlockState lowerWater = waterState(); - PlatformBlockState upperWater = waterState(); - PlatformBlockState adjacentWater = waterState(); + Hunk output = Hunk.newArrayHunk(3, 4, 1); + NativeBlockState lowerWater = waterState(); + NativeBlockState upperWater = waterState(); + NativeBlockState adjacentWater = waterState(); output.set(1, 1, 0, lowerWater); output.set(1, 2, 0, upperWater); output.set(0, 1, 0, adjacentWater); @@ -118,9 +118,9 @@ public class IrisDecorantActuatorRiverTest { @Test public void dryAquaticSeaSurfacePlacementRestoresTheOriginalBlock() { - Hunk output = Hunk.newArrayHunk(3, 3, 1); - PlatformBlockState air = state("minecraft:air"); - PlatformBlockState seagrass = state("minecraft:seagrass"); + Hunk output = Hunk.newArrayHunk(3, 3, 1); + NativeBlockState air = state("minecraft:air"); + NativeBlockState seagrass = state("minecraft:seagrass"); output.set(1, 1, 0, air); output.set(1, 1, 0, seagrass); @@ -237,14 +237,14 @@ public class IrisDecorantActuatorRiverTest { ); } - private static PlatformBlockState waterState() { - PlatformBlockState water = state("minecraft:water"); + private static NativeBlockState waterState() { + NativeBlockState water = state("minecraft:water"); doReturn(true).when(water).isWater(); return water; } - private static PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); doReturn(key).when(state).key(); return state; } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContainmentTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContainmentTest.java index a9b783bea..9afbc4f9f 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContainmentTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContainmentTest.java @@ -22,7 +22,7 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.decoration.IrisDepositGenerator; import art.arcane.iris.generation.decoration.IrisDepositHeightDistribution; import art.arcane.iris.generation.decoration.IrisDepositPlacementScope; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; import org.junit.Test; @@ -65,9 +65,9 @@ public class IrisDepositModifierContainmentTest { @Test public void airAndFluidTargetsAreRejected() { - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); - PlatformBlockState fluid = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); + NativeBlockState fluid = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); when(fluid.isFluid()).thenReturn(true); @@ -81,8 +81,8 @@ public class IrisDepositModifierContainmentTest { public void surfacePolicyReplacesBuriedHostPolicyWithoutAffectingCaves() { IrisDepositGenerator generator = mock(IrisDepositGenerator.class); IrisBiome biome = mock(IrisBiome.class); - PlatformBlockState sand = mock(PlatformBlockState.class); - PlatformBlockState basalt = mock(PlatformBlockState.class); + NativeBlockState sand = mock(NativeBlockState.class); + NativeBlockState basalt = mock(NativeBlockState.class); when(generator.hasSurfaceReplaceableBlocks(biome)).thenReturn(true); when(generator.canReplaceSurface(sand, biome)).thenReturn(true); when(generator.canReplace(sand)).thenReturn(false); @@ -175,10 +175,10 @@ public class IrisDepositModifierContainmentTest { @Test public void terrainSurfaceIncludesExteriorAirButExcludesCaveAir() { - Hunk data = Hunk.newHunk(3, 3, 3); - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState exteriorAir = mock(PlatformBlockState.class); - PlatformBlockState caveAir = mock(PlatformBlockState.class); + Hunk data = Hunk.newHunk(3, 3, 3); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState exteriorAir = mock(NativeBlockState.class); + NativeBlockState caveAir = mock(NativeBlockState.class); when(exteriorAir.isAir()).thenReturn(true); when(exteriorAir.key()).thenReturn("minecraft:air"); when(caveAir.isAir()).thenReturn(true); @@ -202,9 +202,9 @@ public class IrisDepositModifierContainmentTest { @Test public void exposureProbeChecksOnlyInBoundsOrthogonalNeighbors() { - Hunk data = Hunk.newHunk(3, 3, 3); - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState air = mock(PlatformBlockState.class); + Hunk data = Hunk.newHunk(3, 3, 3); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); when(air.isAir()).thenReturn(true); for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContextTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContextTest.java index 056b5a624..7542b6a98 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContextTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierContextTest.java @@ -6,7 +6,7 @@ import art.arcane.iris.generation.decoration.IrisDepositGenerator; import art.arcane.iris.generation.decoration.IrisDepositHeightDistribution; import art.arcane.iris.generation.decoration.IrisDepositPlacementScope; import art.arcane.iris.structure.object.IrisObject; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.iris.generation.concurrent.BurstExecutor; import art.arcane.iris.generation.concurrent.MultiBurst; @@ -222,7 +222,7 @@ public class IrisDepositModifierContextTest { private void generateAndCheckCleanup() throws Exception { ChunkContext callerContext = mock(ChunkContext.class); try (IrisContext.Scope ignored = IrisContext.open(engine, 19L, callerContext)) { - modifier.generateDeposits(Hunk.newArrayHunk(16, 16, 16), + modifier.generateDeposits(Hunk.newArrayHunk(16, 16, 16), 2, -3, true, context); assertSame(callerContext, IrisContext.require().getChunkContext()); assertEquals(19L, IrisContext.require().getGenerationSessionId()); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierOrderingTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierOrderingTest.java index 1bfbd7ee8..11ccacd26 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierOrderingTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierOrderingTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.generation.decoration.IrisDepositPlacementScope; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.block.B; @@ -59,7 +59,7 @@ public class IrisDepositModifierOrderingTest { public void bindPlatform() { IrisPlatforms.unbind(); PlatformRegistries registries = mock(PlatformRegistries.class); - PlatformBlockState stone = state("stone"); + NativeBlockState stone = state("stone"); when(registries.block(anyString())).thenReturn(stone); IrisPlatform platform = mock(IrisPlatform.class); when(platform.registries()).thenReturn(registries); @@ -111,11 +111,11 @@ public class IrisDepositModifierOrderingTest { } } - private static PlatformBlockState state(String material) { + private static NativeBlockState state(String material) { return new KeyedBlockState("minecraft:" + material); } - private static final class KeyedBlockState implements PlatformBlockState { + private static final class KeyedBlockState implements NativeBlockState { private final String key; private KeyedBlockState(String key) { @@ -163,7 +163,7 @@ public class IrisDepositModifierOrderingTest { } @Override - public PlatformBlockState placementBaseState() { + public NativeBlockState placementBaseState() { return null; } @@ -238,12 +238,12 @@ public class IrisDepositModifierOrderingTest { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return false; } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return false; } @@ -258,7 +258,7 @@ public class IrisDepositModifierOrderingTest { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { return null; } @@ -276,11 +276,11 @@ public class IrisDepositModifierOrderingTest { private final CountDownLatch regionReady = new CountDownLatch(1); private final Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS); private final ChunkContext context = mock(ChunkContext.class, RETURNS_DEEP_STUBS); - private final PlatformBlockState stone = state("stone"); - private final PlatformBlockState granite = state("granite"); - private final PlatformBlockState diorite = state("diorite"); - private final PlatformBlockState diamond = state("diamond_ore"); - private final Hunk output = Hunk.newArrayHunk(16, 16, 16); + private final NativeBlockState stone = state("stone"); + private final NativeBlockState granite = state("granite"); + private final NativeBlockState diorite = state("diorite"); + private final NativeBlockState diamond = state("diamond_ore"); + private final Hunk output = Hunk.newArrayHunk(16, 16, 16); private final MantleChunk chunk; private final boolean parallel; private final int attempts; @@ -316,7 +316,7 @@ public class IrisDepositModifierOrderingTest { } } - private IrisDepositGenerator generator(String name, PlatformBlockState requiredHost, PlatformBlockState replacement) { + private IrisDepositGenerator generator(String name, NativeBlockState requiredHost, NativeBlockState replacement) { IrisDepositGenerator generator = mock(IrisDepositGenerator.class); when(generator.getSpawnChance()).thenReturn(1D); when(generator.getPerClumpSpawnChance()).thenReturn(1D); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierParityTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierParityTest.java index fce67184a..d9a7f6fce 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierParityTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDepositModifierParityTest.java @@ -13,7 +13,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.block.B; @@ -51,7 +51,7 @@ public class IrisDepositModifierParityTest { @ClassRule public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean(); - private final Map states = new HashMap<>(); + private final Map states = new HashMap<>(); @Before public void bindPlatform() { @@ -137,7 +137,7 @@ public class IrisDepositModifierParityTest { int y = invocation.getArgument(1); return x == 4 && y % 7 == 0 ? cavern : null; }); - Hunk terrain = Hunk.newArrayHunk(16, 48, 16); + Hunk terrain = Hunk.newArrayHunk(16, 48, 16); for (int y = 0; y < 48; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { @@ -153,8 +153,8 @@ public class IrisDepositModifierParityTest { blocks.when(() -> B.getStateOrNull(anyString(), anyBoolean())) .thenAnswer(invocation -> state(invocation.getArgument(0))); blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> { - PlatformBlockState host = invocation.getArgument(0); - PlatformBlockState ore = invocation.getArgument(1); + NativeBlockState host = invocation.getArgument(0); + NativeBlockState ore = invocation.getArgument(1); return host.isDeepSlate() && ore.isOre() ? state("deepslate_" + ore.key().substring(10)) : ore; }); @@ -166,7 +166,7 @@ public class IrisDepositModifierParityTest { for (int y = 0; y < 48; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { - PlatformBlockState block = terrain.get(x, y, z); + NativeBlockState block = terrain.get(x, y, z); result[0] = (result[0] ^ block.key().hashCode()) * 0x100000001b3L; if (block.key().endsWith("granite")) { result[1]++; @@ -202,12 +202,12 @@ public class IrisDepositModifierParityTest { .setReplaceableBlocks(new KList<>(hosts)); } - private PlatformBlockState state(String block) { + private NativeBlockState state(String block) { String key = block.contains(":") ? block : "minecraft:" + block; return states.computeIfAbsent(key, KeyedBlockState::new); } - private static final class KeyedBlockState implements PlatformBlockState { + private static final class KeyedBlockState implements NativeBlockState { private final String key; private final boolean ore; private final boolean deepSlate; @@ -261,7 +261,7 @@ public class IrisDepositModifierParityTest { } @Override - public PlatformBlockState placementBaseState() { + public NativeBlockState placementBaseState() { return null; } @@ -336,12 +336,12 @@ public class IrisDepositModifierParityTest { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return false; } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return false; } @@ -356,7 +356,7 @@ public class IrisDepositModifierParityTest { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { return null; } @@ -367,14 +367,14 @@ public class IrisDepositModifierParityTest { } private static final class PaletteGenerator extends IrisDepositGenerator { - private final KList blocks; + private final KList blocks; - private PaletteGenerator(PlatformBlockState block) { + private PaletteGenerator(NativeBlockState block) { blocks = new KList<>(block); } @Override - public KList getBlockData(IrisData data) { + public KList getBlockData(IrisData data) { return blocks; } } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisDimensionStackActuatorMetadataTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisDimensionStackActuatorMetadataTest.java index 08477df1d..3766945fc 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisDimensionStackActuatorMetadataTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisDimensionStackActuatorMetadataTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.generation.stage; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.generation.mantle.TerrainMatterView; @@ -40,12 +40,12 @@ public class IrisDimensionStackActuatorMetadataTest { @Before public void bindPlatform() { IrisPlatforms.unbind(); - Map states = new HashMap<>(); + Map states = new HashMap<>(); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenAnswer(invocation -> states.computeIfAbsent( invocation.getArgument(0), key -> { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); return state; })); diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisNaturalCaveScanTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisNaturalCaveScanTest.java index 7b9d01c91..961f9e38b 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisNaturalCaveScanTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisNaturalCaveScanTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.decoration.IrisDecorationPart; import art.arcane.iris.generation.decoration.IrisDecorator; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.mantle.runtime.Mantle; @@ -69,7 +69,7 @@ public class IrisNaturalCaveScanTest { public void listenerFailureReleasesMantleChunk() throws Exception { Fixture fixture = new Fixture(); IllegalStateException failure = new IllegalStateException("listener rejected decoration"); - Hunk output = fixture.output(3, 2).listen((x, y, z, state) -> { + Hunk output = fixture.output(3, 2).listen((x, y, z, state) -> { throw failure; }); assertSame(failure, assertThrows(IllegalStateException.class, @@ -81,7 +81,7 @@ public class IrisNaturalCaveScanTest { @SuppressWarnings("unchecked") public void dimensionFailureReleasesMantleChunk() throws Exception { Fixture fixture = new Fixture(); - Hunk output = mock(Hunk.class); + Hunk output = mock(Hunk.class); doReturn(3).when(output).getWidth(); doReturn(2).when(output).getDepth(); IllegalStateException failure = new IllegalStateException("height unavailable"); @@ -93,7 +93,7 @@ public class IrisNaturalCaveScanTest { private static void verifyScan(int width, int depth) throws Exception { Fixture fixture = new Fixture(); - Hunk output = fixture.output(width, depth); + Hunk output = fixture.output(width, depth); List writes = new ArrayList<>(); fixture.modifier.decorateNaturalCaves(-32, 48, output.listen((x, y, z, state) -> writes.add(x + ":" + y + ":" + z + ":" + state.key()))); @@ -124,7 +124,7 @@ public class IrisNaturalCaveScanTest { } } for (int y = 0; y < 32; y++) { - PlatformBlockState expected = initialState(fixture, y); + NativeBlockState expected = initialState(fixture, y); if (y == 2 || y == 20) { expected = fixture.floor; } else if (y == 5 || y == 22) { @@ -146,13 +146,13 @@ public class IrisNaturalCaveScanTest { verify(fixture.chunk).release(); } - private static PlatformBlockState initialState(Fixture fixture, int y) { + private static NativeBlockState initialState(Fixture fixture, int y) { return y >= 2 && y <= 5 || y == 10 || y >= 14 && y <= 17 || y >= 28 ? fixture.air : fixture.stone; } - private static PlatformBlockState block(String key, boolean solid) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState block(String key, boolean solid) { + NativeBlockState state = mock(NativeBlockState.class); doReturn(key).when(state).key(); doReturn(key).when(state).materialKey(); doReturn(solid).when(state).isSolid(); @@ -160,10 +160,10 @@ public class IrisNaturalCaveScanTest { } private static final class Fixture { - private final PlatformBlockState air = block("minecraft:cave_air", false); - private final PlatformBlockState stone = block("minecraft:stone", true); - private final PlatformBlockState floor = block("minecraft:moss_block", true); - private final PlatformBlockState ceiling = block("minecraft:calcite", true); + private final NativeBlockState air = block("minecraft:cave_air", false); + private final NativeBlockState stone = block("minecraft:stone", true); + private final NativeBlockState floor = block("minecraft:moss_block", true); + private final NativeBlockState ceiling = block("minecraft:calcite", true); private final IrisCarveModifier modifier = mock(IrisCarveModifier.class, CALLS_REAL_METHODS); private final MantleChunk chunk; private final List calls = new ArrayList<>(); @@ -203,7 +203,7 @@ public class IrisNaturalCaveScanTest { int x = call.getArgument(0); int z = call.getArgument(1); int y = call.getArgument(11); - Hunk output = call.getArgument(8); + Hunk output = call.getArgument(8); calls.add("floor:" + x + ":" + z + ":" + y + ":" + call.getArgument(12)); output.setRaw(x, y + 1, z, floor); if (y == 1) { @@ -218,15 +218,15 @@ public class IrisNaturalCaveScanTest { int x = call.getArgument(0); int z = call.getArgument(1); int y = call.getArgument(11); - Hunk output = call.getArgument(8); + Hunk output = call.getArgument(8); calls.add("ceiling:" + x + ":" + z + ":" + y + ":" + call.getArgument(12)); output.setRaw(x, y, z, ceiling); return null; }).when(roof).decorate(anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(), eq(biome), any(), anyInt(), anyInt()); } - private Hunk output(int width, int depth) { - Hunk output = Hunk.newArrayHunk(width, 32, depth); + private Hunk output(int width, int depth) { + Hunk output = Hunk.newArrayHunk(width, 32, depth); for (int x = 0; x < width; x++) { for (int z = 0; z < depth; z++) { for (int y = 0; y < 32; y++) { diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisPerfectionModifierSpeleothemTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisPerfectionModifierSpeleothemTest.java index 4d68f8a65..6753771c7 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisPerfectionModifierSpeleothemTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisPerfectionModifierSpeleothemTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.generation.stage; import art.arcane.iris.generation.decoration.IrisSpeleothems; import art.arcane.iris.generation.decoration.IrisProceduralBlocks; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.bukkit.block.BlockSupport; import org.bukkit.block.data.BlockData; @@ -34,7 +34,7 @@ public class IrisPerfectionModifierSpeleothemTest { } int supportY = upward ? 1 : 5; - PlatformBlockState pool = fluid.equals("minecraft:water") ? fixture.water : fluid(fluid); + NativeBlockState pool = fluid.equals("minecraft:water") ? fixture.water : fluid(fluid); fixture.output.set(0, supportY, 0, pool); fixture.normalize(); @@ -101,15 +101,15 @@ public class IrisPerfectionModifierSpeleothemTest { } } - private static PlatformBlockState spike(String material, boolean upward, String thickness, boolean waterlogged) { + private static NativeBlockState spike(String material, boolean upward, String thickness, boolean waterlogged) { return spike(material, Map.of( "vertical_direction", upward ? "up" : "down", "thickness", thickness, "waterlogged", Boolean.toString(waterlogged))); } - private static PlatformBlockState spike(String material, Map properties) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState spike(String material, Map properties) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(material + "[thickness=" + properties.get("thickness") + ",vertical_direction=" + properties.get("vertical_direction") + ",waterlogged=" + properties.get("waterlogged") + "]"); @@ -122,8 +122,8 @@ public class IrisPerfectionModifierSpeleothemTest { return state; } - private static PlatformBlockState sturdyState() { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState sturdyState() { + NativeBlockState state = mock(NativeBlockState.class); BlockData nativeState = mock(BlockData.class); when(state.key()).thenReturn("minecraft:sulfur"); when(state.nativeHandle()).thenReturn(nativeState); @@ -131,8 +131,8 @@ public class IrisPerfectionModifierSpeleothemTest { return state; } - private static PlatformBlockState fluid(String material) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState fluid(String material) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(material); when(state.isFluid()).thenReturn(true); when(state.isWater()).thenReturn(material.equals("minecraft:water")); @@ -140,9 +140,9 @@ public class IrisPerfectionModifierSpeleothemTest { } private static class Fixture { - private final PlatformBlockState air = mock(PlatformBlockState.class); - private final PlatformBlockState water = fluid("minecraft:water"); - private final Hunk output = Hunk.newArrayHunk(1, 9, 1); + private final NativeBlockState air = mock(NativeBlockState.class); + private final NativeBlockState water = fluid("minecraft:water"); + private final Hunk output = Hunk.newArrayHunk(1, 9, 1); private Fixture() { when(air.key()).thenReturn("minecraft:air"); @@ -161,7 +161,7 @@ public class IrisPerfectionModifierSpeleothemTest { private void normalize() { for (int y = output.getHeight() - 1; y >= 0; y--) { - PlatformBlockState state = output.get(0, y, 0); + NativeBlockState state = output.get(0, y, 0); if (IrisSpeleothems.isSpike(state)) { IrisPerfectionModifier.normalizeSpike(state, output, 0, 0, y, air, water); } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisPostModifierWallShoreTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisPostModifierWallShoreTest.java index c128b7bc2..88555daee 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisPostModifierWallShoreTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisPostModifierWallShoreTest.java @@ -26,7 +26,7 @@ import art.arcane.iris.generation.context.ChunkedDataCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.hunk.Hunk; import art.arcane.volmlib.util.math.RNG; @@ -53,9 +53,9 @@ public class IrisPostModifierWallShoreTest { private static final int PLANE_WIDTH = 3; private static final int SURFACE = 64; private static final int HUNK_HEIGHT = 80; - private static final PlatformBlockState GROUND = state("minecraft:sand"); - private static final PlatformBlockState WALL = state("minecraft:stone"); - private static final PlatformBlockState AIR = state("minecraft:air"); + private static final NativeBlockState GROUND = state("minecraft:sand"); + private static final NativeBlockState WALL = state("minecraft:stone"); + private static final NativeBlockState AIR = state("minecraft:air"); @Test public void shoreColumnsKeepTheirSurfaceBlockBesideASeawardDrop() throws ReflectiveOperationException { @@ -78,8 +78,8 @@ public class IrisPostModifierWallShoreTest { * whether the wall patcher fires, and building it through onModify would need a live mantle. */ @SuppressWarnings("unchecked") - private PlatformBlockState surfaceAfterPost(boolean shore, boolean walls, boolean river) throws ReflectiveOperationException { - Hunk data = Hunk.newHunk(1, HUNK_HEIGHT, 1); + private NativeBlockState surfaceAfterPost(boolean shore, boolean walls, boolean river) throws ReflectiveOperationException { + Hunk data = Hunk.newHunk(1, HUNK_HEIGHT, 1); for (int y = 0; y < HUNK_HEIGHT; y++) { data.set(0, y, 0, y <= SURFACE ? GROUND : AIR); @@ -129,8 +129,8 @@ public class IrisPostModifierWallShoreTest { return data.get(0, SURFACE, 0); } - private static PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); doReturn(key).when(state).key(); doReturn(key).when(state).materialKey(); return state; diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorHydrologyTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorHydrologyTest.java index 5ed6dd797..1ebd8f411 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorHydrologyTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorHydrologyTest.java @@ -17,7 +17,7 @@ import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.generation.hydrology.IrisRiverMaterialConfig; import art.arcane.iris.generation.terrain.Terrain3DColumn; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.iris.generation.context.ChunkedDataCache; @@ -54,9 +54,9 @@ public class IrisTerrainNormalActuatorHydrologyTest { @Test public void hydrologyOwnedFluidBypassesBiomeSeaLayers() { - PlatformBlockState water = mock(PlatformBlockState.class); - PlatformBlockState ice = mock(PlatformBlockState.class); - KList seaLayers = new KList<>(); + NativeBlockState water = mock(NativeBlockState.class); + NativeBlockState ice = mock(NativeBlockState.class); + KList seaLayers = new KList<>(); seaLayers.add(ice); assertSame(water, HydrologyFluidLayerSelector.select(seaLayers, 0, water, true)); @@ -66,12 +66,12 @@ public class IrisTerrainNormalActuatorHydrologyTest { @Test public void frozenTerrainPublishesSourceWaterBeforeStandardTopLayerFreezing() { - PlatformBlockState water = mock(PlatformBlockState.class); - PlatformBlockState ice = mock(PlatformBlockState.class); + NativeBlockState water = mock(NativeBlockState.class); + NativeBlockState ice = mock(NativeBlockState.class); when(water.key()).thenReturn("minecraft:water"); when(water.isWater()).thenReturn(true); when(ice.key()).thenReturn("minecraft:ice"); - KList frozenSeaLayers = new KList<>(); + KList frozenSeaLayers = new KList<>(); frozenSeaLayers.add(ice); HydrologyFeatureRef feature = new HydrologyFeatureRef( 11L, @@ -118,7 +118,7 @@ public class IrisTerrainNormalActuatorHydrologyTest { boolean hydrologyOwned = acceptedColumn.primarySurfaceFluidLayer().isPresent(); ArrayList stages = new ArrayList<>(); - PlatformBlockState state = HydrologyFluidLayerSelector.select( + NativeBlockState state = HydrologyFluidLayerSelector.select( frozenSeaLayers, 0, water, @@ -232,9 +232,9 @@ public class IrisTerrainNormalActuatorHydrologyTest { IrisBiome biome = mock(IrisBiome.class); IrisRegion region = new IrisRegion(); ChunkContext context = mock(ChunkContext.class); - PlatformBlockState rock = mock(PlatformBlockState.class); - PlatformBlockState grass = mock(PlatformBlockState.class); - PlatformBlockState painted = mock(PlatformBlockState.class); + NativeBlockState rock = mock(NativeBlockState.class); + NativeBlockState grass = mock(NativeBlockState.class); + NativeBlockState painted = mock(NativeBlockState.class); when(rock.key()).thenReturn("minecraft:stone"); when(grass.key()).thenReturn("minecraft:grass_block"); when(painted.key()).thenReturn("minecraft:dirt"); @@ -261,7 +261,7 @@ public class IrisTerrainNormalActuatorHydrologyTest { ProceduralStream.of((x, z) -> rock, Interpolated.of(value -> 0D, value -> rock)), 11, -4, false)); when(biome.generateLayers(eq(dimension), anyDouble(), anyDouble(), any(RNG.class), anyInt(), anyInt(), eq(data), eq(complex))).thenReturn(new KList<>(List.of(grass))); - Hunk output = Hunk.newArrayHunk(1, 64, 1); + Hunk output = Hunk.newArrayHunk(1, 64, 1); new IrisTerrainNormalActuator(engine).terrainSliver(11, -4, 0, output, context); @@ -361,8 +361,8 @@ public class IrisTerrainNormalActuatorHydrologyTest { } private static final class Fixture { - private final PlatformBlockState biomeLayer = mock(PlatformBlockState.class); - private final PlatformBlockState painted = mock(PlatformBlockState.class); + private final NativeBlockState biomeLayer = mock(NativeBlockState.class); + private final NativeBlockState painted = mock(NativeBlockState.class); private final IrisMaterialPalette palette = mock(IrisMaterialPalette.class); private final IrisData data = mock(IrisData.class); private final RNG rng = new RNG(7L); @@ -379,7 +379,7 @@ public class IrisTerrainNormalActuatorHydrologyTest { return new IrisRiverMaterialConfig().setPalette(palette).setDepth(4); } - private PlatformBlockState paint(IrisRiverMaterialConfig material, int depth) { + private NativeBlockState paint(IrisRiverMaterialConfig material, int depth) { return IrisTerrainNormalActuator.paintHydrologyMaterial( biomeLayer, material, depth, rng, 11, 63, -4, data); } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorStrataCutTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorStrataCutTest.java index 5638823da..a8d51aa9e 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorStrataCutTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisTerrainNormalActuatorStrataCutTest.java @@ -16,7 +16,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.terrain.IrisRegion; import art.arcane.iris.generation.terrain.Terrain3DColumn; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.hunk.Hunk; @@ -45,7 +45,7 @@ public class IrisTerrainNormalActuatorStrataCutTest { public void exposedBankCutDeeperThanThePaletteShowsTheDeepestAuthoredLayer() { Fixture fixture = new Fixture(); - Hunk output = fixture.actuate(); + Hunk output = fixture.actuate(); assertBlock(fixture.dirt, output.get(0, 40, 0)); assertBlock(fixture.dirt, output.get(0, 39, 0)); @@ -55,7 +55,7 @@ public class IrisTerrainNormalActuatorStrataCutTest { public void exposedBankBelowTheAuthoredSoilStillFallsThroughToRock() { Fixture fixture = new Fixture(); - Hunk output = fixture.actuate(); + Hunk output = fixture.actuate(); assertBlock(fixture.rock, output.get(0, 38, 0)); } @@ -64,7 +64,7 @@ public class IrisTerrainNormalActuatorStrataCutTest { public void uncutColumnsKeepTheirExactPaletteDepths() { Fixture fixture = new Fixture(); - Hunk output = fixture.actuate(); + Hunk output = fixture.actuate(); assertBlock(fixture.grass, output.get(0, 60, 0)); assertBlock(fixture.dirt, output.get(0, 59, 0)); @@ -98,7 +98,7 @@ public class IrisTerrainNormalActuatorStrataCutTest { } } - private static void assertBlock(PlatformBlockState expected, PlatformBlockState actual) { + private static void assertBlock(NativeBlockState expected, NativeBlockState actual) { assertEquals(expected.key(), actual == null ? null : actual.key()); assertSame(expected, actual); } @@ -108,9 +108,9 @@ public class IrisTerrainNormalActuatorStrataCutTest { * erosion, over a two block soil palette. */ private static final class Fixture { - private final PlatformBlockState rock = mock(PlatformBlockState.class); - private final PlatformBlockState grass = mock(PlatformBlockState.class); - private final PlatformBlockState dirt = mock(PlatformBlockState.class); + private final NativeBlockState rock = mock(NativeBlockState.class); + private final NativeBlockState grass = mock(NativeBlockState.class); + private final NativeBlockState dirt = mock(NativeBlockState.class); private final Engine engine = mock(Engine.class); private final ChunkContext context = mock(ChunkContext.class); @@ -161,8 +161,8 @@ public class IrisTerrainNormalActuatorStrataCutTest { anyInt(), anyInt(), eq(data), eq(complex))).thenReturn(new KList<>()); } - private Hunk actuate() { - Hunk output = Hunk.newArrayHunk(1, 64, 1); + private Hunk actuate() { + Hunk output = Hunk.newArrayHunk(1, 64, 1); new IrisTerrainNormalActuator(engine).terrainSliver(11, -4, 0, output, context); return output; } diff --git a/core/src/test/java/art/arcane/iris/generation/stage/IrisTransitionGeometryActuatorTest.java b/core/src/test/java/art/arcane/iris/generation/stage/IrisTransitionGeometryActuatorTest.java index 6be5bc8a4..cf5c79e94 100644 --- a/core/src/test/java/art/arcane/iris/generation/stage/IrisTransitionGeometryActuatorTest.java +++ b/core/src/test/java/art/arcane/iris/generation/stage/IrisTransitionGeometryActuatorTest.java @@ -9,8 +9,8 @@ import art.arcane.iris.world.history.SavedTerrainChunk; import art.arcane.iris.world.history.TransitionGenerationPlan; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.context.ChunkContext; @@ -38,8 +38,8 @@ public class IrisTransitionGeometryActuatorTest { private IrisPlatform previous; private PlatformRegistries registries; - private PlatformBlockState stone; - private PlatformBlockState air; + private NativeBlockState stone; + private NativeBlockState air; @Before public void bind() { @@ -67,15 +67,15 @@ public class IrisTransitionGeometryActuatorTest { @Test public void boundaryReceiptRetainsCustomKeyAndCarrierPhysics() throws Exception { String key = "itemsadder:rocks/ruby_ore"; - PlatformBlockState carrier = state("minecraft:oak_slab[type=bottom,waterlogged=true]", false); + NativeBlockState carrier = state("minecraft:oak_slab[type=bottom,waterlogged=true]", false); when(carrier.isWaterLogged()).thenReturn(true); - PlatformBlockState custom = state(key, false); + NativeBlockState custom = state(key, false); when(custom.isCustom()).thenReturn(true); when(custom.placementBaseState()).thenReturn(carrier); - Hunk blocks = filled(); + Hunk blocks = filled(); blocks.setRaw(0, 3, 0, custom); - Hunk biomes = Hunk.newArrayHunk(16, 16, 16); - PlatformBiome biome = mock(PlatformBiome.class); + Hunk biomes = Hunk.newArrayHunk(16, 16, 16); + NativeBiome biome = mock(NativeBiome.class); when(biome.key()).thenReturn("minecraft:plains"); biomes.fill(biome); ChunkContext context = mock(ChunkContext.class); @@ -94,7 +94,7 @@ public class IrisTransitionGeometryActuatorTest { @Test public void appliesCaveAndIslandGeometryThenUpdatesDecorationHeight() { - Hunk blocks = filled(); + Hunk blocks = filled(); ChunkContext context = context("minecraft:stone"); Engine engine = mock(Engine.class); @@ -113,8 +113,8 @@ public class IrisTransitionGeometryActuatorTest { public void refusesUnresolvableRetainedMaterialInsteadOfReplacingItWithAir() { Engine engine = mock(Engine.class); ChunkContext context = context("removed:stone"); - Hunk blocks = filled(); - Hunk biomes = Hunk.newArrayHunk(16, 16, 16); + Hunk blocks = filled(); + Hunk biomes = Hunk.newArrayHunk(16, 16, 16); assertThrows(IllegalStateException.class, () -> new IrisTransitionGeometryActuator(engine) .generate(16, 0, blocks, biomes, false, context)); @@ -142,8 +142,8 @@ public class IrisTransitionGeometryActuatorTest { return context; } - private Hunk filled() { - Hunk blocks = Hunk.newArrayHunk(16, 16, 16); + private Hunk filled() { + Hunk blocks = Hunk.newArrayHunk(16, 16, 16); for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < 16; y++) { @@ -154,8 +154,8 @@ public class IrisTransitionGeometryActuatorTest { return blocks; } - private static PlatformBlockState state(String key, boolean isAir) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key, boolean isAir) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.isAir()).thenReturn(isAir); when(state.isSolid()).thenReturn(!isAir); diff --git a/core/src/test/java/art/arcane/iris/generation/validation/GoldenHashEngineTest.java b/core/src/test/java/art/arcane/iris/generation/validation/GoldenHashEngineTest.java index 28b478b6d..815bfeca9 100644 --- a/core/src/test/java/art/arcane/iris/generation/validation/GoldenHashEngineTest.java +++ b/core/src/test/java/art/arcane/iris/generation/validation/GoldenHashEngineTest.java @@ -3,8 +3,8 @@ package art.arcane.iris.generation.validation; import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -30,10 +30,10 @@ public class GoldenHashEngineTest { private IrisSettings previousSettings; private File goldenDir; private Engine engine; - private PlatformBlockState stone; - private PlatformBlockState dirt; - private PlatformBiome plains; - private PlatformBiome forest; + private NativeBlockState stone; + private NativeBlockState dirt; + private NativeBiome plains; + private NativeBiome forest; @Before public void setUp() throws Exception { @@ -44,13 +44,13 @@ public class GoldenHashEngineTest { IrisDimension dimension = mock(IrisDimension.class); when(dimension.getLoadKey()).thenReturn("testdim"); when(engine.getDimension()).thenReturn(dimension); - stone = mock(PlatformBlockState.class); + stone = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); - dirt = mock(PlatformBlockState.class); + dirt = mock(NativeBlockState.class); when(dirt.key()).thenReturn("minecraft:dirt"); - plains = mock(PlatformBiome.class); + plains = mock(NativeBiome.class); when(plains.key()).thenReturn("minecraft:plains"); - forest = mock(PlatformBiome.class); + forest = mock(NativeBiome.class); when(forest.key()).thenReturn("minecraft:forest"); } @@ -161,11 +161,11 @@ public class GoldenHashEngineTest { }; } - private GoldenHashEngine.ChunkSource source(PlatformBlockState special, int sx, int sy, int sz) { + private GoldenHashEngine.ChunkSource source(NativeBlockState special, int sx, int sy, int sz) { return source(special, sx, sy, sz, plains); } - private GoldenHashEngine.ChunkSource source(PlatformBlockState special, int sx, int sy, int sz, PlatformBiome biome) { + private GoldenHashEngine.ChunkSource source(NativeBlockState special, int sx, int sy, int sz, NativeBiome biome) { return (int chunkX, int chunkZ) -> new GoldenHashEngine.ChunkSnapshot() { @Override public int minY() { @@ -178,12 +178,12 @@ public class GoldenHashEngineTest { } @Override - public PlatformBlockState block(int x, int y, int z) { + public NativeBlockState block(int x, int y, int z) { return x == sx && y == sy && z == sz ? special : stone; } @Override - public PlatformBiome biome(int x, int y, int z) { + public NativeBiome biome(int x, int y, int z) { return biome; } }; diff --git a/core/src/test/java/art/arcane/iris/integration/ExternalDataSVCBlockRoutingTest.java b/core/src/test/java/art/arcane/iris/integration/ExternalDataSVCBlockRoutingTest.java index f5070239a..bbf0baf52 100644 --- a/core/src/test/java/art/arcane/iris/integration/ExternalDataSVCBlockRoutingTest.java +++ b/core/src/test/java/art/arcane/iris/integration/ExternalDataSVCBlockRoutingTest.java @@ -1,8 +1,8 @@ package art.arcane.iris.integration; import art.arcane.iris.integration.data.DataType; -import art.arcane.iris.platform.bukkit.nms.container.BlockProperty; -import art.arcane.iris.platform.bukkit.nms.container.Pair; +import art.arcane.volmlib.nativelib.terrain.BlockProperty; +import art.arcane.volmlib.util.collection.Pair; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.block.IrisCustomData; import art.arcane.volmlib.util.collection.KMap; diff --git a/core/src/test/java/art/arcane/iris/pack/ContentKeyValidatorTest.java b/core/src/test/java/art/arcane/iris/pack/ContentKeyValidatorTest.java index 4b5a0498c..e3d2b28c9 100644 --- a/core/src/test/java/art/arcane/iris/pack/ContentKeyValidatorTest.java +++ b/core/src/test/java/art/arcane/iris/pack/ContentKeyValidatorTest.java @@ -2,12 +2,12 @@ package art.arcane.iris.pack; import art.arcane.iris.pack.ContentKeyValidator.ContentKeyError; import art.arcane.iris.pack.ContentKeyValidator.ContentRegistry; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; -import art.arcane.iris.spi.PlatformNumericRange; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; import art.arcane.iris.spi.PlatformRegistries; import org.junit.Test; @@ -37,14 +37,14 @@ public class ContentKeyValidatorTest { List.of(), Map.of( "minecraft:oak_log", List.of( - new PlatformBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null), - new PlatformBlockProperty("waterlogged", "boolean", false, List.of(true, false), null)), + new NativeBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null), + new NativeBlockProperty("waterlogged", "boolean", false, List.of(true, false), null)), // The Bukkit shape for a numeric property: no enumerable values, bounds instead. Modded // enumerates 0..15 into allowedValues, so both must be validated the same way. "minecraft:water", List.of( - new PlatformBlockProperty("level", "integer", 0, List.of(), - new PlatformNumericRange(0, 15, false, false)), - new PlatformBlockProperty("custom", "string", "a", List.of(), null)), + new NativeBlockProperty("level", "integer", 0, List.of(), + new NativeNumericRange(0, 15, false, false)), + new NativeBlockProperty("custom", "string", "a", List.of(), null)), "create:cogwheel", List.of())); } @@ -240,44 +240,44 @@ public class ContentKeyValidatorTest { } private record FakeRegistries(List blocks, List items, List entities, - Map> properties) implements PlatformRegistries { + Map> properties) implements PlatformRegistries { @Override - public PlatformBlockState block(String key) { + public NativeBlockState block(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { return null; } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return null; } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return null; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return null; } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return null; } @@ -327,7 +327,7 @@ public class ContentKeyValidatorTest { } @Override - public Map> blockStateProperties() { + public Map> blockStateProperties() { return properties; } } diff --git a/core/src/test/java/art/arcane/iris/pack/DefaultPackBootstrapProvisionerTest.java b/core/src/test/java/art/arcane/iris/pack/DefaultPackBootstrapProvisionerTest.java index cd83d273e..c7abf39d4 100644 --- a/core/src/test/java/art/arcane/iris/pack/DefaultPackBootstrapProvisionerTest.java +++ b/core/src/test/java/art/arcane/iris/pack/DefaultPackBootstrapProvisionerTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.pack; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; +import art.arcane.iris.pack.datapack.DataVersion; import art.arcane.iris.world.lifecycle.BukkitStartupPaths; import art.arcane.iris.world.history.GenerationRegistryContractFactory; diff --git a/core/src/test/java/art/arcane/iris/pack/PackValidatorDimensionHeightTest.java b/core/src/test/java/art/arcane/iris/pack/PackValidatorDimensionHeightTest.java index 99e9e35d2..30726b6ee 100644 --- a/core/src/test/java/art/arcane/iris/pack/PackValidatorDimensionHeightTest.java +++ b/core/src/test/java/art/arcane/iris/pack/PackValidatorDimensionHeightTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.pack; import art.arcane.iris.generation.terrain.IrisDimensionType; import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; diff --git a/core/src/test/java/art/arcane/iris/pack/PackValidatorStructureGraphTest.java b/core/src/test/java/art/arcane/iris/pack/PackValidatorStructureGraphTest.java index 7f5b55b03..34601f80d 100644 --- a/core/src/test/java/art/arcane/iris/pack/PackValidatorStructureGraphTest.java +++ b/core/src/test/java/art/arcane/iris/pack/PackValidatorStructureGraphTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.pack; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformStructureHooks.JigsawSourceMetadata; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; import art.arcane.iris.testsupport.PlatformLeakGuard; import org.junit.ClassRule; import org.junit.Rule; diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersionTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/DataVersionTest.java similarity index 96% rename from core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersionTest.java rename to core/src/test/java/art/arcane/iris/pack/datapack/DataVersionTest.java index 06ba0b463..c3fe97d64 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/DataVersionTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/DataVersionTest.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.nms.datapack; +package art.arcane.iris.pack.datapack; import org.junit.Test; diff --git a/core/src/test/java/art/arcane/iris/pack/datapack/IrisDatapackCompilerTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/IrisDatapackCompilerTest.java index de229cdd3..c3af8d245 100644 --- a/core/src/test/java/art/arcane/iris/pack/datapack/IrisDatapackCompilerTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/IrisDatapackCompilerTest.java @@ -5,8 +5,8 @@ import art.arcane.iris.world.WorldSlotKey; import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration.IrisGeneratorBinding; import art.arcane.iris.world.lifecycle.BukkitStartupPaths; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.v1217.DataFixerV1217; import art.arcane.iris.pack.DefaultPackBootstrapProvisioner; import art.arcane.iris.world.history.GenerationEpochContractFactory; import art.arcane.iris.world.history.GenerationHistory; diff --git a/core/src/test/java/art/arcane/iris/pack/datapack/ServerConfiguratorRegistryVerificationTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/ServerConfiguratorRegistryVerificationTest.java index cad00cf01..c2c0ccbdd 100644 --- a/core/src/test/java/art/arcane/iris/pack/datapack/ServerConfiguratorRegistryVerificationTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/ServerConfiguratorRegistryVerificationTest.java @@ -96,7 +96,7 @@ public class ServerConfiguratorRegistryVerificationTest { INMSBinding binding = mock(INMSBinding.class); when(binding.supportsDataPacks()).thenReturn(true); - when(binding.getCustomBiomeBaseFor("overworld:missing")).thenReturn(null); + when(binding.hasBiome("overworld:missing")).thenReturn(false); when(binding.missingDimensionTypes("iris:overworld")).thenReturn(true); return new VerificationFixture(data, dimension, binding); } diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217ClockTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217ClockTest.java similarity index 90% rename from core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217ClockTest.java rename to core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217ClockTest.java index b8816c49a..81b259936 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217ClockTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217ClockTest.java @@ -1,6 +1,6 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1217; +package art.arcane.iris.pack.datapack.v1217; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer.Dimension; +import art.arcane.iris.pack.datapack.IDataFixer.Dimension; import art.arcane.volmlib.util.json.JSONObject; import org.junit.Test; diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217CustomBiomeTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217CustomBiomeTest.java similarity index 97% rename from core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217CustomBiomeTest.java rename to core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217CustomBiomeTest.java index 43a749ed3..be2b2539f 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217CustomBiomeTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217CustomBiomeTest.java @@ -1,4 +1,4 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1217; +package art.arcane.iris.pack.datapack.v1217; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.biome.IrisBiomeCustomParticle; diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217DimensionTypeTest.java b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217DimensionTypeTest.java similarity index 93% rename from core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217DimensionTypeTest.java rename to core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217DimensionTypeTest.java index 696a2e6ff..9ed732d86 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/datapack/v1217/DataFixerV1217DimensionTypeTest.java +++ b/core/src/test/java/art/arcane/iris/pack/datapack/v1217/DataFixerV1217DimensionTypeTest.java @@ -1,6 +1,6 @@ -package art.arcane.iris.platform.bukkit.nms.datapack.v1217; +package art.arcane.iris.pack.datapack.v1217; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer.Dimension; +import art.arcane.iris.pack.datapack.IDataFixer.Dimension; import art.arcane.iris.generation.terrain.IrisDimensionTypeOptions; import art.arcane.volmlib.util.json.JSONObject; import org.junit.Test; diff --git a/core/src/test/java/art/arcane/iris/pack/loading/IrisDataAuthoringCacheTest.java b/core/src/test/java/art/arcane/iris/pack/loading/IrisDataAuthoringCacheTest.java index 45aa32eb6..8ed816504 100644 --- a/core/src/test/java/art/arcane/iris/pack/loading/IrisDataAuthoringCacheTest.java +++ b/core/src/test/java/art/arcane/iris/pack/loading/IrisDataAuthoringCacheTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.configuration.IrisSettings; import art.arcane.iris.generation.runtime.PreservationRegistry; import art.arcane.iris.generation.noise.IrisGenerator; import art.arcane.iris.generation.block.B; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.testsupport.PlatformLeakGuard; @@ -121,7 +121,7 @@ public class IrisDataAuthoringCacheTest { Files.writeString(resource, "{}"); source = IrisData.openRuntime(root.toFile()); try (MockedStatic blocks = mockStatic(B.class)) { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.placementBaseState()).thenReturn(state); when(state.materialKey()).thenReturn("minecraft:stone"); blocks.when(() -> B.getStateOrNull("minecraft:stone", false)).thenReturn(state); @@ -152,8 +152,8 @@ public class IrisDataAuthoringCacheTest { """); source = IrisData.openRuntime(root.toFile()); source.setContentGate(mock(ContentGate.class)); - PlatformBlockState custom = mock(PlatformBlockState.class); - PlatformBlockState carrier = mock(PlatformBlockState.class); + NativeBlockState custom = mock(NativeBlockState.class); + NativeBlockState carrier = mock(NativeBlockState.class); when(custom.materialKey()).thenReturn("rocks:ruby_ore"); when(custom.placementBaseState()).thenReturn(carrier); when(carrier.materialKey()).thenReturn("minecraft:note_block"); diff --git a/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderIdentityTest.java b/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderIdentityTest.java index a477bee3f..b5c6ba69c 100644 --- a/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderIdentityTest.java +++ b/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderIdentityTest.java @@ -39,17 +39,17 @@ import art.arcane.iris.structure.nativegen.IrisVanillaStructureAdjustment; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.LogLevel; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; -import art.arcane.iris.spi.PlatformNumericRange; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -78,7 +78,7 @@ public class SchemaBuilderIdentityTest { @ClassRule public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean(); - private static final String EXPECTED_SCHEMA_DIGEST = "5f041feb24213ee5b7e2e06c84b113ec0242b8f8e04c73dde6ea2356944d93fd"; + private static final String EXPECTED_SCHEMA_DIGEST = "91c36f3cdb0a10a0700b62fb6a6d2456576bd50f75a4d588103acdd42a7763c1"; private static final List> SCHEMA_ROOTS = List.of( IrisDimension.class, @@ -202,50 +202,50 @@ public class SchemaBuilderIdentityTest { } private static final class FakeRegistries implements PlatformRegistries { - private static final List STONE_PROPERTIES = List.of( - new PlatformBlockProperty("waterlogged", "boolean", Boolean.FALSE, List.of(Boolean.TRUE, Boolean.FALSE), null), - new PlatformBlockProperty("level", "integer", 0, List.of(0, 1, 2), - new PlatformNumericRange(0D, 2D, false, false))); - private static final List LOG_PROPERTIES = List.of( - new PlatformBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null)); + private static final List STONE_PROPERTIES = List.of( + new NativeBlockProperty("waterlogged", "boolean", Boolean.FALSE, List.of(Boolean.TRUE, Boolean.FALSE), null), + new NativeBlockProperty("level", "integer", 0, List.of(0, 1, 2), + new NativeNumericRange(0D, 2D, false, false))); + private static final List LOG_PROPERTIES = List.of( + new NativeBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null)); @Override - public PlatformBlockState block(String key) { + public NativeBlockState block(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { return null; } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return null; } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return null; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return null; } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return null; } @@ -300,8 +300,8 @@ public class SchemaBuilderIdentityTest { } @Override - public Map> blockStateProperties() { - Map> properties = new LinkedHashMap<>(); + public Map> blockStateProperties() { + Map> properties = new LinkedHashMap<>(); properties.put("minecraft:stone", STONE_PROPERTIES); properties.put("minecraft:andesite", STONE_PROPERTIES); properties.put("minecraft:oak_log", LOG_PROPERTIES); @@ -376,7 +376,7 @@ public class SchemaBuilderIdentityTest { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { return false; } diff --git a/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderParityTest.java b/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderParityTest.java index 2e611481d..b81cef764 100644 --- a/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderParityTest.java +++ b/core/src/test/java/art/arcane/iris/pack/schema/SchemaBuilderParityTest.java @@ -30,7 +30,7 @@ import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.structure.placement.IrisStructure; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.nativegen.IrisVanillaStructureAdjustment; import art.arcane.iris.pack.schema.annotation.ArrayType; import art.arcane.volmlib.util.documentation.Description; @@ -48,16 +48,16 @@ import art.arcane.iris.pack.schema.annotation.functions.LootTableKeyFunction; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.LogLevel; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -203,7 +203,7 @@ public class SchemaBuilderParityTest { assertEquals(0, horizontalPadding.getInt("minimum")); assertEquals(128, horizontalPadding.getInt("maximum")); IrisStructureTerrain terrainDefaults = new IrisStructureTerrain(); - assertEquals(IrisStructureTerrainMode.SOURCE, terrainDefaults.resolvedMode()); + assertEquals(StructureTerrainMode.SOURCE, terrainDefaults.resolvedMode()); assertEquals(64, terrainDefaults.getFlattenRange()); assertEquals(0, terrainDefaults.getHorizontalPadding()); assertEquals(List.of("BOX", "ROUNDED", "ERODED"), oneOfValues( @@ -460,42 +460,42 @@ public class SchemaBuilderParityTest { private static final class FakeRegistries implements PlatformRegistries { @Override - public PlatformBlockState block(String key) { + public NativeBlockState block(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return null; } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { return null; } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return null; } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return null; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return null; } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return null; } @@ -550,7 +550,7 @@ public class SchemaBuilderParityTest { } @Override - public Map> blockStateProperties() { + public Map> blockStateProperties() { return Map.of(); } } @@ -622,7 +622,7 @@ public class SchemaBuilderParityTest { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { return false; } diff --git a/core/src/test/java/art/arcane/iris/pack/validation/CompatFixtures.java b/core/src/test/java/art/arcane/iris/pack/validation/CompatFixtures.java index 5526300d5..e44438866 100644 --- a/core/src/test/java/art/arcane/iris/pack/validation/CompatFixtures.java +++ b/core/src/test/java/art/arcane/iris/pack/validation/CompatFixtures.java @@ -6,16 +6,16 @@ import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; import art.arcane.iris.spi.LogLevel; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import java.io.File; import java.util.ArrayList; @@ -81,18 +81,18 @@ final class CompatFixtures { int blockLookups = 0; @Override - public PlatformBlockState block(String key) { - PlatformBlockState state = blockOrNull(key, false); + public NativeBlockState block(String key) { + NativeBlockState state = blockOrNull(key, false); return state == null ? air() : state; } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return blockOrNull(key, false); } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { blockLookups++; String normalized = key.trim(); int props = normalized.indexOf('['); @@ -108,27 +108,27 @@ final class CompatFixtures { } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return new FakeBlockState("minecraft:air"); } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return ore; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return null; } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return null; } @@ -178,12 +178,12 @@ final class CompatFixtures { } @Override - public Map> blockStateProperties() { + public Map> blockStateProperties() { return Map.of(); } } - record FakeBlockState(String key) implements PlatformBlockState { + record FakeBlockState(String key) implements NativeBlockState { private static final Map HANDLES = new java.util.concurrent.ConcurrentHashMap<>(); @Override @@ -282,12 +282,12 @@ final class CompatFixtures { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return true; } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return state != null && key.equals(state.key()); } @@ -297,7 +297,7 @@ final class CompatFixtures { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { return this; } @@ -387,7 +387,7 @@ final class CompatFixtures { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { return false; } diff --git a/core/src/test/java/art/arcane/iris/pack/validation/ContentGatePlaceholderTest.java b/core/src/test/java/art/arcane/iris/pack/validation/ContentGatePlaceholderTest.java index b00081d3c..a8eb4a716 100644 --- a/core/src/test/java/art/arcane/iris/pack/validation/ContentGatePlaceholderTest.java +++ b/core/src/test/java/art/arcane/iris/pack/validation/ContentGatePlaceholderTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.structure.object.IrisObjectPlacement; import art.arcane.iris.structure.object.IrisObjectReplace; import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.math.RNG; import org.junit.AfterClass; @@ -52,11 +52,11 @@ public class ContentGatePlaceholderTest { @Test public void placeholderOnlyForMissingKeysAndOnlyWhenReady() { ContentGate gate = gate(); - PlatformBlockState present = gate.resolveBlockOrPlaceholder("STONE"); + NativeBlockState present = gate.resolveBlockOrPlaceholder("STONE"); assertFalse(MissingBlockState.isPlaceholder(present)); assertEquals("minecraft:stone", present.key()); - PlatformBlockState missing = gate.resolveBlockOrPlaceholder("minecraft:sulfur[lit=true]"); + NativeBlockState missing = gate.resolveBlockOrPlaceholder("minecraft:sulfur[lit=true]"); assertTrue(MissingBlockState.isPlaceholder(missing)); assertEquals("minecraft:sulfur[lit=true]", missing.key()); assertEquals("minecraft:sulfur", missing.materialKey()); @@ -70,7 +70,7 @@ public class ContentGatePlaceholderTest { @Test public void placeholderBehavesAsAirButKeepsItsKey() { - PlatformBlockState air = registries.air(); + NativeBlockState air = registries.air(); MissingBlockState placeholder = MissingBlockState.of("minecraft:sulfur[lit=true,axis=y]", air); assertTrue(placeholder.isAir()); assertFalse(placeholder.isSolid()); @@ -94,17 +94,17 @@ public class ContentGatePlaceholderTest { @Test public void blockDataPlaceholderFollowsTheChainBeforeGivingUp() throws Exception { IrisData data = IrisData.get(Files.createTempDirectory("iris-compat-placeholder").toFile()); - PlatformBlockState present = new IrisBlockData("minecraft:stone").getBlockDataOrPlaceholder(data); + NativeBlockState present = new IrisBlockData("minecraft:stone").getBlockDataOrPlaceholder(data); assertFalse(MissingBlockState.isPlaceholder(present)); assertEquals("minecraft:stone", present.key()); - PlatformBlockState revived = new IrisBlockData("minecraft:sulfur").setBackup(new IrisBlockData("minecraft:sand")).getBlockDataOrPlaceholder(data); + NativeBlockState revived = new IrisBlockData("minecraft:sulfur").setBackup(new IrisBlockData("minecraft:sand")).getBlockDataOrPlaceholder(data); assertEquals("minecraft:sand", revived.key()); assertFalse(MissingBlockState.isPlaceholder(revived)); IrisBlockData missing = new IrisBlockData("minecraft:sulfur"); missing.getData().put("lit", true); - PlatformBlockState placeholder = missing.getBlockDataOrPlaceholder(data); + NativeBlockState placeholder = missing.getBlockDataOrPlaceholder(data); assertTrue(MissingBlockState.isPlaceholder(placeholder)); assertEquals("minecraft:sulfur[lit=true]", placeholder.key()); assertTrue(missing.getBlockData(data).isAir()); @@ -118,7 +118,7 @@ public class ContentGatePlaceholderTest { IrisData data = IrisData.get(pack); IrisObject object = data.getObjectLoader().load("rescue"); assertNotNull(object); - PlatformBlockState loaded = object.getBlocks().values().iterator().next(); + NativeBlockState loaded = object.getBlocks().values().iterator().next(); assertTrue(loaded.toString(), MissingBlockState.isPlaceholder(loaded)); IrisObjectPlacement placement = placement(); @@ -138,7 +138,7 @@ public class ContentGatePlaceholderTest { mismatch.getEdit().add(replace(1F, true, "minecraft:sulfur[lit=false]", "minecraft:sand")); RecordingPlacer mismatchPlacer = new RecordingPlacer(); object.place(0, 64, 0, mismatchPlacer, mismatch, new RNG(7L), data); - PlatformBlockState untouched = mismatchPlacer.writes.getFirst(); + NativeBlockState untouched = mismatchPlacer.writes.getFirst(); assertTrue(MissingBlockState.isPlaceholder(untouched)); assertTrue(untouched.isAir()); assertEquals(registries.air().nativeHandle(), untouched.nativeHandle()); @@ -183,7 +183,7 @@ public class ContentGatePlaceholderTest { } private static final class RecordingPlacer implements IObjectPlacer { - private final List writes = new ArrayList<>(); + private final List writes = new ArrayList<>(); @Override public int getHighest(int x, int z, IrisData data) { @@ -196,12 +196,12 @@ public class ContentGatePlaceholderTest { } @Override - public void set(int x, int y, int z, PlatformBlockState d) { + public void set(int x, int y, int z, NativeBlockState d) { writes.add(d); } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return null; } diff --git a/core/src/test/java/art/arcane/iris/pack/validation/IrisBlockDataResolutionTest.java b/core/src/test/java/art/arcane/iris/pack/validation/IrisBlockDataResolutionTest.java index e8cf8ebe2..a9874b452 100644 --- a/core/src/test/java/art/arcane/iris/pack/validation/IrisBlockDataResolutionTest.java +++ b/core/src/test/java/art/arcane/iris/pack/validation/IrisBlockDataResolutionTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.pack.validation; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.block.IrisBlockData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -108,7 +108,7 @@ public class IrisBlockDataResolutionTest { IrisBlockData entry = new IrisBlockData("fancy"); entry.getData().put("axis", "x"); - PlatformBlockState resolved = entry.getBlockData(data); + NativeBlockState resolved = entry.getBlockData(data); IrisBlockData parsed = IrisBlockData.from(resolved.key()); assertEquals("craftengine:forest/amber_log", parsed.getBlock()); @@ -124,7 +124,7 @@ public class IrisBlockDataResolutionTest { entry.getData().put("randomYaw", true); entry.getData().put("variant", "OakLarge"); - PlatformBlockState resolved = entry.getBlockData(data); + NativeBlockState resolved = entry.getBlockData(data); IrisBlockData parsed = IrisBlockData.from(resolved.key()); assertEquals(true, parsed.getData().get("randomYaw")); diff --git a/core/src/test/java/art/arcane/iris/pack/value/PlatformStateHookContractTest.java b/core/src/test/java/art/arcane/iris/pack/value/PlatformStateHookContractTest.java index 8a24e5fcd..ff603d766 100644 --- a/core/src/test/java/art/arcane/iris/pack/value/PlatformStateHookContractTest.java +++ b/core/src/test/java/art/arcane/iris/pack/value/PlatformStateHookContractTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.block.BlockDataMergeSupport; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KMap; import org.junit.Test; @@ -19,8 +19,8 @@ import static org.mockito.Mockito.when; public class PlatformStateHookContractTest { @Test public void boundRotatorWinsWhenBukkitClassesArePresent() { - PlatformBlockState source = mock(PlatformBlockState.class); - PlatformBlockState rotated = mock(PlatformBlockState.class); + NativeBlockState source = mock(NativeBlockState.class); + NativeBlockState rotated = mock(NativeBlockState.class); IrisObjectRotation.StateRotator hook = mock(IrisObjectRotation.StateRotator.class); IrisObjectRotation rotation = new IrisObjectRotation(); when(hook.rotate(rotation, source, 1, 2, 3)).thenReturn(rotated); @@ -34,9 +34,9 @@ public class PlatformStateHookContractTest { @Test public void boundMergerWinsWhenBukkitClassesArePresent() { - PlatformBlockState base = mock(PlatformBlockState.class); - PlatformBlockState update = mock(PlatformBlockState.class); - PlatformBlockState merged = mock(PlatformBlockState.class); + NativeBlockState base = mock(NativeBlockState.class); + NativeBlockState update = mock(NativeBlockState.class); + NativeBlockState merged = mock(NativeBlockState.class); BlockDataMergeSupport.StateMerger hook = mock(BlockDataMergeSupport.StateMerger.class); when(hook.merge(base, update)).thenReturn(merged); BlockDataMergeSupport.StateMerger previous = BlockDataMergeSupport.bindPlatformMerger(hook); @@ -49,7 +49,7 @@ public class PlatformStateHookContractTest { @Test public void boundTileFactoryWinsWhenBukkitClassesArePresent() { - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); KMap properties = new KMap<>(); TileData expected = mock(TileData.class); TileData.TileFactory hook = mock(TileData.TileFactory.class); diff --git a/core/src/test/java/art/arcane/iris/platform/BlockStateKeyTest.java b/core/src/test/java/art/arcane/iris/platform/BlockStateKeyTest.java deleted file mode 100644 index 237bde79c..000000000 --- a/core/src/test/java/art/arcane/iris/platform/BlockStateKeyTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package art.arcane.iris.platform; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; - -public class BlockStateKeyTest { - @Test - public void namespaceIgnoresPropertyValues() { - assertEquals("minecraft", BlockStateKey.namespace("stone")); - assertEquals("minecraft", BlockStateKey.namespace("stone[variant=custom:stone]")); - assertEquals("custom", BlockStateKey.namespace("custom:stone[variant=minecraft:stone]")); - assertEquals("", BlockStateKey.namespace(":stone")); - } - - @Test - public void addsAndReplacesPropertiesInEncounterOrder() { - assertEquals("minecraft:stairs[facing=east]", - BlockStateKey.withProperty("minecraft:stairs", "facing", "east")); - assertEquals("minecraft:stairs[facing=east,half=top]", - BlockStateKey.withProperty("minecraft:stairs[facing=north,half=top]", "facing", "east")); - assertEquals("minecraft:stairs[facing=north,half=top,waterlogged=true]", - BlockStateKey.withProperty("minecraft:stairs[facing=north,half=top]", "waterlogged", "true")); - } - - @Test - public void retainsDuplicateAndMalformedEntryHandling() { - assertEquals("custom:block[a=last,b=left=right,c=value]", - BlockStateKey.withProperty("custom:block[ a = first ,invalid,b=left=right,a=last]", "c", "value")); - assertEquals("stone[x=y]", BlockStateKey.withProperty("stone[]", "x", "y")); - assertThrows(StringIndexOutOfBoundsException.class, - () -> BlockStateKey.withProperty("stone[x=y", "x", "z")); - assertThrows(NullPointerException.class, () -> BlockStateKey.namespace(null)); - } -} diff --git a/core/src/test/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibrariesTest.java b/core/src/test/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibrariesTest.java new file mode 100644 index 000000000..2518a2cfc --- /dev/null +++ b/core/src/test/java/art/arcane/iris/platform/bootstrap/NativeRuntimeLibrariesTest.java @@ -0,0 +1,53 @@ +package art.arcane.iris.platform.bootstrap; + +import io.github.slimjar.exceptions.DownloaderException; +import io.github.slimjar.resolver.data.Dependency; +import io.github.slimjar.resolver.data.DependencyData; +import org.junit.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class NativeRuntimeLibrariesTest { + @Test + public void selectsOnlyCommonAndMatchingProvider() throws Exception { + Properties manifest = manifest(); + Dependency original = new Dependency("example", "library", "1", null, List.of()); + DependencyData data = new DependencyData(List.of(), List.of(), List.of(original), List.of()); + List selected = NativeRuntimeLibraries.from(manifest, "26.3.0").merge(data).dependencies() + .stream().map(Dependency::artifactId).sorted().toList(); + assertEquals(List.of("library", "native-common", "native-v26_3_R1"), selected); + assertThrows(UnsupportedOperationException.class, () -> NativeRuntimeLibraries.from(manifest, "1.21.11")); + } + + @Test + public void rejectsProviderBytesFromDifferentBuild() throws Exception { + Path jar = Files.createTempFile("native-runtime", ".jar"); + try { + Files.write(jar, new byte[0]); + NativeRuntimeLibraries.verify(jar.toFile(), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + Files.writeString(jar, "different build"); + assertThrows(DownloaderException.class, () -> NativeRuntimeLibraries.verify(jar.toFile(), + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + } finally { + Files.deleteIfExists(jar); + } + } + + private static Properties manifest() { + Properties properties = new Properties(); + List modules = List.of("native-common", "native-v26_2_R1", "native-v26_3_R1"); + properties.setProperty("modules", String.join(",", modules)); + properties.setProperty("repository", "https://jitpack.io/"); + for (String module : modules) { + properties.setProperty(module + ".coordinate", "com.github.VolmitSoftware.VolmLib:" + module + ":revision"); + properties.setProperty(module + ".sha256", "0".repeat(64)); + } + return properties; + } +} diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/BoardSVCSavedBiomeTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/BoardSVCSavedBiomeTest.java index 4edcb2f5b..e61ffd0ba 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/BoardSVCSavedBiomeTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/BoardSVCSavedBiomeTest.java @@ -21,7 +21,7 @@ import art.arcane.iris.platform.generation.PlatformChunkGenerator; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.world.task.J; @@ -80,7 +80,7 @@ public class BoardSVCSavedBiomeTest { IrisPlatform platform = mock(IrisPlatform.class); PlatformRegistries registries = mock(PlatformRegistries.class); when(platform.registries()).thenReturn(registries); - when(registries.block(anyString())).thenReturn(mock(PlatformBlockState.class)); + when(registries.block(anyString())).thenReturn(mock(NativeBlockState.class)); IrisPlatforms.bind(platform); } diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitBiomeTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitBiomeTest.java index 3e637096e..58ab9d1d7 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitBiomeTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitBiomeTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.platform.bukkit; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import com.github.benmanes.caffeine.cache.Cache; import org.bukkit.NamespacedKey; import org.junit.Before; @@ -39,7 +39,7 @@ public class BukkitBiomeTest { AtomicInteger keyReads = new AtomicInteger(); Object biome = biome("iris", "riverbank", keyReads); - PlatformBiome wrapped = wrap(biome); + NativeBiome wrapped = wrap(biome); assertEquals("iris:riverbank", wrapped.key()); assertEquals("iris", wrapped.namespace()); assertSame(biome, wrapped.nativeHandle()); @@ -53,8 +53,8 @@ public class BukkitBiomeTest { Object previous = biome("iris", "registry_replacement", new AtomicInteger()); Object replacement = biome("iris", "registry_replacement", new AtomicInteger()); - PlatformBiome oldHandle = wrap(previous); - PlatformBiome newHandle = wrap(replacement); + NativeBiome oldHandle = wrap(previous); + NativeBiome newHandle = wrap(replacement); assertNotSame(oldHandle, newHandle); assertEquals(oldHandle.key(), newHandle.key()); @@ -65,8 +65,8 @@ public class BukkitBiomeTest { @Test public void customAndVanillaNamespacesRemainDistinct() throws Exception { - PlatformBiome vanilla = wrap(biome("minecraft", "plains", new AtomicInteger())); - PlatformBiome custom = wrap(biome("iris", "plains", new AtomicInteger())); + NativeBiome vanilla = wrap(biome("minecraft", "plains", new AtomicInteger())); + NativeBiome custom = wrap(biome("iris", "plains", new AtomicInteger())); assertEquals("minecraft:plains", vanilla.key()); assertEquals("minecraft", vanilla.namespace()); @@ -77,7 +77,7 @@ public class BukkitBiomeTest { @Test public void cacheEvictionPreservesBiomeKeysAndNativeHandles() throws Exception { Object retained = biome("iris", "retained", new AtomicInteger()); - PlatformBiome original = wrap(retained); + NativeBiome original = wrap(retained); for (int index = 0; index < 8_192; index++) { wrap(biome("iris", "eviction_" + index, new AtomicInteger())); } @@ -87,14 +87,14 @@ public class BukkitBiomeTest { cache.cleanUp(); assertTrue(cache.estimatedSize() <= 4_096); - PlatformBiome restored = wrap(retained); + NativeBiome restored = wrap(retained); assertEquals(original.key(), restored.key()); assertEquals(original.namespace(), restored.namespace()); assertSame(retained, restored.nativeHandle()); } - private PlatformBiome wrap(Object biome) throws Exception { - return (PlatformBiome) wrap.invoke(null, biome); + private NativeBiome wrap(Object biome) throws Exception { + return (NativeBiome) wrap.invoke(null, biome); } private Object biome(String namespace, String name, AtomicInteger keyReads) { diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitCustomBlockTransformTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitCustomBlockTransformTest.java index 2fe1c7c9f..58e7ae513 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitCustomBlockTransformTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitCustomBlockTransformTest.java @@ -3,10 +3,10 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.BukkitTestServer; import art.arcane.iris.generation.block.IrisCustomData; -import art.arcane.iris.platform.bukkit.registry.RegistryUtil; +import art.arcane.iris.platform.registry.RegistryUtil; import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.junit.BeforeClass; @@ -34,11 +34,11 @@ public class BukkitCustomBlockTransformTest { public void waterloggingUpdatesSemanticIdentityAndResolvedCarrier() { IrisCustomData original = custom("craftengine:forest/amber_slab[axis=x,waterlogged=false]", 1); IrisCustomData expected = custom("craftengine:forest/amber_slab[axis=x,waterlogged=true]", 2); - PlatformBlockState state = BukkitBlockState.of(original); + NativeBlockState state = BukkitBlockState.of(original); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { resolveTo(resolution, expected); - PlatformBlockState result = state.withProperty("waterlogged", "true"); + NativeBlockState result = state.withProperty("waterlogged", "true"); assertSame(expected, result.nativeHandle()); assertEquals(expected.getCustom().toString(), result.deferredPlacementKey()); @@ -50,7 +50,7 @@ public class BukkitCustomBlockTransformTest { @Test public void customPropertyRejectsAnUnresolvableValue() { - PlatformBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_log[axis=x]", 1)); + NativeBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_log[axis=x]", 1)); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { IllegalArgumentException error = assertThrows(IllegalArgumentException.class, @@ -66,11 +66,11 @@ public class BukkitCustomBlockTransformTest { public void semanticFacingRotatesWithoutDirectionalCarrier() { IrisCustomData original = custom("craftengine:forest/amber_lamp[facing=north,waterlogged=true]", 1); IrisCustomData expected = custom("craftengine:forest/amber_lamp[facing=west,waterlogged=true]", 2); - PlatformBlockState state = BukkitBlockState.of(original); + NativeBlockState state = BukkitBlockState.of(original); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { resolveTo(resolution, expected); - PlatformBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); + NativeBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); assertSame(expected, result.nativeHandle()); assertEquals(expected.getCustom().toString(), result.key()); @@ -96,12 +96,12 @@ public class BukkitCustomBlockTransformTest { @Test public void semanticRotationPreservesSixteenWayOrientation() { - PlatformBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_sign[rotation=0]", 1)); + NativeBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_sign[rotation=0]", 1)); IrisCustomData expected = custom("craftengine:forest/amber_sign[rotation=12]", 2); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { resolveTo(resolution, expected); - PlatformBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); + NativeBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); assertSame(expected, result.nativeHandle()); resolution.verify(() -> BukkitBlockResolution.resolveOrNull(anyString())); @@ -110,14 +110,14 @@ public class BukkitCustomBlockTransformTest { @Test public void semanticConnectionsRotateTogether() { - PlatformBlockState state = BukkitBlockState.of(custom( + NativeBlockState state = BukkitBlockState.of(custom( "craftengine:forest/amber_fence[north=true,east=false,south=false,west=false]", 1)); IrisCustomData expected = custom( "craftengine:forest/amber_fence[north=false,east=false,south=false,west=true]", 2); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { resolveTo(resolution, expected); - PlatformBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); + NativeBlockState result = IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0); assertSame(expected, result.nativeHandle()); resolution.verify(() -> BukkitBlockResolution.resolveOrNull(anyString())); @@ -126,7 +126,7 @@ public class BukkitCustomBlockTransformTest { @Test public void unsupportedRotationRetainsTheOriginalCustomBlock() { - PlatformBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_lamp[facing=north]", 1)); + NativeBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_lamp[facing=north]", 1)); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { assertSame(state, IrisObjectRotation.of(90, 0, 0).rotate(state, 0, 0, 0)); @@ -136,7 +136,7 @@ public class BukkitCustomBlockTransformTest { @Test public void unchangedSemanticAxisDoesNotResolveOrMutateCarrier() { - PlatformBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_log[axis=y]", 1)); + NativeBlockState state = BukkitBlockState.of(custom("craftengine:forest/amber_log[axis=y]", 1)); try (MockedStatic resolution = mockStatic(BukkitBlockResolution.class)) { assertSame(state, IrisObjectRotation.of(0, 90, 0).rotate(state, 0, 0, 0)); diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java index d300afc53..acd31474e 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/BukkitSpiConformanceTest.java @@ -21,8 +21,8 @@ package art.arcane.iris.platform.bukkit; import art.arcane.iris.integration.Identifier; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.BukkitTestServer; import art.arcane.iris.generation.block.IrisCustomData; @@ -130,7 +130,7 @@ public class BukkitSpiConformanceTest { public void customStateKeepsProviderIdentityAndProperties() throws Exception { BlockData base = blockData("minecraft:note_block[instrument=harp,note=5,powered=false]"); Identifier identifier = Identifier.fromString("craftengine:forest/amber_log[axis=x]"); - PlatformBlockState state = BukkitBlockState.of(IrisCustomData.of(base, identifier)); + NativeBlockState state = BukkitBlockState.of(IrisCustomData.of(base, identifier)); assertEquals(identifier.toString(), state.key()); assertEquals(identifier.toString(), state.deferredPlacementKey()); @@ -148,9 +148,9 @@ public class BukkitSpiConformanceTest { public void customCarrierPropertyChangeKeepsProviderPropertiesSeparate() { BlockData base = blockData("minecraft:oak_slab[type=bottom,waterlogged=false]"); Identifier identifier = Identifier.fromString("itemsadder:forest/amber_slab"); - PlatformBlockState state = BukkitBlockState.of(IrisCustomData.of(base, identifier)); + NativeBlockState state = BukkitBlockState.of(IrisCustomData.of(base, identifier)); - PlatformBlockState merged = state.withProperty("waterlogged", "true"); + NativeBlockState merged = state.withProperty("waterlogged", "true"); assertEquals(identifier.toString(), merged.key()); assertEquals(identifier.toString(), merged.deferredPlacementKey()); @@ -170,21 +170,21 @@ public class BukkitSpiConformanceTest { @Test public void withPropertyReplacesExistingProperty() { BlockData data = blockData("iristest:merge_block[axis=y,waterlogged=false]"); - PlatformBlockState merged = BukkitBlockState.of(data).withProperty("axis", "x"); + NativeBlockState merged = BukkitBlockState.of(data).withProperty("axis", "x"); assertEquals("iristest:merge_block[axis=x,waterlogged=false]", merged.key()); } @Test public void withPropertyAppendsToExistingProperties() { BlockData data = blockData("iristest:append_block[axis=y]"); - PlatformBlockState merged = BukkitBlockState.of(data).withProperty("lit", "true"); + NativeBlockState merged = BukkitBlockState.of(data).withProperty("lit", "true"); assertEquals("iristest:append_block[axis=y,lit=true]", merged.key()); } @Test public void withPropertyAddsBracketSectionWhenAbsent() { BlockData data = blockData("iristest:bare_block"); - PlatformBlockState merged = BukkitBlockState.of(data).withProperty("lit", "true"); + NativeBlockState merged = BukkitBlockState.of(data).withProperty("lit", "true"); assertEquals("iristest:bare_block[lit=true]", merged.key()); } @@ -256,7 +256,7 @@ public class BukkitSpiConformanceTest { // Pre-flight before touching the registry: org.bukkit.Registry fail-initialises without a live // RegistryAccess, and that is permanent and JVM-wide, so the catch below is already too late. Assume.assumeTrue("live Bukkit registry unavailable in this environment", liveRegistriesAvailable()); - Map> states; + Map> states; try { states = registries().blockStateProperties(); } catch (Throwable unavailable) { @@ -264,9 +264,9 @@ public class BukkitSpiConformanceTest { return; } assertFalse(states.isEmpty()); - for (Map.Entry> entry : states.entrySet()) { + for (Map.Entry> entry : states.entrySet()) { assertTrue("expected namespaced block key but was '" + entry.getKey() + "'", entry.getKey().contains(":")); - for (PlatformBlockProperty property : entry.getValue()) { + for (NativeBlockProperty property : entry.getValue()) { assertFalse(property.name().isEmpty()); assertFalse(property.jsonType().isEmpty()); } diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/BukkitBindingTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/nms/BukkitBindingTest.java new file mode 100644 index 000000000..922b7553b --- /dev/null +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/nms/BukkitBindingTest.java @@ -0,0 +1,51 @@ +package art.arcane.iris.platform.bukkit.nms; + +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRegistry; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainAccess; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainSnapshots; +import art.arcane.volmlib.nativelib.terrain.NativeWorldGeneration; +import art.arcane.volmlib.nativelib.terrain.NativeWorldLifecycleFactory; +import org.bukkit.World; +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class BukkitBindingTest { + @Test + public void chunkCapabilitiesOverrideLimitedBindingDefaults() { + Fixture fixture = new Fixture(); + World world = mock(World.class); + when(fixture.terrain.pollChunkTask(world)).thenReturn(true); + when(fixture.terrain.forceEvictChunk(world, 2, 3)).thenReturn(true); + assertTrue(fixture.binding.pollChunkTask(world)); + assertTrue(fixture.binding.forceEvictChunk(world, 2, 3)); + fixture.binding.flushChunkIO(world); + verify(fixture.terrain).flushChunkIO(world); + } + + @Test + public void lifecycleCapabilitiesOverrideLimitedBindingDefaults() { + Fixture fixture = new Fixture(); + when(fixture.lifecycle.isServerStopping()).thenReturn(true); + assertTrue(fixture.binding.isServerStopping()); + fixture.binding.deferPluginClassLoaderClose(); + fixture.binding.releasePluginClassLoaderClose(); + verify(fixture.lifecycle).deferPluginClassLoaderClose(); + verify(fixture.lifecycle).releasePluginClassLoaderClose(); + } + + private static final class Fixture { + private final NativeTerrainAccess terrain = mock(NativeTerrainAccess.class); + private final NativeWorldLifecycleFactory.Controller lifecycle = mock(NativeWorldLifecycleFactory.Controller.class); + private final BukkitBinding binding; + + private Fixture() { + NativeWorldGeneration generation = mock(NativeWorldGeneration.class); + when(generation.lifecycle(BukkitWorldLifecyclePolicy.INSTANCE)).thenReturn(lifecycle); + binding = new BukkitBinding(new BukkitBinding.Capabilities(terrain, generation, + mock(NativeTerrainSnapshots.class), mock(NativeGenerationRegistry.class))); + } + } +} diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelectorTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelectorTest.java deleted file mode 100644 index b1fa41857..000000000 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/NmsBindingSelectorTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package art.arcane.iris.platform.bukkit.nms; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; - -public class NmsBindingSelectorTest { - @Test - public void selects263Revision() { - assertEquals("v26_3_R1", NmsBindingSelector.select(MinecraftVersion.fromBukkitVersion("26.3-R0.1-SNAPSHOT"))); - assertEquals("v26_3_R1", NmsBindingSelector.select(MinecraftVersion.fromBukkitVersion("26.3.0-R0.1-SNAPSHOT"))); - } - - @Test - public void selectsOnlyTheExactSupportedRevision() { - MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.2-R0.1-SNAPSHOT"); - - assertEquals("v26_2_R1", NmsBindingSelector.select(version)); - } - - @Test - public void selectsSharedRevisionFor2612() { - MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1.2-R0.1-SNAPSHOT"); - - assertEquals("v26_2_R1", NmsBindingSelector.select(version)); - } - - @Test - public void rejectsUnsupportedVersionsWithoutProbingAnotherRevision() { - MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.4-R0.1-SNAPSHOT"); - - IllegalStateException failure = assertThrows(IllegalStateException.class, - () -> NmsBindingSelector.select(version)); - - assertTrue(failure.getMessage().contains("26.4")); - } - - @Test - public void rejectsBasePatchOfSupportedMinorLine() { - MinecraftVersion version = MinecraftVersion.fromBukkitVersion("26.1-R0.1-SNAPSHOT"); - - IllegalStateException failure = assertThrows(IllegalStateException.class, - () -> NmsBindingSelector.select(version)); - - assertTrue(failure.getMessage().contains("26.1")); - } - - @Test - public void rejectsMissingVersionDetection() { - assertThrows(IllegalStateException.class, () -> NmsBindingSelector.select(null)); - } -} diff --git a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundaryTest.java b/core/src/test/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundaryTest.java index 2cfd4d79b..ccb2f6b35 100644 --- a/core/src/test/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundaryTest.java +++ b/core/src/test/java/art/arcane/iris/platform/bukkit/nms/ServerShutdownBoundaryTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.platform.bukkit.nms; +import art.arcane.volmlib.nativelib.terrain.ServerShutdownBoundary; + import org.junit.Test; import java.net.URL; diff --git a/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorHistoryTargetTest.java b/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorHistoryTargetTest.java index 4ce723513..14087e4fe 100644 --- a/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorHistoryTargetTest.java +++ b/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorHistoryTargetTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.platform.generation; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineTarget; import art.arcane.iris.pack.loading.IrisData; diff --git a/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorStudioSelectionTest.java b/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorStudioSelectionTest.java index 5229112a2..0544b1171 100644 --- a/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorStudioSelectionTest.java +++ b/core/src/test/java/art/arcane/iris/platform/generation/BukkitChunkGeneratorStudioSelectionTest.java @@ -1,7 +1,7 @@ package art.arcane.iris.platform.generation; import art.arcane.iris.generation.runtime.IrisEngine; -import art.arcane.iris.generation.cache.AtomicCache; +import art.arcane.volmlib.util.cache.AtomicCache; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EngineTarget; import art.arcane.iris.generation.terrain.IrisDimension; diff --git a/core/src/test/java/art/arcane/iris/platform/generation/EngineBukkitOpsDeferredMaterializationTest.java b/core/src/test/java/art/arcane/iris/platform/generation/EngineBukkitOpsDeferredMaterializationTest.java index f14ab1aca..b067f0b48 100644 --- a/core/src/test/java/art/arcane/iris/platform/generation/EngineBukkitOpsDeferredMaterializationTest.java +++ b/core/src/test/java/art/arcane/iris/platform/generation/EngineBukkitOpsDeferredMaterializationTest.java @@ -11,7 +11,7 @@ import art.arcane.iris.world.IrisWorld; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.world.task.J; @@ -356,7 +356,7 @@ public class EngineBukkitOpsDeferredMaterializationTest { IrisPlatform previous = IrisPlatforms.isBound() ? IrisPlatforms.get() : null; IrisPlatform platform = mock(IrisPlatform.class); PlatformRegistries registries = mock(PlatformRegistries.class); - when(registries.block("AIR")).thenReturn(mock(PlatformBlockState.class)); + when(registries.block("AIR")).thenReturn(mock(NativeBlockState.class)); when(platform.registries()).thenReturn(registries); IrisPlatforms.unbind(); IrisPlatforms.bind(platform); diff --git a/core/src/test/java/art/arcane/iris/purity/PackTypeBukkitPurityGateTest.java b/core/src/test/java/art/arcane/iris/purity/PackTypeBukkitPurityGateTest.java index 8a9beed79..8812b54de 100644 --- a/core/src/test/java/art/arcane/iris/purity/PackTypeBukkitPurityGateTest.java +++ b/core/src/test/java/art/arcane/iris/purity/PackTypeBukkitPurityGateTest.java @@ -119,7 +119,7 @@ public class PackTypeBukkitPurityGateTest { * Bukkit adapter, so they stay out of the gate.) */ private static final List BUKKIT_STATIC_HOLDERS = List.of( - "art.arcane.iris.platform.bukkit.registry.Particles"); + "art.arcane.iris.platform.registry.Particles"); @Test public void theGateActuallyHidesBukkit() throws Exception { diff --git a/core/src/test/java/art/arcane/iris/structure/VillageImporterConnectorRoundTripTest.java b/core/src/test/java/art/arcane/iris/structure/VillageImporterConnectorRoundTripTest.java index 97a2eb607..64325f0c6 100644 --- a/core/src/test/java/art/arcane/iris/structure/VillageImporterConnectorRoundTripTest.java +++ b/core/src/test/java/art/arcane/iris/structure/VillageImporterConnectorRoundTripTest.java @@ -1,6 +1,7 @@ package art.arcane.iris.structure; import art.arcane.iris.structure.export.VanillaJigsawDatapackExporter; +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; import art.arcane.iris.structure.export.VanillaJigsawExportRequest; import art.arcane.iris.structure.export.VanillaJigsawExportResult; import art.arcane.iris.structure.export.VanillaJigsawExportSource; @@ -14,7 +15,7 @@ import art.arcane.iris.structure.jigsaw.IrisJigsawPool; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.structure.placement.IrisStructure; import art.arcane.iris.structure.object.ObjectPlaceMode; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import org.junit.Before; @@ -31,7 +32,6 @@ import java.nio.file.Path; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -60,9 +60,8 @@ public class VillageImporterConnectorRoundTripTest { @Test public void vanillaNbtMetadataSurvivesIrisPieceAndVanillaExport() throws Exception { - FakeJigsawBlockInfo jigsaw = new FakeJigsawBlockInfo(-29, -17); - FakeStructureBlockInfo info = new FakeStructureBlockInfo(new FakeCompoundTag(FINAL_STATE)); - VillageImporter.ConnectorMetadata metadata = VillageImporter.readConnectorMetadata(jigsaw, info); + VillageImporter.ConnectorMetadata metadata = VillageImporter.readConnectorMetadata( + new NativeStructureReader.ConnectorMetadata(FINAL_STATE, -17, -29)); Map importedConnector = VillageImporter.connectorJson( 1, 1, @@ -115,7 +114,7 @@ public class VillageImporterConnectorRoundTripTest { private static TestGraph graph(IrisJigsawPiece piece, IrisJigsawConnector connector) { TestGraph graph = new TestGraph(); IrisObject object = new IrisObject(3, 3, 3); - PlatformBlockState finalState = mock(PlatformBlockState.class); + NativeBlockState finalState = mock(NativeBlockState.class); when(finalState.key()).thenReturn(connector.getFinalState()); object.setUnsigned(1, 1, 0, finalState); graph.objects.put("objects/start", object); @@ -137,18 +136,6 @@ public class VillageImporterConnectorRoundTripTest { return graph; } - private record FakeJigsawBlockInfo(int placementPriority, int selectionPriority) { - } - - private record FakeStructureBlockInfo(FakeCompoundTag nbt) { - } - - private record FakeCompoundTag(String finalState) { - public Optional getString(String key) { - return "final_state".equals(key) ? Optional.of(finalState) : Optional.empty(); - } - } - private static final class TestGraph implements StructureGraphResolver { private final Map pools = new LinkedHashMap<>(); private final Map pieces = new LinkedHashMap<>(); diff --git a/core/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementTest.java b/core/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementTest.java index 2974adc8b..a3c71f2e5 100644 --- a/core/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementTest.java +++ b/core/src/test/java/art/arcane/iris/structure/VillageImporterListPoolElementTest.java @@ -19,6 +19,8 @@ package art.arcane.iris.structure; import art.arcane.iris.structure.authoring.StructureCapability; +import art.arcane.volmlib.nativelib.terrain.NativeStructureReader; +import java.util.function.LongSupplier; import art.arcane.iris.structure.authoring.StructureLoss; import art.arcane.iris.structure.object.IrisObject; import org.junit.Test; @@ -373,49 +375,33 @@ public class VillageImporterListPoolElementTest { assertTrue(imported.emittedCapabilities(omitted).isEmpty()); } - private static final class FakeListPoolElement { - private final List elements; + private abstract static class TestElement implements NativeStructureReader.Element { + @Override + public String typeName() { return getClass().getSimpleName(); } + @Override + public String templateLocation() { return null; } + @Override + public List children() { return null; } + @Override + public NativeStructureReader.ConnectorSet connectors(LongSupplier seed) { throw new UnsupportedOperationException(); } + } - private FakeListPoolElement(List elements) { + private static final class FakeListPoolElement extends TestElement { + private final List elements; + private FakeListPoolElement(List elements) { this.elements = List.copyOf(elements); } - - public List getElements() { - return elements; - } + @Override + public List children() { return elements; } } - private static final class FakeSinglePoolElement { - private final FakeResourceLocation location; - - private FakeSinglePoolElement(String location) { - String[] parts = location.split(":", 2); - this.location = new FakeResourceLocation(parts[0], parts[1]); - } - - public FakeResourceLocation getTemplateLocation() { - return location; - } + private static final class FakeSinglePoolElement extends TestElement { + private final String location; + private FakeSinglePoolElement(String location) { this.location = location; } + @Override + public String templateLocation() { return location; } } - private static final class FakeFeaturePoolElement { - } - - private static final class FakeResourceLocation { - private final String namespace; - private final String path; - - private FakeResourceLocation(String namespace, String path) { - this.namespace = namespace; - this.path = path; - } - - public String getNamespace() { - return namespace; - } - - public String getPath() { - return path; - } + private static final class FakeFeaturePoolElement extends TestElement { } } diff --git a/core/src/test/java/art/arcane/iris/structure/VillageImporterMaxDistanceReadTest.java b/core/src/test/java/art/arcane/iris/structure/VillageImporterMaxDistanceReadTest.java deleted file mode 100644 index 5d7e245f0..000000000 --- a/core/src/test/java/art/arcane/iris/structure/VillageImporterMaxDistanceReadTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package art.arcane.iris.structure; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -/** - * JigsawStructure.maxDistanceFromCenter is a plain int on some server builds and a - * JigsawStructure$MaxDistance record ({int horizontal, int vertical}) on others (e.g. Leaf - * 26.2-33). The reflective member reader must handle both — the wrapper shape previously threw - * IllegalArgumentException from Field.getInt, failing the jigsaw import of every datapack - * structure ("Failed to read jigsaw structure graph"). - */ -public class VillageImporterMaxDistanceReadTest { - private static final class IntShape { - private final int maxDistanceFromCenter = 80; - } - - private record MaxDistance(int horizontal, int vertical) { - } - - private static final class WrapperShape { - private final MaxDistance maxDistanceFromCenter = new MaxDistance(96, 48); - } - - private static final class BoxedShape { - private final Integer maxDistanceFromCenter = 64; - } - - @Test - public void readsPlainIntField() throws Exception { - assertEquals(80, VillageImporter.readIntMember(new IntShape(), "maxDistanceFromCenter")); - } - - @Test - public void readsLargestIntComponentFromWrapperRecord() throws Exception { - assertEquals(96, VillageImporter.readIntMember(new WrapperShape(), "maxDistanceFromCenter")); - } - - @Test - public void readsBoxedNumberField() throws Exception { - assertEquals(64, VillageImporter.readIntMember(new BoxedShape(), "maxDistanceFromCenter")); - } -} diff --git a/core/src/test/java/art/arcane/iris/structure/export/VanillaJigsawDatapackExporterTest.java b/core/src/test/java/art/arcane/iris/structure/export/VanillaJigsawDatapackExporterTest.java index a993073d6..6efb7f46a 100644 --- a/core/src/test/java/art/arcane/iris/structure/export/VanillaJigsawDatapackExporterTest.java +++ b/core/src/test/java/art/arcane/iris/structure/export/VanillaJigsawDatapackExporterTest.java @@ -18,7 +18,7 @@ import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.collection.KList; @@ -58,7 +58,7 @@ public class VanillaJigsawDatapackExporterTest { @ClassRule public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean(); - private static PlatformBlockState stone; + private static NativeBlockState stone; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @@ -196,7 +196,7 @@ public class VanillaJigsawDatapackExporterTest { @Test public void preservesExplicitAirAndStructureVoidConnectorFinalStates() throws Exception { TestGraph graph = terminalGraph(); - PlatformBlockState air = blockState("minecraft:air"); + NativeBlockState air = blockState("minecraft:air"); IrisObject object = graph.objects.get("objects/start"); object.setUnsigned(0, 1, 1, air); IrisJigsawPiece piece = graph.pieces.get("pieces/start"); @@ -407,8 +407,8 @@ public class VanillaJigsawDatapackExporterTest { return new IrisJigsawPieceEntry().setEmpty(true).setWeight(weight); } - private static PlatformBlockState blockState(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState blockState(String key) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.namespace()).thenReturn(key.substring(0, key.indexOf(':'))); return state; diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControlTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControlTest.java index 55178c94d..0bbfddb6c 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControlTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/IrisImportedStructureControlTest.java @@ -21,7 +21,7 @@ package art.arcane.iris.structure.nativegen; import art.arcane.iris.structure.placement.IrisStructureSetFrequencyOverride; import art.arcane.iris.structure.placement.IrisStructureStiltSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.placement.IrisStructureYBand; import art.arcane.volmlib.util.collection.KList; @@ -224,9 +224,9 @@ public class IrisImportedStructureControlTest { @Test public void multipleMatchesUseTheLastConfiguredTerrainAndBand() { IrisStructureTerrain broadTerrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.SOURCE); + .setMode(StructureTerrainMode.SOURCE); IrisStructureTerrain exactTerrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE) + .setMode(StructureTerrainMode.ENCASE) .setHorizontalPadding(4); IrisStructureYBand band = new IrisStructureYBand().setMin(-120).setMax(-20); IrisVanillaStructureAdjustment broad = new IrisVanillaStructureAdjustment() @@ -253,27 +253,27 @@ public class IrisImportedStructureControlTest { public void namespaceVacuumCanPreserveSpecificStructuresLater() { IrisVanillaStructureAdjustment namespace = new IrisVanillaStructureAdjustment() .setMatch(keys("towns_and_towers:")) - .setTerrain(new IrisStructureTerrain().setMode(IrisStructureTerrainMode.VACUUM)); + .setTerrain(new IrisStructureTerrain().setMode(StructureTerrainMode.VACUUM)); IrisVanillaStructureAdjustment preserveShips = new IrisVanillaStructureAdjustment() .setMatch(keys( "towns_and_towers:mimic_desert", "towns_and_towers:pillager_outpost_ocean", "towns_and_towers:village_ocean", "towns_and_towers:wreckage_ocean")) - .setTerrain(new IrisStructureTerrain().setMode(IrisStructureTerrainMode.PRESERVE)); + .setTerrain(new IrisStructureTerrain().setMode(StructureTerrainMode.PRESERVE)); KList adjustments = new KList<>(); adjustments.add(namespace); adjustments.add(preserveShips); IrisImportedStructureControl control = new IrisImportedStructureControl() .setAdjustments(adjustments); - assertEquals(IrisStructureTerrainMode.VACUUM, + assertEquals(StructureTerrainMode.VACUUM, control.resolve("towns_and_towers:village_forest", false) .terrain().resolvedMode()); - assertEquals(IrisStructureTerrainMode.PRESERVE, + assertEquals(StructureTerrainMode.PRESERVE, control.resolve("towns_and_towers:village_ocean", false) .terrain().resolvedMode()); - assertEquals(IrisStructureTerrainMode.PRESERVE, + assertEquals(StructureTerrainMode.PRESERVE, control.resolve("towns_and_towers:mimic_desert", true) .terrain().resolvedMode()); } diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScaleTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScaleTest.java index 101d3d73a..b67198236 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScaleTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureFrequencyScaleTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.NativeStructureFrequencyScale; + import org.junit.Test; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureObjectVetoTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureObjectVetoTest.java index 46c95bfd5..8f4fa88b1 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureObjectVetoTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureObjectVetoTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; + import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.structure.object.IObjectPlacer; @@ -11,7 +13,7 @@ import art.arcane.iris.structure.object.ObjectPlaceMode; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.stream.ProceduralStream; @@ -39,7 +41,7 @@ public class NativeStructureObjectVetoTest { private IrisData data; private Engine engine; - private PlatformBlockState log; + private NativeBlockState log; @Before public void bindPlatform() { @@ -251,8 +253,8 @@ public class NativeStructureObjectVetoTest { return new NativeStructureVolume("minecraft:village_plains", minX, minY, minZ, maxX, maxY, maxZ); } - private static PlatformBlockState state(String key, boolean solid) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key, boolean solid) { + NativeBlockState state = mock(NativeBlockState.class); when(state.isSolid()).thenReturn(solid); when(state.key()).thenReturn(key); when(state.materialKey()).thenReturn(key); @@ -261,7 +263,7 @@ public class NativeStructureObjectVetoTest { private static final class RecordingPlacer implements IObjectPlacer { private final List written = new ArrayList<>(); - private final PlatformBlockState air = state("minecraft:air", false); + private final NativeBlockState air = state("minecraft:air", false); private final Engine engine; private int tiles; private int markers; @@ -293,12 +295,12 @@ public class NativeStructureObjectVetoTest { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { written.add(new int[]{x, y, z}); } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return air; } diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecordTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecordTest.java index c090acf45..384f65bc8 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecordTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipRecordTest.java @@ -1,9 +1,11 @@ package art.arcane.iris.structure.nativegen; -import art.arcane.iris.structure.placement.IrisStructureCarveShape; +import art.arcane.volmlib.nativelib.terrain.structure.StructureReferenceBounds; + +import art.arcane.volmlib.util.structure.StructureCarveShape; import art.arcane.iris.structure.placement.IrisStructureStiltSettings; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import org.junit.Test; import java.io.ByteArrayInputStream; @@ -46,11 +48,11 @@ public class NativeStructureOwnershipRecordTest { @Test public void decisionSnapshotFreezesTerrainAndStiltSettings() { IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(9) .setCeilingPadding(3) .setFloorPadding(2) - .setShape(IrisStructureCarveShape.ERODED) + .setShape(StructureCarveShape.ERODED) .setErosionStrength(0.42D) .setErosionFrequency(0.17D) .setLobeFrequency(0.03D) @@ -75,8 +77,8 @@ public class NativeStructureOwnershipRecordTest { IrisNativeStructureDecision restored = snapshot.restore(); assertTrue(restored.generate()); - assertEquals(IrisStructureTerrainMode.FORCE_CARVE, restored.terrain().resolvedMode()); - assertEquals(IrisStructureCarveShape.ERODED, restored.terrain().resolvedShape()); + assertEquals(StructureTerrainMode.FORCE_CARVE, restored.terrain().resolvedMode()); + assertEquals(StructureCarveShape.ERODED, restored.terrain().resolvedShape()); assertEquals(9, restored.terrain().getHorizontalPadding()); assertEquals(0.42D, restored.terrain().getErosionStrength(), 0D); assertNotNull(restored.stilt()); @@ -103,8 +105,7 @@ public class NativeStructureOwnershipRecordTest { NativeStructureOwnershipRecord original = record( "nova_structures:tavern_oak", -3, 9, 83L); - NativeStructureOwnershipRecord refreshed = original.withReferenceEnvelope( - -6, 0, 6, 12); + NativeStructureOwnershipRecord refreshed = original.withReferenceEnvelope(new StructureReferenceBounds(-6, 0, 6, 12)); assertEquals(original.schema(), refreshed.schema()); assertEquals(original.ownershipKey(), refreshed.ownershipKey()); diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipStoreTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipStoreTest.java index 0f11e40ff..455c2063c 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipStoreTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureOwnershipStoreTest.java @@ -6,7 +6,7 @@ import art.arcane.iris.generation.runtime.IrisEngine; import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.mantle.runtime.Mantle; import art.arcane.volmlib.util.mantle.runtime.MantleChunk; @@ -48,7 +48,7 @@ public class NativeStructureOwnershipStoreTest { @BeforeClass public static void initializeMantleBlockState() throws Exception { - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState("AIR")).thenReturn(air); Class.forName(EngineMantle.class.getName()); diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructurePlacementPlannerTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructurePlacementPlannerTest.java index 98586e4a5..cb7fab779 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructurePlacementPlannerTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructurePlacementPlannerTest.java @@ -9,7 +9,7 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.structure.placement.IrisStructureAnchorMode; import art.arcane.iris.structure.placement.IrisStructurePlacement; import art.arcane.iris.structure.placement.IrisStructureTerrain; -import art.arcane.iris.structure.placement.IrisStructureTerrainMode; +import art.arcane.volmlib.util.structure.StructureTerrainMode; import art.arcane.iris.structure.placement.StructureDistribution; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.collection.KList; @@ -76,7 +76,7 @@ public class NativeStructurePlacementPlannerTest { @Test public void configuredDecisionOverridesSuppressedSourceWithoutLosingTerrain() { IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(24); IrisStructurePlacement placement = nativePlacement() .setUnderground(true) diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemoTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemoTest.java index 76dcfae03..0440eb2ee 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemoTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeMemoTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.EnginePlatformHooks; diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeTest.java index 2bb165238..ffa340429 100644 --- a/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeTest.java +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/NativeStructureVolumeTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.nativegen; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; + import org.junit.Test; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/art/arcane/iris/structure/nativegen/StructureLocateSearchTest.java b/core/src/test/java/art/arcane/iris/structure/nativegen/StructureLocateSearchTest.java new file mode 100644 index 000000000..34e8158d8 --- /dev/null +++ b/core/src/test/java/art/arcane/iris/structure/nativegen/StructureLocateSearchTest.java @@ -0,0 +1,68 @@ +package art.arcane.iris.structure.nativegen; + +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.structure.placement.IrisStructureLocator; +import art.arcane.volmlib.nativelib.terrain.StructureLocateProbe; +import org.junit.Test; + +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +public class StructureLocateSearchTest { + private static final IrisStructureLocator.LocateResult RESULT = new IrisStructureLocator.LocateResult( + IrisStructureLocator.LocateStatus.FOUND, -17, 64, 33); + + @Test + @SuppressWarnings("unchecked") + public void missingStartDoesNotResolveOwnership() { + StructureLocateProbe probe = mock(StructureLocateProbe.class); + Function ownership = mock(Function.class); + StructureLocateSearch search = search(probe, ownership); + + assertNull(search.verify(RESULT)); + verify(probe).verifySelected(-2, 2); + verifyNoInteractions(ownership); + } + + @Test + @SuppressWarnings("unchecked") + public void existingStartRequiresProvenOwnership() { + StructureLocateProbe probe = mock(StructureLocateProbe.class); + Function ownership = mock(Function.class); + when(probe.verifySelected(-2, 2)).thenReturn("native-start"); + + assertNull(search(probe, ownership).verify(RESULT)); + verify(ownership).apply("native-start"); + } + + @Test + @SuppressWarnings("unchecked") + public void verifiedSelectionReferencesTheOriginalStart() { + StructureLocateProbe probe = mock(StructureLocateProbe.class); + Function ownership = mock(Function.class); + NativeStructureOwnershipRecord record = mock(NativeStructureOwnershipRecord.class); + when(probe.verifySelected(-2, 2)).thenReturn("native-start"); + when(ownership.apply("native-start")).thenReturn(record); + StructureLocateSearch search = search(probe, ownership); + + StructureLocateSearch.VerifiedStart verified = search.verify(RESULT); + + assertEquals("native-start", verified.start()); + assertSame(record, verified.ownership()); + search.reference(verified); + verify(probe).reference("native-start"); + } + + private static StructureLocateSearch search(StructureLocateProbe probe, + Function ownership) { + return new StructureLocateSearch<>(new StructureLocateSearch.Options<>( + mock(Engine.class), "minecraft:village_plains", 0, 0, 16, probe, ownership)); + } +} diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectCopyTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectCopyTest.java index acd9cf148..be56c97e6 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectCopyTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectCopyTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.Vector3i; import art.arcane.volmlib.util.collection.KMap; @@ -26,7 +26,7 @@ public class IrisObjectCopyTest { @Test public void copyWaitsForVolumeMutationToComplete() throws Exception { IrisObject source = new IrisObject(1, 1, 1); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); CountDownLatch started = new CountDownLatch(1); try (ExecutorService executor = Executors.newSingleThreadExecutor()) { Future result; @@ -55,7 +55,7 @@ public class IrisObjectCopyTest { @Test public void copyOwnsItsVolumeCoordinatesAndTiles() { IrisObject source = new IrisObject(1, 1, 1); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); TileData tile = new TileData("minecraft:chest", new KMap<>()); tile.getProperties().put("name", "source"); source.setUnsigned(0, 0, 0, block); diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectDirectPlacementTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectDirectPlacementTest.java index 97c1c7d38..6892c8d54 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectDirectPlacementTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectDirectPlacementTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.integration.Identifier; import art.arcane.iris.integration.ExternalDataSVC; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.IrisCustomData; import org.bukkit.Location; import org.bukkit.World; @@ -64,7 +64,7 @@ public class IrisObjectDirectPlacementTest { private static IrisObject object(BlockData data) { IrisObject object = new IrisObject(1, 1, 1); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.nativeHandle()).thenReturn(data); object.setUnsigned(0, 0, 0, state); return object; diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectGlobalScaleTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectGlobalScaleTest.java index 342460776..cd9e772a5 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectGlobalScaleTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectGlobalScaleTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.math.RNG; import com.google.gson.Gson; @@ -78,7 +78,7 @@ public class IrisObjectGlobalScaleTest { @Test public void globalAndExplicitFactorsScaleOccupiedCubeBoundsExactly() { IrisObject origin = new IrisObject(4, 4, 4); - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { for (int z = 0; z < 4; z++) { diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoPaletteTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoPaletteTest.java index bd4c0f2b0..d70637e81 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoPaletteTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoPaletteTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.testsupport.KeyedBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import org.junit.Test; @@ -41,7 +41,7 @@ public class IrisObjectIoPaletteTest { List firstSeen = new ArrayList<>(); Set seen = new HashSet<>(); - for (Map.Entry entry : object.blocks) { + for (Map.Entry entry : object.blocks) { if (seen.add(entry.getValue().key())) { firstSeen.add(entry.getValue().key()); } diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoWriteLimitsTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoWriteLimitsTest.java index f630fb9b6..5c37bdca9 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoWriteLimitsTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectIoWriteLimitsTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.testsupport.KeyedBlockState; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KMap; import org.junit.BeforeClass; @@ -34,7 +34,7 @@ public class IrisObjectIoWriteLimitsTest { oversizedPaletteObject = objectWithDistinctStates(32_768); } - private static PlatformBlockState state(String key) { + private static NativeBlockState state(String key) { return new KeyedBlockState(key); } diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectPlacementRunnerRegressionTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectPlacementRunnerRegressionTest.java index 8e0ced642..082d8f543 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectPlacementRunnerRegressionTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectPlacementRunnerRegressionTest.java @@ -12,11 +12,11 @@ import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.structure.nativegen.NativeStructureVolume; +import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume; import art.arcane.iris.structure.placement.PlacedObject; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.noise.CNG; @@ -63,14 +63,14 @@ public class IrisObjectPlacementRunnerRegressionTest { private IrisData data; private Engine engine; - private PlatformBlockState solid; - private PlatformBlockState schematicAir; + private NativeBlockState solid; + private NativeBlockState schematicAir; @Before public void bindPlatform() { IrisPlatforms.unbind(); PlatformRegistries registries = mock(PlatformRegistries.class); - Map registryStates = new HashMap<>(); + Map registryStates = new HashMap<>(); when(registries.block(anyString())).thenAnswer(invocation -> { String key = invocation.getArgument(0); return registryStates.computeIfAbsent(key, value -> state(value.toLowerCase(), !value.toLowerCase().contains("air"))); @@ -94,7 +94,7 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void unsupportedRotatedOrientationOmitsBlockTileAndListener() { - PlatformBlockState torch = state("minecraft:wall_torch", false); + NativeBlockState torch = state("minecraft:wall_torch", false); Directional directional = mock(Directional.class); Material material = mock(Material.class); when(torch.nativeHandle()).thenReturn(directional); @@ -109,7 +109,7 @@ public class IrisObjectPlacementRunnerRegressionTest { object.setUnsigned(1, 0, 0, torch); object.setUnsignedTile(1, 0, 0, new TileData("minecraft:wall_torch", new KMap<>())); RecordingPlacer placer = new RecordingPlacer(null); - List placed = new ArrayList<>(); + List placed = new ArrayList<>(); int result = object.place(0, ANCHOR_Y, 0, placer, placement, new RNG(2L), (position, state) -> placed.add(state), null, data); @@ -232,7 +232,7 @@ public class IrisObjectPlacementRunnerRegressionTest { object.place(0, ANCHOR_Y, 0, debug, placement, new RNG(2L), data); object.place(0, ANCHOR_Y, 0, normal, placement, new RNG(2L), data); - PlatformBlockState cachedState = object.getBlocks().get(object.getSigned(0, 0, 0)); + NativeBlockState cachedState = object.getBlocks().get(object.getSigned(0, 0, 0)); assertSame(IrisObject.States.vair(), cachedState); if (IrisObject.States.vair() != IrisObject.States.vairDebug()) { assertTrue(cachedState != IrisObject.States.vairDebug()); @@ -252,7 +252,7 @@ public class IrisObjectPlacementRunnerRegressionTest { RecordingPlacer placer = new RecordingPlacer(null); placer.set(0, ANCHOR_Y, 0, solid); placer.set(1, ANCHOR_Y, 0, solid); - PlatformBlockState vine = state("minecraft:vine[east=false,north=true]", false); + NativeBlockState vine = state("minecraft:vine[east=false,north=true]", false); when(vine.isVineBlock()).thenReturn(true); IrisObject object = new IrisObject(1, 1, 1); object.setUnsigned(0, 0, 0, vine); @@ -269,7 +269,7 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void changingMaterialWithAnEditClearsOriginalTileData() { RecordingPlacer placer = new RecordingPlacer(null); - PlatformBlockState chest = state("minecraft:chest", true); + NativeBlockState chest = state("minecraft:chest", true); IrisObject object = new IrisObject(1, 1, 1); object.setUnsigned(0, 0, 0, chest); object.setUnsignedTile(0, 0, 0, new TileData("minecraft:chest", new KMap<>())); @@ -422,7 +422,7 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void everyStiltModePreservesTheBarrelAndItsTileWithoutRepeatingIt() { - PlatformBlockState barrel = state("minecraft:barrel[facing=up,open=false]", true); + NativeBlockState barrel = state("minecraft:barrel[facing=up,open=false]", true); when(barrel.materialKey()).thenReturn("minecraft:barrel"); when(barrel.isStorage()).thenReturn(true); when(barrel.hasTileEntity()).thenReturn(true); @@ -457,8 +457,8 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void excludedSourceMaterialKeepsItsBlockAndDoesNotUseTheStiltPalette() { - PlatformBlockState calcite = state("minecraft:calcite", true); - PlatformBlockState cobblestone = state("minecraft:cobblestone", true); + NativeBlockState calcite = state("minecraft:calcite", true); + NativeBlockState cobblestone = state("minecraft:cobblestone", true); IrisMaterialPalette palette = mock(IrisMaterialPalette.class); when(palette.get(any(RNG.class), anyDouble(), anyDouble(), anyDouble(), any(IrisData.class))) .thenReturn(cobblestone); @@ -479,9 +479,9 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void translucentFullBlocksGrowFoundationsWhileCrystalModelsRemainUnextended() { - PlatformBlockState ice = state("minecraft:ice", true); + NativeBlockState ice = state("minecraft:ice", true); when(ice.isOccluding()).thenReturn(false); - PlatformBlockState cluster = state("minecraft:amethyst_cluster", true); + NativeBlockState cluster = state("minecraft:amethyst_cluster", true); when(cluster.isOccluding()).thenReturn(false); IrisObject object = new IrisObject(3, 1, 1); object.setUnsigned(0, 0, 0, ice); @@ -503,9 +503,9 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void generatedGlacialOverhangGrowsPackedIceRootsToTerrain() { - PlatformBlockState ice = state("minecraft:ice", true); + NativeBlockState ice = state("minecraft:ice", true); when(ice.isOccluding()).thenReturn(false); - PlatformBlockState packedIce = state("minecraft:packed_ice", true); + NativeBlockState packedIce = state("minecraft:packed_ice", true); IrisMaterialPalette body = mock(IrisMaterialPalette.class); when(body.getPalette()).thenReturn(new KList<>(new IrisBlockData("minecraft:ice"))); when(body.get(any(RNG.class), anyDouble(), anyDouble(), anyDouble(), any(IrisData.class))) @@ -546,7 +546,7 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void excludedLowestBlockDoesNotMoveTheFoundationUpIntoTheOriginalObject() { - PlatformBlockState machine = state("test:machine", true); + NativeBlockState machine = state("test:machine", true); when(machine.hasTileEntity()).thenReturn(true); IrisObject object = new IrisObject(1, 3, 1); object.setUnsigned(0, 0, 0, machine); @@ -562,10 +562,10 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void stiltPaletteCannotIntroduceExcludedMaterialsOrContainers() { - PlatformBlockState calcite = state("minecraft:calcite", true); - PlatformBlockState barrel = state("minecraft:barrel", true); + NativeBlockState calcite = state("minecraft:calcite", true); + NativeBlockState barrel = state("minecraft:barrel", true); when(barrel.isStorage()).thenReturn(true); - for (PlatformBlockState replacement : List.of(calcite, barrel)) { + for (NativeBlockState replacement : List.of(calcite, barrel)) { IrisMaterialPalette palette = mock(IrisMaterialPalette.class); when(palette.get(any(RNG.class), anyDouble(), anyDouble(), anyDouble(), any(IrisData.class))) .thenReturn(replacement); @@ -583,10 +583,10 @@ public class IrisObjectPlacementRunnerRegressionTest { @Test public void objectEditsCannotIntroduceExcludedMaterialsOrTileEntitiesIntoStilts() { - PlatformBlockState calcite = state("minecraft:calcite", true); - PlatformBlockState machine = state("test:machine", true); + NativeBlockState calcite = state("minecraft:calcite", true); + NativeBlockState machine = state("test:machine", true); when(machine.hasTileEntity()).thenReturn(true); - for (PlatformBlockState replacement : List.of(calcite, machine)) { + for (NativeBlockState replacement : List.of(calcite, machine)) { IrisObjectReplace edit = mock(IrisObjectReplace.class); IrisMaterialPalette palette = mock(IrisMaterialPalette.class); when(edit.getChance()).thenReturn(1F); @@ -641,8 +641,8 @@ public class IrisObjectPlacementRunnerRegressionTest { return object; } - private static PlatformBlockState state(String key, boolean solid) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key, boolean solid) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.materialKey()).thenReturn(key); when(state.isSolid()).thenReturn(solid); @@ -652,7 +652,7 @@ public class IrisObjectPlacementRunnerRegressionTest { private static final class RecordingPlacer implements IObjectPlacer { private final List writes = new ArrayList<>(); - private final Map world = new HashMap<>(); + private final Map world = new HashMap<>(); private final Map data = new HashMap<>(); private final List sampledColumns = new ArrayList<>(); private final Map terrain = new HashMap<>(); @@ -687,7 +687,7 @@ public class IrisObjectPlacementRunnerRegressionTest { return writes; } - private List writesOf(PlatformBlockState state) { + private List writesOf(NativeBlockState state) { return writes.stream().filter(write -> write.state() == state).toList(); } @@ -708,7 +708,7 @@ public class IrisObjectPlacementRunnerRegressionTest { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { if (writes.size() >= failAfterWrites) { throw new IllegalStateException("write failed"); } @@ -717,7 +717,7 @@ public class IrisObjectPlacementRunnerRegressionTest { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return world.get(x + ":" + y + ":" + z); } @@ -747,7 +747,7 @@ public class IrisObjectPlacementRunnerRegressionTest { @Override public boolean isSolid(int x, int y, int z) { - PlatformBlockState state = get(x, y, z); + NativeBlockState state = get(x, y, z); return state != null && state.isSolid(); } @@ -787,6 +787,6 @@ public class IrisObjectPlacementRunnerRegressionTest { } } - private record BlockWrite(int x, int y, int z, PlatformBlockState state) { + private record BlockWrite(int x, int y, int z, NativeBlockState state) { } } diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectScaleCacheTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectScaleCacheTest.java index 30d244a07..bc0ec87c7 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectScaleCacheTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectScaleCacheTest.java @@ -1,7 +1,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.math.RNG; import org.junit.After; import org.junit.Before; @@ -76,7 +76,7 @@ public class IrisObjectScaleCacheTest { IrisObjectScale scale = new IrisObjectScale().setSize(2D); IrisObject empty = scale.get(new RNG(1L), origin); - origin.setUnsigned(0, 0, 0, mock(PlatformBlockState.class)); + origin.setUnsigned(0, 0, 0, mock(NativeBlockState.class)); IrisObject populated = scale.get(new RNG(1L), origin); assertEquals(0, empty.getBlocks().size()); diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectShapingTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectShapingTest.java index ec8cb1e21..588e200b7 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectShapingTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectShapingTest.java @@ -2,7 +2,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -32,9 +32,9 @@ public class IrisObjectShapingTest { public void bindPlatform() { IrisPlatforms.unbind(); PlatformRegistries registries = mock(PlatformRegistries.class); - Map states = new HashMap<>(); + Map states = new HashMap<>(); when(registries.block(anyString())).thenAnswer(invocation -> states.computeIfAbsent( - invocation.getArgument(0), key -> mock(PlatformBlockState.class))); + invocation.getArgument(0), key -> mock(NativeBlockState.class))); IrisPlatform platform = mock(IrisPlatform.class); when(platform.registries()).thenReturn(registries); IrisPlatforms.bind(platform); @@ -48,7 +48,7 @@ public class IrisObjectShapingTest { @Test public void smartBoreMatchesOrderedAxisClosureAndPreservesOriginalBlocks() { Random random = new Random(617431L); - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); for (int fixture = 0; fixture < 24; fixture++) { int offset = switch (fixture % 3) { case 0 -> -1050; @@ -56,10 +56,10 @@ public class IrisObjectShapingTest { default -> -3; }; IrisObject object = new IrisObject(6, 6, 6); - Map expected = new HashMap<>(); + Map expected = new HashMap<>(); for (int index = 0; index < 36; index++) { Cell cell = new Cell(offset + random.nextInt(6), offset + random.nextInt(6), offset + random.nextInt(6)); - PlatformBlockState state = switch (index % 3) { + NativeBlockState state = switch (index % 3) { case 0 -> IrisObject.States.air(); case 1 -> IrisObject.States.vair(); default -> stone; @@ -75,7 +75,7 @@ public class IrisObjectShapingTest { assertTrue(object.isSmartBored()); assertEquals("fixture " + fixture, expected.size(), object.getBlocks().size()); - for (Map.Entry entry : expected.entrySet()) { + for (Map.Entry entry : expected.entrySet()) { assertSame(entry.getValue(), object.getBlocks().get(entry.getKey().vector())); } long revision = object.getBlocks().modificationRevision(); @@ -94,7 +94,7 @@ public class IrisObjectShapingTest { assertTrue(object.getBlocks().isEmpty()); } - private static void closeAxis(Map blocks, int axis) { + private static void closeAxis(Map blocks, int axis) { List occupied = new ArrayList<>(blocks.keySet()); for (Cell start : occupied) { for (Cell end : occupied) { diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectSurfaceSupportPlacementTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectSurfaceSupportPlacementTest.java index eec7091ea..61589e629 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectSurfaceSupportPlacementTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectSurfaceSupportPlacementTest.java @@ -8,7 +8,7 @@ import art.arcane.iris.generation.terrain.IrisSlopeClip; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.volmlib.util.stream.ProceduralStream; import art.arcane.volmlib.util.math.RNG; @@ -249,7 +249,7 @@ public class IrisObjectSurfaceSupportPlacementTest { } private IrisObject object() { - PlatformBlockState solid = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); when(solid.isSolid()).thenReturn(true); when(solid.key()).thenReturn("minecraft:stone"); when(solid.materialKey()).thenReturn("minecraft:stone"); @@ -261,7 +261,7 @@ public class IrisObjectSurfaceSupportPlacementTest { private static final class SurfacePlacer implements IObjectPlacer { private final Set carved = new HashSet<>(); - private final Map written = new HashMap<>(); + private final Map written = new HashMap<>(); private final Engine engine; private SurfacePlacer() { @@ -276,7 +276,7 @@ public class IrisObjectSurfaceSupportPlacementTest { carved.add(blockKey(x, y, z)); } - private Map written() { + private Map written() { return written; } @@ -291,12 +291,12 @@ public class IrisObjectSurfaceSupportPlacementTest { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { written.put(blockKey(x, y, z), state); } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return null; } diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsNearestBlockIndexTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsNearestBlockIndexTest.java index 978ad2676..e06a899be 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsNearestBlockIndexTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsNearestBlockIndexTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.structure.object; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.iris.generation.block.VectorMap; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -16,10 +16,10 @@ import static org.junit.Assert.assertSame; public class IrisObjectTransformsNearestBlockIndexTest { @Test public void spatialLookupMatchesLinearIterationIncludingDistanceTies() { - VectorMap blocks = new VectorMap<>(); + VectorMap blocks = new VectorMap<>(); Random random = new Random(812734L); for (int i = 0; i < 120; i++) { - PlatformBlockState state = new SolidBlockState(false); + NativeBlockState state = new SolidBlockState(false); blocks.put(new IrisBlockVector( random.nextInt(25) - 12, random.nextInt(25) - 12, @@ -27,11 +27,11 @@ public class IrisObjectTransformsNearestBlockIndexTest { ), state); } - PlatformBlockState explicitAir = new SolidBlockState(true); + NativeBlockState explicitAir = new SolidBlockState(true); blocks.put(new IrisBlockVector(0, 0, 0), explicitAir); IrisObjectTransforms.NearestBlockIndex index = IrisObjectTransforms.NearestBlockIndex.create(blocks); - List> candidates = new ArrayList<>(); - for (Map.Entry entry : blocks) { + List> candidates = new ArrayList<>(); + for (Map.Entry entry : blocks) { candidates.add(entry); } @@ -39,8 +39,8 @@ public class IrisObjectTransformsNearestBlockIndexTest { for (int y = -10; y <= 10; y++) { for (int z = -10; z <= 10; z++) { IrisBlockVector query = new IrisBlockVector(x, y, z); - PlatformBlockState direct = blocks.get(query); - PlatformBlockState actual = B.isAir(direct) ? index.nearest(x, y, z, direct) : direct; + NativeBlockState direct = blocks.get(query); + NativeBlockState actual = B.isAir(direct) ? index.nearest(x, y, z, direct) : direct; assertSame("Mismatch at " + x + "," + y + "," + z, nearestByLinearIteration(blocks, candidates, query), actual); } @@ -50,8 +50,8 @@ public class IrisObjectTransformsNearestBlockIndexTest { @Test public void emptyIndexRetainsMissingAndExplicitAirFallbacks() { - VectorMap blocks = new VectorMap<>(); - PlatformBlockState explicitAir = new SolidBlockState(true); + VectorMap blocks = new VectorMap<>(); + NativeBlockState explicitAir = new SolidBlockState(true); blocks.put(new IrisBlockVector(1, 2, 3), explicitAir); IrisObjectTransforms.NearestBlockIndex index = IrisObjectTransforms.NearestBlockIndex.create(blocks); @@ -59,19 +59,19 @@ public class IrisObjectTransformsNearestBlockIndexTest { assertSame(null, index.nearest(4, 5, 6, null)); } - private static PlatformBlockState nearestByLinearIteration( - VectorMap blocks, - List> candidates, + private static NativeBlockState nearestByLinearIteration( + VectorMap blocks, + List> candidates, IrisBlockVector query ) { - PlatformBlockState result = blocks.get(query); + NativeBlockState result = blocks.get(query); if (!B.isAir(result)) { return result; } double nearestDistance = Double.MAX_VALUE; - for (Map.Entry entry : candidates) { - PlatformBlockState state = entry.getValue(); + for (Map.Entry entry : candidates) { + NativeBlockState state = entry.getValue(); if (B.isAir(state)) { continue; } @@ -85,7 +85,7 @@ public class IrisObjectTransformsNearestBlockIndexTest { return result; } - private static final class SolidBlockState implements PlatformBlockState { + private static final class SolidBlockState implements NativeBlockState { private final boolean air; private SolidBlockState(boolean air) { @@ -133,7 +133,7 @@ public class IrisObjectTransformsNearestBlockIndexTest { } @Override - public PlatformBlockState placementBaseState() { + public NativeBlockState placementBaseState() { return null; } @@ -208,12 +208,12 @@ public class IrisObjectTransformsNearestBlockIndexTest { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return false; } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return false; } @@ -228,7 +228,7 @@ public class IrisObjectTransformsNearestBlockIndexTest { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { return null; } diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsTileTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsTileTest.java index ddf1b8036..8265738f4 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsTileTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransformsTileTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.generation.block.TileData; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -41,7 +41,7 @@ public class IrisObjectTransformsTileTest { public void bindPlatform() { IrisPlatforms.unbind(); PlatformRegistries registries = mock(PlatformRegistries.class); - Map states = new HashMap<>(); + Map states = new HashMap<>(); when(registries.block(anyString())).thenAnswer(invocation -> { String key = invocation.getArgument(0); return states.computeIfAbsent(key, IrisObjectTransformsTileTest::state); @@ -59,7 +59,7 @@ public class IrisObjectTransformsTileTest { @Test public void rotationOmitsUnsupportedBlocksAndTheirTiles() { IrisObject source = new IrisObject(3, 1, 1); - PlatformBlockState unsupported = state("minecraft:wall_torch"); + NativeBlockState unsupported = state("minecraft:wall_torch"); source.setUnsigned(0, 0, 0, unsupported); source.setUnsignedTile(0, 0, 0, tile("minecraft:wall_torch", "omitted")); source.setUnsigned(1, 0, 0, state("minecraft:chest")); @@ -102,7 +102,7 @@ public class IrisObjectTransformsTileTest { assertEquals(8, scaled.getBlocks().size()); assertEquals(8, scaled.getStates().size()); Set copies = Collections.newSetFromMap(new IdentityHashMap<>()); - for (Map.Entry block : scaled.getBlocks()) { + for (Map.Entry block : scaled.getBlocks()) { TileData copy = scaled.getStates().get(block.getKey()); assertNotNull(copy); assertNotSame(original, copy); @@ -125,7 +125,7 @@ public class IrisObjectTransformsTileTest { assertTrue(scaled.getBlocks().size() < source.getBlocks().size()); assertEquals(scaled.getBlocks().size(), scaled.getStates().size()); - for (Map.Entry block : scaled.getBlocks()) { + for (Map.Entry block : scaled.getBlocks()) { TileData copy = scaled.getStates().get(block.getKey()); assertNotNull(copy); assertEquals(block.getValue().materialKey(), copy.getMaterialKey()); @@ -148,7 +148,7 @@ public class IrisObjectTransformsTileTest { assertFalse(scaled.getStates().isEmpty()); for (Map.Entry tile : scaled.getStates()) { - PlatformBlockState block = scaled.getBlocks().get(tile.getKey()); + NativeBlockState block = scaled.getBlocks().get(tile.getKey()); assertNotNull(block); assertEquals(tile.getValue().getMaterialKey(), block.materialKey()); } @@ -247,7 +247,7 @@ public class IrisObjectTransformsTileTest { assertEquals(1, first.getStates().size()); assertEquals("minecraft:stone", first.getBlocks().get(new IrisBlockVector(-1, 0, 0)).materialKey()); assertEquals("winner", first.getStates().get(new IrisBlockVector(0, 0, 0)).getProperties().get("name")); - for (Map.Entry entry : first.getBlocks()) { + for (Map.Entry entry : first.getBlocks()) { assertEquals(entry.getValue().materialKey(), second.getBlocks().get(entry.getKey()).materialKey()); assertEquals(first.getStates().get(entry.getKey()), second.getStates().get(entry.getKey())); } @@ -316,7 +316,7 @@ public class IrisObjectTransformsTileTest { assertEquals(expected.getD(), actual.getD()); assertEquals(expected.getBlocks().size(), actual.getBlocks().size()); assertEquals(expected.getStates().size(), actual.getStates().size()); - for (Map.Entry entry : expected.getBlocks()) { + for (Map.Entry entry : expected.getBlocks()) { assertEquals(entry.getValue(), actual.getBlocks().get(entry.getKey())); assertEquals(expected.getStates().get(entry.getKey()), actual.getStates().get(entry.getKey())); } @@ -343,8 +343,8 @@ public class IrisObjectTransformsTileTest { return new TileData(material, properties); } - private static PlatformBlockState state(String key) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.materialKey()).thenReturn(key); when(state.isAir()).thenReturn(key.contains("air")); diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransientBlockTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransientBlockTest.java index e14512cf5..0725dcf85 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransientBlockTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisObjectTransientBlockTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.structure.object; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.generation.terrain.IrisDimension; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.volmlib.util.collection.KMap; @@ -85,7 +85,7 @@ public class IrisObjectTransientBlockTest { IrisObject object = placement.scaleObject(new RNG(1337), source, new IrisDimension().setAllObjectScaleFactor(factor)); assertEquals(factor == 2D ? 8 : 1, object.getStates().size()); - for (Map.Entry entry : object.getBlocks()) { + for (Map.Entry entry : object.getBlocks()) { assertFalse(entry.getValue().materialKey().equals("minecraft:moving_piston")); if (entry.getValue().materialKey().equals("minecraft:chest")) { assertNotNull(object.getStates().get(entry.getKey())); @@ -151,9 +151,9 @@ public class IrisObjectTransientBlockTest { output.writeShort(0); } - private static PlatformBlockState state(String key) { + private static NativeBlockState state(String key) { String material = key.substring(0, key.indexOf('[')); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.materialKey()).thenReturn(material); return state; diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisStaticObjectLayerTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisStaticObjectLayerTest.java index a18e349da..bd464df55 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisStaticObjectLayerTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisStaticObjectLayerTest.java @@ -10,7 +10,7 @@ import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.geometry.IrisBlockVector; @@ -49,7 +49,7 @@ public class IrisStaticObjectLayerTest { @ClassRule public static final PlatformLeakGuard PLATFORM_GUARD = PlatformLeakGuard.clean(); - private final Map states = new HashMap<>(); + private final Map states = new HashMap<>(); private IrisData data; private ResourceLoader loader; private IrisObjectRotation.StateRotator previousRotator; @@ -175,8 +175,8 @@ public class IrisStaticObjectLayerTest { when(mantle.getChunk(anyInt(), anyInt())).thenReturn(chunk); when(chunk.getOrCreate(anyInt())).thenReturn(section); when(section.getSlice(TileWrapper.class)).thenReturn(tiles); - Hunk left = Hunk.newArrayHunk(16, 384, 16); - Hunk right = Hunk.newArrayHunk(16, 384, 16); + Hunk left = Hunk.newArrayHunk(16, 384, 16); + Hunk right = Hunk.newArrayHunk(16, 384, 16); layer.apply(engine, 16, -16, right); layer.apply(engine, 0, -16, left); @@ -249,9 +249,9 @@ public class IrisStaticObjectLayerTest { .findFirst().orElseThrow(); } - private PlatformBlockState state(String key) { + private NativeBlockState state(String key) { return states.computeIfAbsent(key.toLowerCase(), value -> { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn(value); when(block.materialKey()).thenReturn(value); when(block.isAir()).thenReturn(value.contains("air")); diff --git a/core/src/test/java/art/arcane/iris/structure/object/IrisSurfaceSupportTest.java b/core/src/test/java/art/arcane/iris/structure/object/IrisSurfaceSupportTest.java index ad333880b..ca00ea69a 100644 --- a/core/src/test/java/art/arcane/iris/structure/object/IrisSurfaceSupportTest.java +++ b/core/src/test/java/art/arcane/iris/structure/object/IrisSurfaceSupportTest.java @@ -10,7 +10,7 @@ import art.arcane.iris.structure.object.IrisObjectTranslate; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import org.junit.Test; @@ -138,8 +138,8 @@ public class IrisSurfaceSupportTest { @Test public void objectCachesItsLowestNonFoliageSupportBlocks() { - PlatformBlockState solid = mock(PlatformBlockState.class); - PlatformBlockState foliage = mock(PlatformBlockState.class); + NativeBlockState solid = mock(NativeBlockState.class); + NativeBlockState foliage = mock(NativeBlockState.class); when(solid.isSolid()).thenReturn(true); when(foliage.isSolid()).thenReturn(true); when(foliage.isFoliage()).thenReturn(true); @@ -224,11 +224,11 @@ public class IrisSurfaceSupportTest { } @Override - public void set(int x, int y, int z, PlatformBlockState state) { + public void set(int x, int y, int z, NativeBlockState state) { } @Override - public PlatformBlockState get(int x, int y, int z) { + public NativeBlockState get(int x, int y, int z) { return null; } diff --git a/core/src/test/java/art/arcane/iris/structure/placement/IrisStructureLocatorContractTest.java b/core/src/test/java/art/arcane/iris/structure/placement/IrisStructureLocatorContractTest.java index a16fc2469..25bd9afea 100644 --- a/core/src/test/java/art/arcane/iris/structure/placement/IrisStructureLocatorContractTest.java +++ b/core/src/test/java/art/arcane/iris/structure/placement/IrisStructureLocatorContractTest.java @@ -18,6 +18,10 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.util.structure.StructureTerrainMode; + +import art.arcane.volmlib.util.structure.StructureCarveShape; + import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.runtime.SeedManager; @@ -657,10 +661,10 @@ public class IrisStructureLocatorContractTest { placement.setMinHeight(-64); placement.setMaxHeight(100); IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) + .setMode(StructureTerrainMode.FORCE_CARVE) .setHorizontalPadding(2) .setCeilingPadding(20) - .setShape(IrisStructureCarveShape.ERODED); + .setShape(StructureCarveShape.ERODED); placement.setTerrain(terrain); KList pieces = new KList<>(); pieces.add(piece(0, 60, 0, 1, 80, 1)); @@ -672,7 +676,7 @@ public class IrisStructureLocatorContractTest { assertEquals(Integer.valueOf(-1), IrisStructureLocator.resolveUndergroundBurialShift( engine, pieces, placement, 60, -63, 319)); - terrain.setShape(IrisStructureCarveShape.ROUNDED); + terrain.setShape(StructureCarveShape.ROUNDED); assertEquals(Integer.valueOf(-1), IrisStructureLocator.resolveUndergroundBurialShift( engine, pieces, placement, 60, -63, 319)); @@ -691,8 +695,8 @@ public class IrisStructureLocatorContractTest { .setMinHeight(-64) .setMaxHeight(100) .setTerrain(new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.FORCE_CARVE) - .setShape(IrisStructureCarveShape.ROUNDED) + .setMode(StructureTerrainMode.FORCE_CARVE) + .setShape(StructureCarveShape.ROUNDED) .setHorizontalPadding(0) .setCeilingPadding(0)); KList pieces = new KList<>(); diff --git a/core/src/test/java/art/arcane/iris/structure/placement/IrisStructurePlacementCarveSettingsTest.java b/core/src/test/java/art/arcane/iris/structure/placement/IrisStructurePlacementCarveSettingsTest.java index 55a31acf5..09f05bf3f 100644 --- a/core/src/test/java/art/arcane/iris/structure/placement/IrisStructurePlacementCarveSettingsTest.java +++ b/core/src/test/java/art/arcane/iris/structure/placement/IrisStructurePlacementCarveSettingsTest.java @@ -1,5 +1,9 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.util.structure.StructureTerrainMode; + +import art.arcane.volmlib.util.structure.StructureCarveShape; + import art.arcane.iris.generation.terrain.IrisMaterialPalette; import art.arcane.iris.pack.schema.annotation.MaxNumber; @@ -18,9 +22,9 @@ public class IrisStructurePlacementCarveSettingsTest { public void defaultsToSourceTerrainWithBoxCarving() { IrisStructureTerrain terrain = new IrisStructureTerrain(); - assertEquals(IrisStructureTerrainMode.SOURCE, terrain.resolvedMode()); - assertEquals(IrisStructureCarveShape.BOX, terrain.getShape()); - assertEquals(IrisStructureCarveShape.BOX, terrain.resolvedShape()); + assertEquals(StructureTerrainMode.SOURCE, terrain.resolvedMode()); + assertEquals(StructureCarveShape.BOX, terrain.getShape()); + assertEquals(StructureCarveShape.BOX, terrain.resolvedShape()); assertEquals(0.8D, terrain.getErosionStrength(), 0D); assertEquals(0.8D, terrain.resolvedErosionStrength(), 0D); assertEquals(0.07D, terrain.getErosionFrequency(), 0D); @@ -68,15 +72,15 @@ public class IrisStructurePlacementCarveSettingsTest { @Test public void carveShapesExposeTheirMaximumCeilingScale() { - assertEquals(1D, IrisStructureCarveShape.BOX.maximumCeilingScale(), 0D); - assertEquals(1D, IrisStructureCarveShape.ROUNDED.maximumCeilingScale(), 0D); - assertEquals(1.8D, IrisStructureCarveShape.ERODED.maximumCeilingScale(), 0D); - assertEquals(10, IrisStructureCarveShape.BOX.maximumCeilingExtension(10)); - assertEquals(10, IrisStructureCarveShape.ROUNDED.maximumCeilingExtension(10)); - assertEquals(18, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10)); - assertEquals(10, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10, 0D)); - assertEquals(14, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10, 0.5D)); - assertEquals(0, IrisStructureCarveShape.ERODED.maximumCeilingExtension(0)); + assertEquals(1D, StructureCarveShape.BOX.maximumCeilingScale(), 0D); + assertEquals(1D, StructureCarveShape.ROUNDED.maximumCeilingScale(), 0D); + assertEquals(1.8D, StructureCarveShape.ERODED.maximumCeilingScale(), 0D); + assertEquals(10, StructureCarveShape.BOX.maximumCeilingExtension(10)); + assertEquals(10, StructureCarveShape.ROUNDED.maximumCeilingExtension(10)); + assertEquals(18, StructureCarveShape.ERODED.maximumCeilingExtension(10)); + assertEquals(10, StructureCarveShape.ERODED.maximumCeilingExtension(10, 0D)); + assertEquals(14, StructureCarveShape.ERODED.maximumCeilingExtension(10, 0.5D)); + assertEquals(0, StructureCarveShape.ERODED.maximumCeilingExtension(0)); } @Test @@ -86,7 +90,7 @@ public class IrisStructurePlacementCarveSettingsTest { .setErosionStrength(Double.NaN) .setErosionFrequency(Double.POSITIVE_INFINITY); - assertEquals(IrisStructureCarveShape.BOX, terrain.resolvedShape()); + assertEquals(StructureCarveShape.BOX, terrain.resolvedShape()); assertEquals(0.8D, terrain.resolvedErosionStrength(), 0D); assertEquals(0.07D, terrain.resolvedErosionFrequency(), 0D); @@ -116,9 +120,9 @@ public class IrisStructurePlacementCarveSettingsTest { @Test public void encaseModeCarriesAnOptionalPaletteOverride() { IrisStructureTerrain terrain = new IrisStructureTerrain() - .setMode(IrisStructureTerrainMode.ENCASE); + .setMode(StructureTerrainMode.ENCASE); - assertEquals(IrisStructureTerrainMode.ENCASE, terrain.resolvedMode()); + assertEquals(StructureTerrainMode.ENCASE, terrain.resolvedMode()); assertNull(terrain.getEncasePalette()); IrisMaterialPalette palette = new IrisMaterialPalette().qclear().qadd("minecraft:tuff"); diff --git a/core/src/test/java/art/arcane/iris/structure/placement/StructureReachabilityContractTest.java b/core/src/test/java/art/arcane/iris/structure/placement/StructureReachabilityContractTest.java index 4b89777d1..552b905b9 100644 --- a/core/src/test/java/art/arcane/iris/structure/placement/StructureReachabilityContractTest.java +++ b/core/src/test/java/art/arcane/iris/structure/placement/StructureReachabilityContractTest.java @@ -25,7 +25,7 @@ import art.arcane.iris.world.IrisWorld; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.testsupport.PlatformLeakGuard; import org.junit.ClassRule; import org.junit.Test; @@ -104,7 +104,7 @@ public class StructureReachabilityContractTest { public void failedReachabilityBuildIsNotCachedAsEmpty() { Engine engine = mock(Engine.class); IrisWorld world = mock(IrisWorld.class); - PlatformWorld platformWorld = mock(PlatformWorld.class); + NativeWorld platformWorld = mock(NativeWorld.class); IrisPlatform platform = mock(IrisPlatform.class); PlatformStructureHooks hooks = mock(PlatformStructureHooks.class); IllegalStateException cause = new IllegalStateException("registry unavailable"); @@ -131,7 +131,7 @@ public class StructureReachabilityContractTest { public void missingBiomeKeysForwardsCanonicalStructureKey() { Engine engine = mock(Engine.class); IrisWorld world = mock(IrisWorld.class); - PlatformWorld platformWorld = mock(PlatformWorld.class); + NativeWorld platformWorld = mock(NativeWorld.class); IrisPlatform platform = mock(IrisPlatform.class); PlatformStructureHooks hooks = mock(PlatformStructureHooks.class); when(engine.getWorld()).thenReturn(world); diff --git a/core/src/test/java/art/arcane/iris/structure/placement/StructureVerticalBoundsTest.java b/core/src/test/java/art/arcane/iris/structure/placement/StructureVerticalBoundsTest.java index f9b45184b..9ed476c7d 100644 --- a/core/src/test/java/art/arcane/iris/structure/placement/StructureVerticalBoundsTest.java +++ b/core/src/test/java/art/arcane/iris/structure/placement/StructureVerticalBoundsTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.structure.placement; +import art.arcane.volmlib.util.structure.StructureVerticalBounds; + import org.junit.Test; import static org.junit.Assert.assertEquals; diff --git a/core/src/test/java/art/arcane/iris/studio/generation/JigsawStudioGeneratorTest.java b/core/src/test/java/art/arcane/iris/studio/generation/JigsawStudioGeneratorTest.java index 322c2af1d..95cb8b3de 100644 --- a/core/src/test/java/art/arcane/iris/studio/generation/JigsawStudioGeneratorTest.java +++ b/core/src/test/java/art/arcane/iris/studio/generation/JigsawStudioGeneratorTest.java @@ -26,8 +26,8 @@ import art.arcane.iris.structure.object.IrisObjectRotation; import art.arcane.iris.pack.value.IrisPosition; import art.arcane.iris.structure.jigsaw.JigsawJoint; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.collection.KList; import art.arcane.volmlib.util.collection.KMap; @@ -88,7 +88,7 @@ public class JigsawStudioGeneratorTest { .setObject("test/room") .setConnectors(new KList<>()); piece.getConnectors().add(connector); - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); IrisObject object = new IrisObject(3, 3, 3); object.setUnsigned(1, 1, 1, stone); when(pieceLoader.load("test/room", false)).thenReturn(piece); @@ -119,7 +119,7 @@ public class JigsawStudioGeneratorTest { workcell.stableId()).connectorsVisible()); assertSame(stone, stateAt(fixture.generator(), worldX, worldY, worldZ)); - PlatformBlockState marker = mock(PlatformBlockState.class); + NativeBlockState marker = mock(NativeBlockState.class); fixture.generator().getSession().setConnectorsVisible(workcell.stableId(), true); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState("minecraft:jigsaw[orientation=north_up]")).thenReturn(marker); @@ -359,7 +359,7 @@ public class JigsawStudioGeneratorTest { .setConnectors(new KList<>()); piece.getConnectors().add(connector); IrisObject object = new IrisObject(3, 3, 3); - PlatformBlockState sourceBlock = mock(PlatformBlockState.class); + NativeBlockState sourceBlock = mock(NativeBlockState.class); when(sourceBlock.nativeHandle()).thenReturn(mock(BlockData.class)); KMap tileProperties = new KMap<>(); tileProperties.put("CustomName", "QA Chest"); @@ -477,7 +477,7 @@ public class JigsawStudioGeneratorTest { IrisDirection.DOWN_NEGATIVE_Y, IrisDirection.WEST_NEGATIVE_X)); } - private static PlatformBlockState stateAt( + private static NativeBlockState stateAt( JigsawStudioGenerator generator, int worldX, int worldY, @@ -532,14 +532,14 @@ public class JigsawStudioGeneratorTest { "overworld", "test/structure", layout); - PlatformBlockState lightFloor = mock(PlatformBlockState.class); - PlatformBlockState darkFloor = mock(PlatformBlockState.class); - PlatformBlockState frame = mock(PlatformBlockState.class); - PlatformBlockState topologyBase = mock(PlatformBlockState.class); - PlatformBlockState topologyPath = mock(PlatformBlockState.class); - PlatformBlockState connectorCap = mock(PlatformBlockState.class); - PlatformBlockState invalidMarker = mock(PlatformBlockState.class); - PlatformBlockState controlChest = mock(PlatformBlockState.class); + NativeBlockState lightFloor = mock(NativeBlockState.class); + NativeBlockState darkFloor = mock(NativeBlockState.class); + NativeBlockState frame = mock(NativeBlockState.class); + NativeBlockState topologyBase = mock(NativeBlockState.class); + NativeBlockState topologyPath = mock(NativeBlockState.class); + NativeBlockState connectorCap = mock(NativeBlockState.class); + NativeBlockState invalidMarker = mock(NativeBlockState.class); + NativeBlockState controlChest = mock(NativeBlockState.class); JigsawStudioGenerator generator = new JigsawStudioGenerator( engine, request, @@ -595,18 +595,18 @@ public class JigsawStudioGeneratorTest { private record GeneratorFixture( JigsawStudioGenerator generator, JigsawStudioLayout layout, - PlatformBlockState frame, - PlatformBlockState topologyBase, - PlatformBlockState topologyPath, - PlatformBlockState connectorCap, - PlatformBlockState controlChest + NativeBlockState frame, + NativeBlockState topologyBase, + NativeBlockState topologyPath, + NativeBlockState connectorCap, + NativeBlockState controlChest ) { } private static final class RecordingTerrainChunk implements TerrainChunk { private final int minHeight; private final int maxHeight; - private final Map blocks = new HashMap<>(); + private final Map blocks = new HashMap<>(); private RecordingTerrainChunk(int minHeight, int maxHeight) { this.minHeight = minHeight; @@ -614,12 +614,12 @@ public class JigsawStudioGeneratorTest { } @Override - public PlatformBiome getBiome(int x, int y, int z) { + public NativeBiome getBiome(int x, int y, int z) { return null; } @Override - public void setBiome(int x, int y, int z, PlatformBiome biome) { + public void setBiome(int x, int y, int z, NativeBiome biome) { } @Override @@ -633,7 +633,7 @@ public class JigsawStudioGeneratorTest { } @Override - public void setBlock(int x, int y, int z, PlatformBlockState blockData) { + public void setBlock(int x, int y, int z, NativeBlockState blockData) { blocks.put(key(x, y, z), blockData); } @@ -645,7 +645,7 @@ public class JigsawStudioGeneratorTest { int xMax, int yMax, int zMax, - PlatformBlockState blockData + NativeBlockState blockData ) { for (int x = xMin; x < xMax; x++) { for (int y = yMin; y < yMax; y++) { @@ -657,7 +657,7 @@ public class JigsawStudioGeneratorTest { } @Override - public PlatformBlockState getBlockData(int x, int y, int z) { + public NativeBlockState getBlockData(int x, int y, int z) { return blocks.get(key(x, y, z)); } @@ -666,9 +666,9 @@ public class JigsawStudioGeneratorTest { return null; } - private int countState(PlatformBlockState state) { + private int countState(NativeBlockState state) { int count = 0; - for (PlatformBlockState block : blocks.values()) { + for (NativeBlockState block : blocks.values()) { if (block == state) { count++; } diff --git a/core/src/test/java/art/arcane/iris/studio/generation/ObjectStudioGeometryTest.java b/core/src/test/java/art/arcane/iris/studio/generation/ObjectStudioGeometryTest.java index fd1a5cd36..46960259e 100644 --- a/core/src/test/java/art/arcane/iris/studio/generation/ObjectStudioGeometryTest.java +++ b/core/src/test/java/art/arcane/iris/studio/generation/ObjectStudioGeometryTest.java @@ -11,7 +11,7 @@ import art.arcane.iris.generation.runtime.GenerationSessionLease; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.world.IrisWorld; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.geometry.IrisBlockVector; import art.arcane.iris.world.storage.matter.PreObjectMatterTest; import art.arcane.volmlib.util.collection.KMap; @@ -109,7 +109,7 @@ public class ObjectStudioGeometryTest { IrisObject source = filled(5, 3, 4, new Position(-3, -2, -2), new Position(1, -1, 1)); Fixture fixture = fixture(source, 64, new Position(14, 65, 14)); - Map rendered = render(fixture); + Map rendered = render(fixture); assertPlacement(fixture, rendered, new Position(-3, -2, -2)); assertEquals(40, source.getBlocks().size()); @@ -130,11 +130,11 @@ public class ObjectStudioGeometryTest { TileData tile = new TileData("minecraft:chest", properties); source.getStates().put(new IrisBlockVector(minimum.x(), minimum.y(), minimum.z()), tile); Fixture fixture = fixture(source, 64, new Position(-18, 65, -17)); - Map rendered = render(fixture); + Map rendered = render(fixture); assertPlacement(fixture, rendered, minimum); IrisObject captured = fixture.generator().createCapture(fixture.cell()); - for (Map.Entry entry : rendered.entrySet()) { + for (Map.Entry entry : rendered.entrySet()) { Position position = entry.getKey(); captured.setUnsigned(position.x() - fixture.cell().originX(), position.y() - fixture.cell().originY(), position.z() - fixture.cell().originZ(), @@ -160,7 +160,7 @@ public class ObjectStudioGeometryTest { IrisObject source = filled(5, 3, 4, new Position(-3, -2, -2), new Position(1, -1, 1)); Fixture fixture = fixture(source, 160, new Position(2, 65, 2)); - Map rendered = render(fixture); + Map rendered = render(fixture); assertEquals(161, fixture.cell().originY()); assertPlacement(fixture, rendered, new Position(-3, -2, -2)); @@ -168,7 +168,7 @@ public class ObjectStudioGeometryTest { } private static IrisObject filled(int width, int height, int depth, Position minimum, Position maximum) { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn("minecraft:oak_leaves[persistent=true]"); IrisObject object = new IrisObject(width, height, depth); for (int x = minimum.x(); x <= maximum.x(); x++) { @@ -215,7 +215,7 @@ public class ObjectStudioGeometryTest { when(engine.getMaxHeight()).thenReturn(320); when(engine.acquireGenerationLease(anyString())).thenAnswer(invocation -> GenerationSessionLease.noop()); ObjectStudioGenerator generator = new ObjectStudioGenerator(engine, 2, - mock(PlatformBlockState.class), mock(PlatformBlockState.class), mock(PlatformBlockState.class)); + mock(NativeBlockState.class), mock(NativeBlockState.class), mock(NativeBlockState.class)); TerrainChunk initial = mock(TerrainChunk.class); when(initial.getMaxHeight()).thenReturn(320); Map tiles = new HashMap<>(); @@ -238,9 +238,9 @@ public class ObjectStudioGeometryTest { return new Fixture(generator, engine, placed, source, tiles, service); } - private static Map render(Fixture fixture) throws Exception { - Map rendered = new HashMap<>(); - PlatformBlockState objectBlock = fixture.source().getBlocks().values().iterator().next(); + private static Map render(Fixture fixture) throws Exception { + Map rendered = new HashMap<>(); + NativeBlockState objectBlock = fixture.source().getBlocks().values().iterator().next(); for (int chunkX = fixture.cell().chunkMinX(); chunkX <= fixture.cell().chunkMaxX(); chunkX++) { for (int chunkZ = fixture.cell().chunkMinZ(); chunkZ <= fixture.cell().chunkMaxZ(); chunkZ++) { int originX = chunkX << 4; @@ -252,7 +252,7 @@ public class ObjectStudioGeometryTest { int y = invocation.getArgument(1); int z = invocation.getArgument(2); assertTrue(x >= 0 && x < 16 && z >= 0 && z < 16); - PlatformBlockState block = invocation.getArgument(3); + NativeBlockState block = invocation.getArgument(3); if (block == objectBlock) { rendered.put(new Position(originX + x, y, originZ + z), block); } @@ -267,9 +267,9 @@ public class ObjectStudioGeometryTest { return rendered; } - private static void assertPlacement(Fixture fixture, Map rendered, Position minimum) { + private static void assertPlacement(Fixture fixture, Map rendered, Position minimum) { assertEquals(fixture.source().getBlocks().size(), rendered.size()); - for (Map.Entry entry : fixture.source().getBlocks()) { + for (Map.Entry entry : fixture.source().getBlocks()) { IrisBlockVector signed = entry.getKey(); Position expected = new Position(fixture.cell().originX() + signed.getBlockX() - minimum.x(), fixture.cell().originY() + signed.getBlockY() - minimum.y(), diff --git a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioGraphEditorResizeTest.java b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioGraphEditorResizeTest.java index f376b597f..ba5c5832e 100644 --- a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioGraphEditorResizeTest.java +++ b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioGraphEditorResizeTest.java @@ -5,7 +5,7 @@ import art.arcane.iris.structure.jigsaw.IrisJigsawConnector; import art.arcane.iris.structure.jigsaw.IrisJigsawPiece; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.pack.value.IrisPosition; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.DurabilityMode; import art.arcane.iris.generation.geometry.IrisBlockVector; import com.google.gson.JsonArray; @@ -141,8 +141,8 @@ public class JigsawStudioGraphEditorResizeTest { @Test public void preservesCanonicalCoordinatesForRotatedVariantsAndMovesExplicitAirUnderlays() throws Exception { IrisObject source = new IrisObject(7, 3, 5); - PlatformBlockState explicitAir = state("minecraft:air", true); - PlatformBlockState stone = state("minecraft:stone", false); + NativeBlockState explicitAir = state("minecraft:air", true); + NativeBlockState stone = state("minecraft:stone", false); source.setUnsigned(6, 1, 2, explicitAir); source.setUnsigned(4, 1, 1, stone); IrisJigsawPiece piece = endPiece(IrisDirection.EAST_POSITIVE_X, new IrisPosition(6, 1, 2)); @@ -169,7 +169,7 @@ public class JigsawStudioGraphEditorResizeTest { @Test public void rejectsExplicitAirOutsideShrunkBoundsWithoutMutatingTheSource() { IrisObject source = new IrisObject(8, 4, 8); - PlatformBlockState explicitAir = state("minecraft:air", true); + NativeBlockState explicitAir = state("minecraft:air", true); source.setUnsigned(7, 1, 1, explicitAir); IrisJigsawPiece piece = new IrisJigsawPiece().setObject("resize/blank"); @@ -190,8 +190,8 @@ public class JigsawStudioGraphEditorResizeTest { @Test public void rejectsConnectorDestinationCollisionsBeforeChangingThePiece() { IrisObject source = new IrisObject(5, 3, 5); - PlatformBlockState underlay = state("minecraft:air", true); - PlatformBlockState collision = state("minecraft:stone", false); + NativeBlockState underlay = state("minecraft:air", true); + NativeBlockState collision = state("minecraft:stone", false); source.setUnsigned(2, 1, 0, underlay); source.setUnsigned(3, 1, 0, collision); IrisPosition originalPosition = new IrisPosition(2, 1, 0); @@ -250,14 +250,14 @@ public class JigsawStudioGraphEditorResizeTest { return sources; } - private static PlatformBlockState state(String key, boolean air) { - PlatformBlockState state = mock(PlatformBlockState.class); + private static NativeBlockState state(String key, boolean air) { + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(key); when(state.isAir()).thenReturn(air); return state; } - private static PlatformBlockState blockAt(IrisObject object, int x, int y, int z) { + private static NativeBlockState blockAt(IrisObject object, int x, int y, int z) { return object.getBlocks().get(object.getSigned(x, y, z)); } diff --git a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRendererTest.java b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRendererTest.java index d7d0ff656..cbacb2ff9 100644 --- a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRendererTest.java +++ b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioPreviewRendererTest.java @@ -4,7 +4,7 @@ import art.arcane.iris.structure.placement.PlacedStructurePiece; import art.arcane.iris.structure.jigsaw.IrisJigsawPiece; import art.arcane.iris.structure.object.IrisObject; import art.arcane.iris.structure.object.IrisObjectRotation; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.bukkit.World; import org.junit.Test; @@ -25,7 +25,7 @@ import static org.mockito.Mockito.when; public class JigsawStudioPreviewRendererTest { @Test public void planUsesPlacedPieceCenterAndExactBlockState() throws Exception { - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); IrisObject object = new IrisObject(3, 3, 3); object.setUnsigned(2, 1, 0, stone); @@ -45,9 +45,9 @@ public class JigsawStudioPreviewRendererTest { @Test public void structureVoidRemovesAPreviouslyAuthoredPreviewCell() throws Exception { - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); - PlatformBlockState structureVoid = mock(PlatformBlockState.class); + NativeBlockState structureVoid = mock(NativeBlockState.class); when(structureVoid.key()).thenReturn("minecraft:structure_void"); IrisObject object = new IrisObject(1, 1, 1); object.setUnsigned(0, 0, 0, stone); diff --git a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioServiceCaptureTest.java b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioServiceCaptureTest.java index 9e73a1423..7da7bcf29 100644 --- a/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioServiceCaptureTest.java +++ b/core/src/test/java/art/arcane/iris/studio/jigsaw/JigsawStudioServiceCaptureTest.java @@ -18,7 +18,7 @@ import art.arcane.iris.platform.bukkit.BukkitPlatform; import art.arcane.iris.spi.IrisLogging; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.iris.generation.block.B; @@ -157,7 +157,7 @@ public class JigsawStudioServiceCaptureTest { World world = mock(World.class); Block target = mock(Block.class); BlockData blockData = mock(BlockData.class); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); JigsawStudioCellDimensions dimensions = new JigsawStudioCellDimensions(15, 15, 15); JigsawStudioBay workcell = new JigsawStudioBay( "spatial", @@ -808,10 +808,10 @@ public class JigsawStudioServiceCaptureTest { 0, new JigsawStudioCellDimensions(3, 1, 3)); List areas = JigsawStudioCapture.chunkIntersections(bounds); - PlatformBlockState sourceState = BukkitBlockState.of(directionalBlockData(BlockFace.NORTH)); + NativeBlockState sourceState = BukkitBlockState.of(directionalBlockData(BlockFace.NORTH)); for (int quarterTurns = 1; quarterTurns <= 3; quarterTurns++) { IrisObjectRotation displayRotation = IrisObjectRotation.of(0, -90.0D * quarterTurns, 0); - PlatformBlockState displayedState = displayRotation.rotate(sourceState, 0, 0, 0); + NativeBlockState displayedState = displayRotation.rotate(sourceState, 0, 0, 0); IrisDirection displayedDirection = displayRotation.rotate(IrisDirection.NORTH_NEGATIVE_Z); JigsawStudioCapture.CapturedConnector connector = new JigsawStudioCapture.CapturedConnector( 1, @@ -861,7 +861,7 @@ public class JigsawStudioServiceCaptureTest { IrisJigsawPiece piece = new IrisJigsawPiece().setConnectors(new KList<>()); piece.getConnectors().add(connector); BlockData chestData = directionalBlockData(Material.CHEST, BlockFace.EAST); - PlatformBlockState chestState = BukkitBlockState.of(chestData); + NativeBlockState chestState = BukkitBlockState.of(chestData); IrisObject sourceObject = new IrisObject(1, 1, 1); sourceObject.setUnsigned(0, 0, 0, chestState); KMap properties = new KMap<>(); @@ -1047,12 +1047,12 @@ public class JigsawStudioServiceCaptureTest { IrisJigsawPiece source = new IrisJigsawPiece().setConnectors(new KList<>()); source.getConnectors().add(sourceFirst); source.getConnectors().add(sourceSecond); - PlatformBlockState sourceState = BukkitBlockState.of( + NativeBlockState sourceState = BukkitBlockState.of( directionalBlockData(Material.OBSERVER, BlockFace.NORTH)); for (int quarterTurns = 1; quarterTurns <= 3; quarterTurns++) { IrisObjectRotation displayRotation = IrisObjectRotation.of(0, -90.0D * quarterTurns, 0); - PlatformBlockState displayedState = displayRotation.rotate(sourceState, 0, 0, 0); + NativeBlockState displayedState = displayRotation.rotate(sourceState, 0, 0, 0); JigsawStudioCapture.CapturedConnector displayedFirst = displayedConnector( sourceFirst, bounds.dimensions(), @@ -1107,7 +1107,7 @@ public class JigsawStudioServiceCaptureTest { }; for (int quarterTurns = 1; quarterTurns <= 3; quarterTurns++) { BlockData displayedData = directionalBlockData(Material.CHEST, BlockFace.EAST); - PlatformBlockState displayedState = BukkitBlockState.of(displayedData); + NativeBlockState displayedState = BukkitBlockState.of(displayedData); JigsawStudioCapture.CapturedBlock capturedBlock = new JigsawStudioCapture.CapturedBlock( 1, 1, @@ -1140,7 +1140,7 @@ public class JigsawStudioServiceCaptureTest { KMap properties = new KMap<>(); properties.put("CustomName", "QA Chest"); TileData tileData = new TileData("minecraft:chest", properties); - PlatformBlockState state = BukkitBlockState.of( + NativeBlockState state = BukkitBlockState.of( directionalBlockData(Material.CHEST, BlockFace.NORTH)); JigsawStudioGenerator.RenderedBay rendered = JigsawStudioGenerator.RenderedBay.valid( new JigsawStudioCellDimensions(3, 2, 5), @@ -1173,12 +1173,12 @@ public class JigsawStudioServiceCaptureTest { IrisObject source = new IrisObject(2, 2, 2); BlockData air = blockData(Material.AIR, "minecraft:air"); BlockData structureVoid = blockData(Material.STRUCTURE_VOID, "minecraft:structure_void"); - PlatformBlockState sourceAir = mock(PlatformBlockState.class); + NativeBlockState sourceAir = mock(NativeBlockState.class); when(sourceAir.isAir()).thenReturn(true); when(sourceAir.key()).thenReturn("minecraft:air"); source.setUnsigned(0, 0, 0, sourceAir); - PlatformBlockState retained = JigsawStudioCapture.retainedSourceAir(source, 0, 0, 0, air); + NativeBlockState retained = JigsawStudioCapture.retainedSourceAir(source, 0, 0, 0, air); assertNotNull(retained); assertNull(JigsawStudioCapture.retainedSourceAir(source, 1, 0, 0, air)); assertNull(JigsawStudioCapture.retainedSourceAir(source, 0, 0, 0, structureVoid)); @@ -1191,7 +1191,7 @@ public class JigsawStudioServiceCaptureTest { private static void assertAirRoundTrip(IrisObject object) throws Throwable { ByteArrayOutputStream output = new ByteArrayOutputStream(); object.write(output); - PlatformBlockState restoredAir = mock(PlatformBlockState.class); + NativeBlockState restoredAir = mock(NativeBlockState.class); when(restoredAir.key()).thenReturn("minecraft:air"); PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenReturn(restoredAir); @@ -1384,7 +1384,7 @@ public class JigsawStudioServiceCaptureTest { 0, new JigsawStudioCellDimensions(20, 2, 1)); List areas = JigsawStudioCapture.chunkIntersections(bounds); - PlatformBlockState stone = mock(PlatformBlockState.class); + NativeBlockState stone = mock(NativeBlockState.class); when(stone.key()).thenReturn("minecraft:stone"); JigsawStudioCapture.ChunkSnapshot first = new JigsawStudioCapture.ChunkSnapshot( areas.getFirst(), @@ -1586,7 +1586,7 @@ public class JigsawStudioServiceCaptureTest { private static IrisObject readCapturedObject( byte[] content, - PlatformBlockState restoredState + NativeBlockState restoredState ) throws Throwable { PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenReturn(restoredState); diff --git a/core/src/test/java/art/arcane/iris/studio/tree/TreePlausibilizerTest.java b/core/src/test/java/art/arcane/iris/studio/tree/TreePlausibilizerTest.java index bd9d366f2..55fbe5194 100644 --- a/core/src/test/java/art/arcane/iris/studio/tree/TreePlausibilizerTest.java +++ b/core/src/test/java/art/arcane/iris/studio/tree/TreePlausibilizerTest.java @@ -1,7 +1,7 @@ package art.arcane.iris.studio.tree; import art.arcane.iris.structure.object.IrisObject; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformBinding; import art.arcane.iris.generation.geometry.IrisBlockVector; import org.junit.ClassRule; @@ -39,7 +39,7 @@ public class TreePlausibilizerTest { private record Pos(int x, int y, int z) { } - private static final class TestState implements PlatformBlockState { + private static final class TestState implements NativeBlockState { private final String key; private TestState(String raw) { @@ -95,7 +95,7 @@ public class TreePlausibilizerTest { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { int bracket = key.indexOf('['); String material = bracket < 0 ? key : key.substring(0, bracket); TreeMap props = new TreeMap<>(); @@ -119,12 +119,12 @@ public class TreePlausibilizerTest { } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return state != null && key.equals(state.key()); } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return true; } @@ -260,7 +260,7 @@ public class TreePlausibilizerTest { private static Map snapshot(IrisObject obj) { Map out = new HashMap<>(); - for (Map.Entry e : obj.getBlocks()) { + for (Map.Entry e : obj.getBlocks()) { IrisBlockVector v = e.getKey(); out.put(new Pos(v.getBlockX(), v.getBlockY(), v.getBlockZ()), e.getValue().key()); } diff --git a/core/src/test/java/art/arcane/iris/studio/workspace/ExternalBlockSchemaTest.java b/core/src/test/java/art/arcane/iris/studio/workspace/ExternalBlockSchemaTest.java index 5c1683dc1..8b224958c 100644 --- a/core/src/test/java/art/arcane/iris/studio/workspace/ExternalBlockSchemaTest.java +++ b/core/src/test/java/art/arcane/iris/studio/workspace/ExternalBlockSchemaTest.java @@ -7,7 +7,7 @@ import art.arcane.iris.pack.loading.ResourceLoader; import art.arcane.iris.generation.block.IrisBlockData; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.json.JSONObject; @@ -38,7 +38,7 @@ public class ExternalBlockSchemaTest { PlatformRegistries registries = mock(PlatformRegistries.class); when(registries.blockTypeKeys()).thenReturn(List.of(QUALIFIED_BLOCK)); when(registries.blockStateProperties()).thenReturn(Map.of(QUALIFIED_BLOCK, - List.of(new PlatformBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null)))); + List.of(new NativeBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null)))); IrisPlatform platform = mock(IrisPlatform.class); when(platform.registries()).thenReturn(registries); IrisPlatforms.bind(platform); diff --git a/core/src/test/java/art/arcane/iris/testsupport/CompatTestPlatform.java b/core/src/test/java/art/arcane/iris/testsupport/CompatTestPlatform.java index b327a309c..a12d2cea2 100644 --- a/core/src/test/java/art/arcane/iris/testsupport/CompatTestPlatform.java +++ b/core/src/test/java/art/arcane/iris/testsupport/CompatTestPlatform.java @@ -23,11 +23,11 @@ import art.arcane.iris.generation.runtime.PreservationRegistry; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; import art.arcane.iris.spi.PlatformRegistries; import org.mockito.Answers; @@ -155,43 +155,43 @@ public final class CompatTestPlatform { } @Override - public PlatformBlockState block(String key) { - PlatformBlockState state = blockOrNull(key, false); + public NativeBlockState block(String key) { + NativeBlockState state = blockOrNull(key, false); return state == null ? air() : state; } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return blockOrNull(key, false); } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { return has(key) ? StateCache.of(base(key)) : null; } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return StateCache.of("minecraft:air"); } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return ore; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return null; } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return null; } @@ -241,7 +241,7 @@ public final class CompatTestPlatform { } @Override - public Map> blockStateProperties() { + public Map> blockStateProperties() { return Map.of(); } } @@ -252,15 +252,15 @@ public final class CompatTestPlatform { * handed out here can outlive the test that bound this platform. */ private static final class StateCache { - private static final Map STATES = new LinkedHashMap<>(); + private static final Map STATES = new LinkedHashMap<>(); private StateCache() { } - static synchronized PlatformBlockState of(String key) { + static synchronized NativeBlockState of(String key) { return STATES.computeIfAbsent(key, k -> { boolean air = k.endsWith("air"); - PlatformBlockState state = mock(PlatformBlockState.class); + NativeBlockState state = mock(NativeBlockState.class); when(state.key()).thenReturn(k); when(state.materialKey()).thenReturn(k); when(state.isAir()).thenReturn(air); diff --git a/core/src/test/java/art/arcane/iris/testsupport/KeyedBlockState.java b/core/src/test/java/art/arcane/iris/testsupport/KeyedBlockState.java index 2bc18cdcf..8fcdce7ec 100644 --- a/core/src/test/java/art/arcane/iris/testsupport/KeyedBlockState.java +++ b/core/src/test/java/art/arcane/iris/testsupport/KeyedBlockState.java @@ -1,8 +1,8 @@ package art.arcane.iris.testsupport; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; -public final class KeyedBlockState implements PlatformBlockState { +public final class KeyedBlockState implements NativeBlockState { private final String key; public KeyedBlockState(String key) { @@ -50,7 +50,7 @@ public final class KeyedBlockState implements PlatformBlockState { } @Override - public PlatformBlockState placementBaseState() { + public NativeBlockState placementBaseState() { return null; } @@ -125,12 +125,12 @@ public final class KeyedBlockState implements PlatformBlockState { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { return false; } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return false; } @@ -145,7 +145,7 @@ public final class KeyedBlockState implements PlatformBlockState { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { return null; } diff --git a/core/src/test/java/art/arcane/iris/world/history/ArchivedRegistryCompilerTest.java b/core/src/test/java/art/arcane/iris/world/history/ArchivedRegistryCompilerTest.java index 365211887..7f21f2840 100644 --- a/core/src/test/java/art/arcane/iris/world/history/ArchivedRegistryCompilerTest.java +++ b/core/src/test/java/art/arcane/iris/world/history/ArchivedRegistryCompilerTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.world.history; import art.arcane.iris.pack.datapack.IrisDatapackCompiler; import art.arcane.iris.pack.AtomicDirectoryPublisher; import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.platform.bukkit.nms.datapack.v1217.DataFixerV1217; +import art.arcane.iris.pack.datapack.v1217.DataFixerV1217; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; diff --git a/core/src/test/java/art/arcane/iris/world/history/GenerationEpochContractFactoryTest.java b/core/src/test/java/art/arcane/iris/world/history/GenerationEpochContractFactoryTest.java index 801d50d2f..f34cd3523 100644 --- a/core/src/test/java/art/arcane/iris/world/history/GenerationEpochContractFactoryTest.java +++ b/core/src/test/java/art/arcane/iris/world/history/GenerationEpochContractFactoryTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.world.history; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.ResourceLoader; import art.arcane.iris.generation.terrain.IrisDimension; diff --git a/core/src/test/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouterSupport.java b/core/src/test/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouterSupport.java index e28d2f5c5..5ab74d4e5 100644 --- a/core/src/test/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouterSupport.java +++ b/core/src/test/java/art/arcane/iris/world/history/GenerationHistoryRuntimeRouterSupport.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.iris.generation.runtime.GenerationSessionManager; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.block.B; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import org.junit.BeforeClass; import org.mockito.MockedStatic; @@ -34,7 +34,7 @@ import static org.mockito.Mockito.when; abstract class GenerationHistoryRuntimeRouterSupport extends GenerationHistorySupport { @BeforeClass public static void initializeMantleBlockState() throws Exception { - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState("AIR")).thenReturn(air); Class.forName(EngineMantle.class.getName()); diff --git a/core/src/test/java/art/arcane/iris/world/history/GenerationRegistryContractFactoryTest.java b/core/src/test/java/art/arcane/iris/world/history/GenerationRegistryContractFactoryTest.java index d6b821fbd..bdd7e4177 100644 --- a/core/src/test/java/art/arcane/iris/world/history/GenerationRegistryContractFactoryTest.java +++ b/core/src/test/java/art/arcane/iris/world/history/GenerationRegistryContractFactoryTest.java @@ -4,8 +4,8 @@ import art.arcane.iris.pack.validation.ContentGate; import art.arcane.iris.pack.validation.KeyStatus; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.pack.loading.ResourceLoader; -import art.arcane.iris.platform.bukkit.nms.datapack.DataVersion; -import art.arcane.iris.platform.bukkit.nms.datapack.IDataFixer; +import art.arcane.iris.pack.datapack.DataVersion; +import art.arcane.iris.pack.datapack.IDataFixer; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.biome.IrisBiomeCustomSpawn; diff --git a/core/src/test/java/art/arcane/iris/world/history/GenerationSemanticCaptureTest.java b/core/src/test/java/art/arcane/iris/world/history/GenerationSemanticCaptureTest.java index 2368de790..add19f54e 100644 --- a/core/src/test/java/art/arcane/iris/world/history/GenerationSemanticCaptureTest.java +++ b/core/src/test/java/art/arcane/iris/world/history/GenerationSemanticCaptureTest.java @@ -7,7 +7,7 @@ import art.arcane.iris.structure.placement.StructurePlacementMarker; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.mantle.EngineMantle; import art.arcane.iris.generation.biome.IrisBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.block.B; import art.arcane.volmlib.util.function.Consumer4; import art.arcane.volmlib.util.mantle.runtime.Mantle; @@ -40,7 +40,7 @@ import static org.mockito.Mockito.when; public final class GenerationSemanticCaptureTest { @BeforeClass public static void initializeMantleBlockState() throws Exception { - PlatformBlockState air = mock(PlatformBlockState.class); + NativeBlockState air = mock(NativeBlockState.class); try (MockedStatic blocks = mockStatic(B.class)) { blocks.when(() -> B.getState("AIR")).thenReturn(air); Class.forName(EngineMantle.class.getName()); diff --git a/core/src/test/java/art/arcane/iris/world/history/SavedTerrainChunkTest.java b/core/src/test/java/art/arcane/iris/world/history/SavedTerrainChunkTest.java index f99083be3..6dda1546a 100644 --- a/core/src/test/java/art/arcane/iris/world/history/SavedTerrainChunkTest.java +++ b/core/src/test/java/art/arcane/iris/world/history/SavedTerrainChunkTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.world.history; +import art.arcane.volmlib.nativelib.terrain.NativeTerrainReceiptStorage; + import art.arcane.volmlib.util.nbt.io.NBTUtil; import art.arcane.volmlib.util.nbt.tag.CompoundTag; import art.arcane.volmlib.util.nbt.tag.ListTag; @@ -7,7 +9,7 @@ import art.arcane.volmlib.util.nbt.tag.StringTag; import net.jpountz.lz4.LZ4BlockOutputStream; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisPlatform; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import org.junit.Before; import org.junit.After; @@ -43,7 +45,7 @@ public final class SavedTerrainChunkTest { when(platform.registries()).thenReturn(registries); for (String state : new String[]{"minecraft:stone", "minecraft:water[level=0]", "minecraft:sea_pickle[pickles=1,waterlogged=true]"}) { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn(state); when(registries.blockOrNull(state.split("\\[")[0])).thenReturn(block); } @@ -142,7 +144,7 @@ public final class SavedTerrainChunkTest { metadata = new CompoundTag(); saved.put("ChunkBukkitValues", metadata); } - metadata.putLong(NativeTerrainReceipt.STRUCTURE_ACTIVATION_KEY, 7); + metadata.putLong(NativeTerrainReceiptStorage.STRUCTURE_ACTIVATION_KEY, 7); writeChunk(world, saved, 2, false); SavedTerrainChunk.verifyCheckpoint(world, -1, -2, "minecraft:structure_starts", null, 7); assertThrows(IOException.class, () -> SavedTerrainChunk.verifyCheckpoint( @@ -163,7 +165,7 @@ public final class SavedTerrainChunkTest { assertEquals("test-epoch", decoded.epochId()); assertFalse(decoded.terrain().hasColumn(3, 3)); CompoundTag bukkitValues = new CompoundTag(); - bukkitValues.putByteArray(NativeTerrainReceipt.NBT_KEY, receipt); + bukkitValues.putByteArray(NativeTerrainReceiptStorage.NBT_KEY, receipt); initial.put("ChunkBukkitValues", bukkitValues); initial.remove("sections"); writeChunk(world, initial, 2, false); diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilityResolutionTest.java b/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilityResolutionTest.java deleted file mode 100644 index 07eea1726..000000000 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilityResolutionTest.java +++ /dev/null @@ -1,164 +0,0 @@ -package art.arcane.iris.world.lifecycle; - -import org.junit.Test; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Method; - -import static org.junit.Assert.assertEquals; - -public class CapabilityResolutionTest { - @Test - public void resolvesExactCreateLevelMethod() throws Exception { - Method method = CapabilityResolution.resolveCreateLevelMethod(CurrentCreateLevelOwner.class); - - assertEquals("createLevel", method.getName()); - assertEquals(3, method.getParameterCount()); - } - - @Test - public void resolvesDeclaredLegacyCreateLevelMethod() throws Exception { - Method method = CapabilityResolution.resolveCreateLevelMethod(DeclaredLegacyCreateLevelOwner.class); - - assertEquals("createLevel", method.getName()); - assertEquals(4, method.getParameterCount()); - } - - @Test - public void resolvesTwoArgLevelStorageAccessMethod() throws Exception { - Method method = CapabilityResolution.resolveLevelStorageAccessMethod(TwoArgLevelStorageSource.class); - - assertEquals("validateAndCreateAccess", method.getName()); - assertEquals(2, method.getParameterCount()); - } - - @Test - public void resolvesOneArgLevelStorageAccessMethod() throws Exception { - Method method = CapabilityResolution.resolveLevelStorageAccessMethod(OneArgLevelStorageSource.class); - - assertEquals("validateAndCreateAccess", method.getName()); - assertEquals(1, method.getParameterCount()); - } - - @Test - public void resolvesPublicWorldDataHelper() throws Exception { - Method method = CapabilityResolution.resolvePaperWorldDataMethod(PublicWorldLoader.class); - - assertEquals("loadWorldData", method.getName()); - assertEquals(3, method.getParameterCount()); - } - - @Test - public void resolvesDeclaredWorldDataHelper() throws Exception { - Method method = CapabilityResolution.resolvePaperWorldDataMethod(DeclaredWorldLoader.class); - - assertEquals("loadWorldData", method.getName()); - assertEquals(3, method.getParameterCount()); - } - - @Test - public void resolvesDeclaredServerRegistryAccessMethod() throws Exception { - Method method = CapabilityResolution.resolveServerRegistryAccessMethod(DeclaredRegistryAccessOwner.class); - - assertEquals("registryAccess", method.getName()); - assertEquals(0, method.getParameterCount()); - } - - @Test - public void resolvesCurrentWorldLoadingInfoConstructor() throws Exception { - Constructor constructor = CapabilityResolution.resolveWorldLoadingInfoConstructor(CurrentWorldLoadingInfo.class); - - assertEquals(4, constructor.getParameterCount()); - } - - @Test - public void resolvesLegacyWorldLoadingInfoConstructor() throws Exception { - Constructor constructor = CapabilityResolution.resolveWorldLoadingInfoConstructor(LegacyWorldLoadingInfo.class); - - assertEquals(5, constructor.getParameterCount()); - } - - public static final class LevelStem { - } - - public static final class WorldLoadingInfo { - } - - public static final class WorldLoadingInfoAndData { - } - - public static final class WorldDataAndGenSettings { - } - - public static final class PrimaryLevelData { - } - - public static final class LevelStorageAccess { - } - - public static final class ResourceKey { - } - - public static final class LoadedWorldData { - } - - public static final class MinecraftServer { - } - - public static final class RegistryAccess { - } - - public enum Environment { - NORMAL - } - - public static final class CurrentCreateLevelOwner { - public void createLevel(LevelStem levelStem, WorldLoadingInfoAndData worldLoadingInfoAndData, WorldDataAndGenSettings worldDataAndGenSettings) { - } - } - - public static final class DeclaredLegacyCreateLevelOwner { - private void createLevel(LevelStem levelStem, WorldLoadingInfo worldLoadingInfo, LevelStorageAccess levelStorageAccess, PrimaryLevelData primaryLevelData) { - } - } - - public static final class TwoArgLevelStorageSource { - public LevelStorageAccess validateAndCreateAccess(String worldName, ResourceKey resourceKey) { - return null; - } - } - - public static final class OneArgLevelStorageSource { - public LevelStorageAccess validateAndCreateAccess(String worldName) { - return null; - } - } - - public static final class PublicWorldLoader { - public static LoadedWorldData loadWorldData(MinecraftServer minecraftServer, ResourceKey dimensionKey, String worldName) { - return null; - } - } - - public static final class DeclaredWorldLoader { - private static LoadedWorldData loadWorldData(MinecraftServer minecraftServer, ResourceKey dimensionKey, String worldName) { - return null; - } - } - - public static final class DeclaredRegistryAccessOwner { - private RegistryAccess registryAccess() { - return null; - } - } - - public static final class CurrentWorldLoadingInfo { - public CurrentWorldLoadingInfo(Environment environment, ResourceKey stemKey, ResourceKey dimensionKey, boolean enabled) { - } - } - - public static final class LegacyWorldLoadingInfo { - private LegacyWorldLoadingInfo(int index, String worldName, String environment, ResourceKey stemKey, boolean enabled) { - } - } -} diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilitySnapshotFixtures.java b/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilitySnapshotFixtures.java index 1ed98c0d4..4aa00752b 100644 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilitySnapshotFixtures.java +++ b/core/src/test/java/art/arcane/iris/world/lifecycle/CapabilitySnapshotFixtures.java @@ -1,131 +1,20 @@ package art.arcane.iris.world.lifecycle; -import art.arcane.iris.world.lifecycle.CapabilitySnapshot.PaperLikeFlavor; +import art.arcane.volmlib.nativelib.terrain.NativeWorldRuntime; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; final class CapabilitySnapshotFixtures { private CapabilitySnapshotFixtures() { } - static CapabilitySnapshot forTesting(ServerFamily serverFamily, boolean regionizedRuntime, boolean worldsProviderHealthy, boolean paperLikeRuntimeHealthy) { - Object minecraftServer = paperLikeRuntimeHealthy ? new TestingPaperLikeServer("datapack-registry", "server-registry") : null; - Method createLevelMethod = null; - Field worldLoaderContextField = null; - Method serverRegistryAccessMethod = null; - try { - createLevelMethod = paperLikeRuntimeHealthy - ? TestingPaperLikeServer.class.getDeclaredMethod("createLevel", Object.class, Object.class, Object.class) - : null; - worldLoaderContextField = paperLikeRuntimeHealthy - ? CapabilityResolution.resolveField(TestingPaperLikeServer.class, "worldLoaderContext") - : null; - serverRegistryAccessMethod = paperLikeRuntimeHealthy - ? CapabilityResolution.resolveServerRegistryAccessMethod(TestingPaperLikeServer.class) - : null; - } catch (NoSuchMethodException | NoSuchFieldException e) { - throw new IllegalStateException(e); - } - return new CapabilitySnapshot( - serverFamily, - regionizedRuntime, - worldsProviderHealthy ? new Object() : null, - worldsProviderHealthy ? Object.class : null, - worldsProviderHealthy ? Object.class : null, - worldsProviderHealthy ? "test-provider" : "inactive", - null, - minecraftServer, - createLevelMethod, - paperLikeRuntimeHealthy ? PaperLikeFlavor.CURRENT_INFO_AND_DATA : PaperLikeFlavor.UNSUPPORTED, - null, - null, - null, - null, - null, - null, - worldLoaderContextField, - serverRegistryAccessMethod, - null, - null, - null, - null, - null, - null, - paperLikeRuntimeHealthy ? "available(test)" : "unsupported(test)" - ); - } - - static CapabilitySnapshot forTestingRuntimeRegistries(ServerFamily serverFamily, boolean regionizedRuntime, Object datapackDimensions, Object serverRegistryAccess) { - TestingPaperLikeServer minecraftServer = new TestingPaperLikeServer(datapackDimensions, serverRegistryAccess); - Method createLevelMethod; - Field worldLoaderContextField; - Method registryAccessMethod; - try { - createLevelMethod = TestingPaperLikeServer.class.getDeclaredMethod("createLevel", Object.class, Object.class, Object.class); - worldLoaderContextField = CapabilityResolution.resolveField(TestingPaperLikeServer.class, "worldLoaderContext"); - registryAccessMethod = CapabilityResolution.resolveServerRegistryAccessMethod(TestingPaperLikeServer.class); - } catch (NoSuchMethodException | NoSuchFieldException e) { - throw new IllegalStateException(e); - } - return new CapabilitySnapshot( - serverFamily, - regionizedRuntime, - null, - null, - null, - "inactive", - null, - minecraftServer, - createLevelMethod, - PaperLikeFlavor.CURRENT_INFO_AND_DATA, - null, - null, - null, - null, - null, - null, - worldLoaderContextField, - registryAccessMethod, - null, - null, - null, - null, - null, - null, - "available(test-runtime-registries)" - ); - } - - private static final class TestingPaperLikeServer { - private final TestingWorldLoaderContext worldLoaderContext; - private final Object registryAccess; - - private TestingPaperLikeServer(Object datapackDimensions, Object registryAccess) { - this.worldLoaderContext = new TestingWorldLoaderContext(datapackDimensions); - this.registryAccess = registryAccess; - } - - @SuppressWarnings("unused") - private void createLevel(Object levelStem, Object worldLoadingInfoAndData, Object worldDataAndGenSettings) { - } - - @SuppressWarnings("unused") - private Object registryAccess() { - return registryAccess; - } - } - - private static final class TestingWorldLoaderContext { - private final Object datapackDimensions; - - private TestingWorldLoaderContext(Object datapackDimensions) { - this.datapackDimensions = datapackDimensions; - } - - @SuppressWarnings("unused") - private Object datapackDimensions() { - return datapackDimensions; - } + static CapabilitySnapshot forTesting(ServerFamily family, boolean regionized, boolean providerHealthy, boolean runtimeHealthy) { + NativeWorldRuntime runtime = mock(NativeWorldRuntime.class); + when(runtime.available()).thenReturn(runtimeHealthy); + when(runtime.description()).thenReturn(runtimeHealthy ? "available(test)" : "unsupported(test)"); + return new CapabilitySnapshot(family, regionized, + providerHealthy ? new Object() : null, providerHealthy ? Object.class : null, + providerHealthy ? Object.class : null, providerHealthy ? "test-provider" : "inactive", runtime, null); } } diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleRuntimeLevelStemTest.java b/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleRuntimeLevelStemTest.java deleted file mode 100644 index 01119248e..000000000 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleRuntimeLevelStemTest.java +++ /dev/null @@ -1,135 +0,0 @@ -package art.arcane.iris.world.lifecycle; - -import art.arcane.iris.pack.loading.IrisData; -import art.arcane.iris.platform.bukkit.nms.INMSBinding; -import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.generation.runtime.EngineTarget; -import art.arcane.iris.platform.generation.PlatformChunkGenerator; -import org.bukkit.NamespacedKey; -import org.bukkit.World; -import org.bukkit.generator.ChunkGenerator; -import org.junit.Test; - -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Proxy; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; - -public class WorldLifecycleRuntimeLevelStemTest { - @Test - public void runtimeStemUsesFullServerRegistryAccessForPlatformGenerators() throws Exception { - Object datapackDimensions = new MissingDimensionTypeRegistry(); - Object serverRegistryAccess = new Object(); - CapabilitySnapshot capabilities = CapabilitySnapshotFixtures.forTestingRuntimeRegistries(ServerFamily.PURPUR, false, datapackDimensions, serverRegistryAccess); - WorldLifecycleRequest request = new WorldLifecycleRequest( - "studio", - NamespacedKey.minecraft("studio"), - World.Environment.NORMAL, - new TestingPlatformChunkGenerator(), - null, - null, - true, - false, - 1337L, - true, - false, - WorldLifecycleCaller.STUDIO - ); - AtomicReference seenRegistryAccess = new AtomicReference<>(); - INMSBinding binding = createBinding((registryAccess, generator) -> { - seenRegistryAccess.set(registryAccess); - return "runtime-stem"; - }); - - Object levelStem = WorldLifecycleSupport.resolveRuntimeLevelStem(capabilities, request, binding); - - assertEquals("runtime-stem", levelStem); - assertSame(serverRegistryAccess, seenRegistryAccess.get()); - assertNotSame(datapackDimensions, seenRegistryAccess.get()); - } - - private static INMSBinding createBinding(RuntimeStemFactory factory) { - InvocationHandler handler = (proxy, method, args) -> { - if ("createRuntimeLevelStem".equals(method.getName())) { - return factory.create(args[0], (ChunkGenerator) args[1]); - } - Class returnType = method.getReturnType(); - if (boolean.class.equals(returnType)) { - return false; - } - if (int.class.equals(returnType)) { - return 0; - } - if (long.class.equals(returnType)) { - return 0L; - } - if (float.class.equals(returnType)) { - return 0F; - } - if (double.class.equals(returnType)) { - return 0D; - } - return null; - }; - return (INMSBinding) Proxy.newProxyInstance( - INMSBinding.class.getClassLoader(), - new Class[]{INMSBinding.class}, - handler - ); - } - - @FunctionalInterface - private interface RuntimeStemFactory { - Object create(Object registryAccess, ChunkGenerator generator); - } - - private static final class MissingDimensionTypeRegistry { - } - - private static final class TestingPlatformChunkGenerator extends ChunkGenerator implements PlatformChunkGenerator { - @Override - public Engine getEngine() { - return null; - } - - @Override - public IrisData getData() { - return null; - } - - @Override - public EngineTarget getTarget() { - return null; - } - - @Override - public void close() { - } - - @Override - public boolean isStudio() { - return true; - } - - @Override - public CompletableFuture getSpawnChunks() { - return CompletableFuture.completedFuture(0); - } - - @Override - public void beginInitialEntry(boolean playerEntry) { - } - - @Override - public void completeInitialEntry() { - } - - @Override - public void hotload() { - } - } -} diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSeedTest.java b/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSeedTest.java deleted file mode 100644 index 634cead80..000000000 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSeedTest.java +++ /dev/null @@ -1,104 +0,0 @@ -package art.arcane.iris.world.lifecycle; - -import org.junit.Test; - -import java.util.OptionalLong; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -public class WorldLifecycleSeedTest { - @Test - public void appliesRequestedSeedToFreshWorldData() throws Exception { - TestWorldOptions options = new TestWorldOptions(111L, true, false); - Object dimensions = new Object(); - TestWorldGenSettings genSettings = new TestWorldGenSettings(options, dimensions); - Object data = new Object(); - TestWorldDataAndGenSettings input = new TestWorldDataAndGenSettings(data, genSettings); - - Object result = WorldLifecycleSupport.applySeedToWorldDataAndGenSettings(input, 69420L); - - assertTrue(result instanceof TestWorldDataAndGenSettings); - TestWorldDataAndGenSettings typed = (TestWorldDataAndGenSettings) result; - assertSame(data, typed.data()); - assertSame(dimensions, typed.genSettings().dimensions()); - assertEquals(69420L, typed.genSettings().options().seed()); - assertTrue(typed.genSettings().options().generateStructures()); - } - - @Test - public void returnsSameInstanceWhenSeedAlreadyMatches() throws Exception { - TestWorldOptions options = new TestWorldOptions(69420L, true, false); - TestWorldGenSettings genSettings = new TestWorldGenSettings(options, new Object()); - TestWorldDataAndGenSettings input = new TestWorldDataAndGenSettings(new Object(), genSettings); - - Object result = WorldLifecycleSupport.applySeedToWorldDataAndGenSettings(input, 69420L); - - assertSame(input, result); - } - - public static final class TestWorldDataAndGenSettings { - private final Object data; - private final TestWorldGenSettings genSettings; - - public TestWorldDataAndGenSettings(Object data, TestWorldGenSettings genSettings) { - this.data = data; - this.genSettings = genSettings; - } - - public Object data() { - return data; - } - - public TestWorldGenSettings genSettings() { - return genSettings; - } - } - - public static final class TestWorldGenSettings { - private final TestWorldOptions options; - private final Object dimensions; - - public TestWorldGenSettings(TestWorldOptions options, Object dimensions) { - this.options = options; - this.dimensions = dimensions; - } - - public TestWorldOptions options() { - return options; - } - - public Object dimensions() { - return dimensions; - } - } - - public static final class TestWorldOptions { - private final long seed; - private final boolean generateStructures; - private final boolean generateBonusChest; - - public TestWorldOptions(long seed, boolean generateStructures, boolean generateBonusChest) { - this.seed = seed; - this.generateStructures = generateStructures; - this.generateBonusChest = generateBonusChest; - } - - public long seed() { - return seed; - } - - public boolean generateStructures() { - return generateStructures; - } - - public boolean generateBonusChest() { - return generateBonusChest; - } - - public TestWorldOptions withSeed(OptionalLong newSeed) { - return new TestWorldOptions(newSeed.orElse(seed), generateStructures, generateBonusChest); - } - } -} diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupportCloseFailureTest.java b/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupportCloseFailureTest.java deleted file mode 100644 index ba5a269d7..000000000 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleSupportCloseFailureTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package art.arcane.iris.world.lifecycle; - -import art.arcane.iris.spi.IrisPlatform; -import art.arcane.iris.spi.IrisPlatforms; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * A level storage access that will not close still holds the world's session lock, and the next attempt to - * open that world fails with a lock message that names no cause. The close failure used to be swallowed - * whole, so the console had nothing to connect the two. - */ -public class WorldLifecycleSupportCloseFailureTest { - private IrisPlatform capturingPlatform; - private IrisPlatform previousPlatform; - - @Before - public void captureReports() { - previousPlatform = IrisPlatforms.isBound() ? IrisPlatforms.get() : null; - IrisPlatforms.unbind(); - capturingPlatform = mock(IrisPlatform.class); - IrisPlatforms.bind(capturingPlatform); - } - - @After - public void restorePlatform() { - IrisPlatforms.unbind(); - if (previousPlatform != null) { - IrisPlatforms.bind(previousPlatform); - } - } - - @Test - public void aFailedStorageCloseIsReportedWithItsCause() { - WorldLifecycleSupport.closeLevelStorageAccess(new FailingStorageAccess()); - - ArgumentCaptor reported = ArgumentCaptor.forClass(Throwable.class); - verify(capturingPlatform).reportError(anyString(), reported.capture()); - assertEquals("session lock still held", reported.getValue().getMessage()); - } - - @Test - public void aClosableStorageAccessReportsNothing() { - WorldLifecycleSupport.closeLevelStorageAccess(new ClosableStorageAccess()); - - verify(capturingPlatform, org.mockito.Mockito.never()).reportError(anyString(), org.mockito.ArgumentMatchers.any()); - } - - public static final class FailingStorageAccess { - public void close() { - throw new IllegalStateException("session lock still held"); - } - } - - public static final class ClosableStorageAccess { - public void close() { - } - } -} diff --git a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleUnloadAsyncTest.java b/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleUnloadAsyncTest.java index b0ad38d3f..afa8b69a4 100644 --- a/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleUnloadAsyncTest.java +++ b/core/src/test/java/art/arcane/iris/world/lifecycle/WorldLifecycleUnloadAsyncTest.java @@ -35,144 +35,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class WorldLifecycleUnloadAsyncTest { - @Test - public void manualUnloadFallbackDispatchesWorldUnloadEvent() { - World world = mock(World.class); - PluginManager pluginManager = mock(PluginManager.class); - - try (MockedStatic bukkit = mockStatic(Bukkit.class)) { - bukkit.when(Bukkit::getPluginManager).thenReturn(pluginManager); - assertTrue(WorldLifecycleSupport.announceManualWorldUnload(world)); - } - - verify(pluginManager).callEvent(any(WorldUnloadEvent.class)); - } - - @Test - public void manualUnloadFallbackHonorsCancelledWorldUnloadEvent() { - World world = mock(World.class); - PluginManager pluginManager = mock(PluginManager.class); - doAnswer(invocation -> { - WorldUnloadEvent event = invocation.getArgument(0); - event.setCancelled(true); - return null; - }).when(pluginManager).callEvent(any(WorldUnloadEvent.class)); - - try (MockedStatic bukkit = mockStatic(Bukkit.class)) { - bukkit.when(Bukkit::getPluginManager).thenReturn(pluginManager); - assertFalse(WorldLifecycleSupport.announceManualWorldUnload(world)); - } - } - - @Test - public void reflectedAsyncUnloadWaitsForTrueCallback() throws Exception { - CallbackServer server = new CallbackServer(); - Method unloadMethod = CallbackServer.class.getMethod( - "unloadWorldAsync", - World.class, - boolean.class, - Consumer.class - ); - - CompletableFuture result = WorldLifecycleSupport.invokeAsyncUnload( - server, - unloadMethod, - mock(World.class), - false - ); - - assertFalse(result.isDone()); - server.complete(true); - assertTrue(result.join()); - } - - @Test - public void reflectedAsyncUnloadWaitsForFalseCallback() throws Exception { - CallbackServer server = new CallbackServer(); - Method unloadMethod = CallbackServer.class.getMethod( - "unloadWorldAsync", - World.class, - boolean.class, - Consumer.class - ); - - CompletableFuture result = WorldLifecycleSupport.invokeAsyncUnload( - server, - unloadMethod, - mock(World.class), - true - ); - - assertFalse(result.isDone()); - server.complete(false); - assertFalse(result.join()); - } - - @Test - public void reflectedAsyncUnloadAcceptsCanvasResultCallback() throws Exception { - CanvasCallbackServer server = new CanvasCallbackServer(); - Method unloadMethod = CanvasCallbackServer.class.getMethod( - "unloadWorldAsync", - World.class, - boolean.class, - Consumer.class - ); - - CompletableFuture result = WorldLifecycleSupport.invokeAsyncUnload( - server, - unloadMethod, - mock(World.class), - true - ); - - assertFalse(result.isDone()); - server.complete(CanvasUnloadResult.SUCCESS); - assertTrue(result.join()); - } - - @Test - public void reflectedAsyncUnloadMapsCanvasFailureResultToFalse() throws Exception { - CanvasCallbackServer server = new CanvasCallbackServer(); - Method unloadMethod = CanvasCallbackServer.class.getMethod( - "unloadWorldAsync", - World.class, - boolean.class, - Consumer.class - ); - - CompletableFuture result = WorldLifecycleSupport.invokeAsyncUnload( - server, - unloadMethod, - mock(World.class), - false - ); - - server.complete(CanvasUnloadResult.FAILURE); - assertFalse(result.join()); - } - - @Test - public void reflectedAsyncUnloadPropagatesInvocationFailure() throws Exception { - FailingServer server = new FailingServer(); - Method unloadMethod = FailingServer.class.getMethod( - "unloadWorldAsync", - World.class, - boolean.class, - Consumer.class - ); - - CompletableFuture result = WorldLifecycleSupport.invokeAsyncUnload( - server, - unloadMethod, - mock(World.class), - false - ); - - CompletionException failure = assertThrows(CompletionException.class, result::join); - assertTrue(failure.getCause() instanceof IllegalStateException); - assertEquals("unload failed", failure.getCause().getMessage()); - } - @Test public void serviceRemovesRememberedBackendOnlyAfterTrueCompletion() { ControlledBackend remembered = new ControlledBackend("remembered"); @@ -354,45 +216,6 @@ public class WorldLifecycleUnloadAsyncTest { return world; } - public static final class CallbackServer { - private Consumer callback; - - public void unloadWorldAsync(World world, boolean save, Consumer callback) { - this.callback = callback; - } - - private void complete(boolean unloaded) { - callback.accept(unloaded); - } - } - - public static final class FailingServer { - public void unloadWorldAsync(World world, boolean save, Consumer callback) { - throw new IllegalStateException("unload failed"); - } - } - - public static final class CanvasCallbackServer { - private Consumer callback; - - public void unloadWorldAsync(World world, boolean save, Consumer callback) { - this.callback = callback; - } - - private void complete(CanvasUnloadResult result) { - callback.accept(result); - } - } - - public enum CanvasUnloadResult { - SUCCESS, - FAILURE; - - public boolean isSuccess() { - return this == SUCCESS; - } - } - private static final class ControlledBackend implements WorldLifecycleBackend { private final String name; private final CompletableFuture unloadResult = new CompletableFuture<>(); diff --git a/core/src/test/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackendTimeTest.java b/core/src/test/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackendTimeTest.java deleted file mode 100644 index ee4a26402..000000000 --- a/core/src/test/java/art/arcane/iris/world/runtime/PaperLikeRuntimeControlBackendTimeTest.java +++ /dev/null @@ -1,82 +0,0 @@ -package art.arcane.iris.world.runtime; - -import art.arcane.iris.spi.IrisLogging; -import art.arcane.iris.testsupport.BukkitTestServer; -import art.arcane.iris.world.lifecycle.CapabilitySnapshot; -import org.bukkit.World; -import org.junit.Before; -import org.junit.Test; -import org.mockito.MockedStatic; - -import java.util.OptionalLong; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.withSettings; - -public class PaperLikeRuntimeControlBackendTimeTest { - @Before - public void installServer() { - BukkitTestServer.install(); - } - - @Test - public void missingWorldHandleSkipsClockWithoutReportingFailure() throws Exception { - assertUnsupportedClock(mock(World.class)); - } - - @Test - public void missingClockMethodsSkipClockWithoutReportingFailure() throws Exception { - assertUnsupportedClock(worldWithHandle(new Object())); - } - - @Test - public void supportedClockStillReadsAndWrites() throws Exception { - ClockHandle handle = new ClockHandle(); - World world = worldWithHandle(handle); - PaperLikeRuntimeControlBackend backend = new PaperLikeRuntimeControlBackend(mock(CapabilitySnapshot.class)); - - assertEquals(OptionalLong.of(12000L), backend.readDayTime(world)); - assertTrue(backend.writeDayTime(world, 6000L)); - assertEquals(OptionalLong.of(6000L), backend.readDayTime(world)); - } - - private void assertUnsupportedClock(World world) throws Exception { - PaperLikeRuntimeControlBackend backend = new PaperLikeRuntimeControlBackend(mock(CapabilitySnapshot.class)); - try (MockedStatic logging = mockStatic(IrisLogging.class)) { - assertEquals(OptionalLong.empty(), backend.readDayTime(world)); - assertEquals(OptionalLong.empty(), backend.readDayTime(world)); - assertFalse(backend.writeDayTime(world, 6000L)); - logging.verify(() -> IrisLogging.reportError(anyString(), any(Throwable.class)), never()); - } - } - - private World worldWithHandle(Object handle) { - World world = mock(World.class, withSettings().extraInterfaces(HandleWorld.class)); - when(((HandleWorld) world).getHandle()).thenReturn(handle); - return world; - } - - public interface HandleWorld { - Object getHandle(); - } - - public static class ClockHandle { - private long dayTime = 12000L; - - public long getDayTime() { - return dayTime; - } - - public void setDayTime(long dayTime) { - this.dayTime = dayTime; - } - } -} diff --git a/core/src/test/java/art/arcane/iris/world/runtime/WorldRuntimeControlServiceTimeLockTest.java b/core/src/test/java/art/arcane/iris/world/runtime/WorldRuntimeControlServiceTimeLockTest.java index 075df1933..c85e2d199 100644 --- a/core/src/test/java/art/arcane/iris/world/runtime/WorldRuntimeControlServiceTimeLockTest.java +++ b/core/src/test/java/art/arcane/iris/world/runtime/WorldRuntimeControlServiceTimeLockTest.java @@ -1,6 +1,8 @@ package art.arcane.iris.world.runtime; import art.arcane.iris.world.lifecycle.CapabilitySnapshot; +import art.arcane.volmlib.nativelib.terrain.NativeWorldRuntime; +import art.arcane.volmlib.nativelib.terrain.NativeWorldClock; import art.arcane.iris.world.lifecycle.ServerFamily; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; @@ -12,7 +14,6 @@ import org.junit.Before; import org.junit.Test; import java.lang.reflect.Constructor; -import java.lang.reflect.Proxy; import java.util.OptionalLong; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; @@ -23,8 +24,10 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class WorldRuntimeControlServiceTimeLockTest { + private NativeWorldClock clock; @Before public void ensureBukkitServer() { BukkitTestServer.install(); @@ -95,166 +98,35 @@ public class WorldRuntimeControlServiceTimeLockTest { private WorldRuntimeControlService createService() throws Exception { Constructor snapshotConstructor = CapabilitySnapshot.class.getDeclaredConstructor( - ServerFamily.class, - boolean.class, - Object.class, - Class.class, - Class.class, - String.class, - Object.class, - Object.class, - java.lang.reflect.Method.class, - CapabilitySnapshot.PaperLikeFlavor.class, - Class.class, - java.lang.reflect.Method.class, - java.lang.reflect.Constructor.class, - java.lang.reflect.Constructor.class, - java.lang.reflect.Method.class, - java.lang.reflect.Method.class, - java.lang.reflect.Field.class, - java.lang.reflect.Method.class, - java.lang.reflect.Field.class, - java.lang.reflect.Field.class, - java.lang.reflect.Method.class, - java.lang.reflect.Method.class, - java.lang.reflect.Method.class, - java.lang.reflect.Method.class, - String.class - ); + ServerFamily.class, boolean.class, Object.class, Class.class, Class.class, String.class, + NativeWorldRuntime.class, java.lang.reflect.Method.class); snapshotConstructor.setAccessible(true); + NativeWorldRuntime runtime = mock(NativeWorldRuntime.class); + when(runtime.clock()).thenReturn(clock); CapabilitySnapshot snapshot = snapshotConstructor.newInstance( - ServerFamily.PAPER, - false, - null, - null, - null, - "test", - Bukkit.getServer(), - null, - null, - CapabilitySnapshot.PaperLikeFlavor.UNSUPPORTED, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - "test" - ); + ServerFamily.PAPER, false, null, null, null, "test", runtime, null); Constructor serviceConstructor = WorldRuntimeControlService.class.getDeclaredConstructor(CapabilitySnapshot.class); serviceConstructor.setAccessible(true); return serviceConstructor.newInstance(snapshot); } - private World createWorldProxy(String name, boolean fixedTime, AtomicBoolean setTimeCalled, AtomicLong dayTime, boolean throwOnSetTime) { - Object dimensionType = Proxy.newProxyInstance( - World.class.getClassLoader(), - new Class[]{DimensionTypeProbe.class}, - (proxy, method, args) -> { - if ("hasFixedTime".equals(method.getName())) { - return fixedTime; - } - if ("fixedTime".equals(method.getName())) { - return fixedTime ? OptionalLong.of(6000L) : OptionalLong.empty(); - } - return null; - } - ); - Object holder = Proxy.newProxyInstance( - World.class.getClassLoader(), - new Class[]{HolderProbe.class}, - (proxy, method, args) -> { - if ("value".equals(method.getName())) { - return dimensionType; - } - return null; - } - ); - Object handle = Proxy.newProxyInstance( - World.class.getClassLoader(), - new Class[]{HandleProbe.class}, - (proxy, method, args) -> { - if ("dimensionTypeRegistration".equals(method.getName())) { - return holder; - } - if ("getDayTime".equals(method.getName())) { - return dayTime.get(); - } - if ("setDayTime".equals(method.getName())) { - setTimeCalled.set(true); - if (throwOnSetTime) { - throw new IllegalArgumentException("Cannot set time in world without world clock"); - } - dayTime.set(((Long) args[0]).longValue()); - return null; - } - return null; - } - ); - return (World) Proxy.newProxyInstance( - World.class.getClassLoader(), - new Class[]{World.class, WorldHandleProbe.class}, - (proxy, method, args) -> { - if ("getName".equals(method.getName())) { - return name; - } - if ("getHandle".equals(method.getName())) { - return handle; - } - if ("getFullTime".equals(method.getName())) { - return dayTime.get(); - } - - Class returnType = method.getReturnType(); - if (boolean.class.equals(returnType)) { - return false; - } - if (int.class.equals(returnType)) { - return 0; - } - if (long.class.equals(returnType)) { - return 0L; - } - if (float.class.equals(returnType)) { - return 0F; - } - if (double.class.equals(returnType)) { - return 0D; - } - - return null; - } - ); - } - - private interface WorldHandleProbe { - Object getHandle(); - } - - private interface HandleProbe { - Object dimensionTypeRegistration(); - - long getDayTime(); - - void setDayTime(long time); - } - - private interface HolderProbe { - Object value(); - } - - private interface DimensionTypeProbe { - boolean hasFixedTime(); - - OptionalLong fixedTime(); + private World createWorldProxy(String name, boolean fixedTime, AtomicBoolean setTimeCalled, + AtomicLong dayTime, boolean throwOnSetTime) throws Exception { + World world = mock(World.class); + when(world.getName()).thenReturn(name); + when(world.getFullTime()).thenAnswer(invocation -> dayTime.get()); + clock = mock(NativeWorldClock.class); + when(clock.hasMutableClock(world)).thenReturn(!fixedTime); + when(clock.readDayTime(world)).thenAnswer(invocation -> OptionalLong.of(dayTime.get())); + when(clock.writeDayTime(world, 6000L)).thenAnswer(invocation -> { + setTimeCalled.set(true); + if (throwOnSetTime) { + throw new IllegalArgumentException("Cannot set time in world without world clock"); + } + dayTime.set(6000L); + return true; + }); + return world; } } diff --git a/core/src/test/java/art/arcane/iris/world/storage/matter/PreObjectMatterTest.java b/core/src/test/java/art/arcane/iris/world/storage/matter/PreObjectMatterTest.java index d7365aae0..c7608b00b 100644 --- a/core/src/test/java/art/arcane/iris/world/storage/matter/PreObjectMatterTest.java +++ b/core/src/test/java/art/arcane/iris/world/storage/matter/PreObjectMatterTest.java @@ -1,6 +1,6 @@ package art.arcane.iris.world.storage.matter; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell; import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction; import art.arcane.iris.testsupport.BukkitTestServer; @@ -110,7 +110,7 @@ public class PreObjectMatterTest { @Test public void blockKeyHasAnExplicitPresenceFlag() throws IOException { - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); when(block.key()).thenReturn("minecraft:stone"); PreObjectMatter matter = new PreObjectMatter(); ByteArrayOutputStream bytes = new ByteArrayOutputStream(); @@ -134,7 +134,7 @@ public class PreObjectMatterTest { @Test public void capturesRemainIndependentAndFirstWriteWins() { MatterCavern cavern = new MatterCavern(true, "", (byte) 0); - PlatformBlockState block = mock(PlatformBlockState.class); + NativeBlockState block = mock(NativeBlockState.class); PreObjectMatterCell cell = PreObjectMatterCell.string(null) .captureBlock(block) .captureCavern(cavern) @@ -143,7 +143,7 @@ public class PreObjectMatterTest { assertTrue(cell.blockCaptured()); assertTrue(cell.stringCaptured()); assertTrue(cell.cavernCaptured()); - assertEquals(block, cell.original(PlatformBlockState.class)); + assertEquals(block, cell.original(NativeBlockState.class)); assertNull(cell.original(String.class)); assertEquals(cavern, cell.original(MatterCavern.class)); assertFalse(cell.captures(Integer.class)); diff --git a/core/src/testFixtures/java/art/arcane/iris/testsupport/PlatformBinding.java b/core/src/testFixtures/java/art/arcane/iris/testsupport/PlatformBinding.java index c813fe73a..007d639c5 100644 --- a/core/src/testFixtures/java/art/arcane/iris/testsupport/PlatformBinding.java +++ b/core/src/testFixtures/java/art/arcane/iris/testsupport/PlatformBinding.java @@ -3,7 +3,7 @@ package art.arcane.iris.testsupport; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.spi.PlatformRegistries; import org.junit.rules.TestRule; import org.junit.runner.Description; @@ -19,7 +19,7 @@ public final class PlatformBinding implements TestRule { private final Supplier factory; private IrisPlatform platform; private PlatformRegistries registries; - private PlatformBlockState block; + private NativeBlockState block; private PlatformBinding(Supplier factory) { this.factory = factory; @@ -53,7 +53,7 @@ public final class PlatformBinding implements TestRule { return registries; } - public PlatformBlockState block() { + public NativeBlockState block() { if (block == null) { block = registries().block("minecraft:stone"); } @@ -86,7 +86,7 @@ public final class PlatformBinding implements TestRule { private void bind() { if (factory == null) { - block = mock(PlatformBlockState.class); + block = mock(NativeBlockState.class); registries = mock(PlatformRegistries.class); when(registries.block(anyString())).thenReturn(block); platform = mock(IrisPlatform.class); diff --git a/gradle.properties b/gradle.properties index b939f4fd0..acbf85722 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,8 +21,6 @@ org.gradle.jvmargs=-Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF org.gradle.caching=true org.gradle.configureondemand=false -nmsTools.repo-url=https://repo.codemc.org/repository/nms/ -nmsTools.specialSourceVersion=1.11.4 irisVersion=4.1.1-26.2 minecraftVersion=26.2 # Bukkit plugin api-version: lowest supported Minecraft release line so one artifact loads on 26.1.2 and 26.2 diff --git a/gradle/generation-build-revision.gradle b/gradle/generation-build-revision.gradle index e3f93f0aa..8785ce3a2 100644 --- a/gradle/generation-build-revision.gradle +++ b/gradle/generation-build-revision.gradle @@ -13,6 +13,17 @@ Configuration kernelDependencies = configurations.create('generationKernelDepend extendsFrom(configurations.implementation, configurations.slim, configurations.annotationProcessor) } dependencies.add(kernelDependencies.name, libs.guava) +String nativeLibraryVersion = providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last() +List nativeRevisionModules = ['native-common', 'native-minecraft26_2-sources'] +for (String adapter : rootProject.extensions.extraProperties.get('nativeDevelopmentAdapters')) { + nativeRevisionModules.add('native-' + adapter) +} +for (String module : nativeRevisionModules) { + dependencies.add(kernelDependencies.name, "com.github.VolmitSoftware.VolmLib:${module}:${nativeLibraryVersion}") { + transitive = false + } +} + GenerationBuildRevision.CaptureOptions revisionScope = GenerationRevisionScope.current(rootProject.projectDir.toPath()) File catalogFile = file('generation-kernels/catalog.tsv') @@ -30,9 +41,11 @@ Closure> resolvedKernelDependencies = { continue } boolean volmLib = (group == 'com.github.VolmitSoftware' && name == 'VolmLib') - || group == 'com.github.VolmitSoftware.VolmLib' + || (group == 'com.github.VolmitSoftware.VolmLib' && name in ['shared', 'volmlib-shared']) || (group == 'art.arcane' && name == 'shared') - String key = volmLib ? 'volmlib' : "${group}:${name}".toString() + String artifactName = group == 'com.github.VolmitSoftware.VolmLib' && name ==~ /v[0-9_]+R[0-9]+/ + ? 'native-' + name : name + String key = volmLib ? 'volmlib' : "${group}:${artifactName}".toString() if (artifact.classifier != null) { key += ':' + artifact.classifier } @@ -50,7 +63,7 @@ Closure> resolvedKernelDependencies = { TaskProvider revisionResources = tasks.register('generateGenerationBuildRevision') { group = 'build' description = 'Captures generation sources and resolved dependencies as build revision provenance.' - dependsOn(kernelDependencies, kernelAgentJar, ":adapters:bukkit:nms:v26_2_R1:convert", ":adapters:bukkit:nms:v26_3_R1:convert") + dependsOn(kernelDependencies, kernelAgentJar) inputs.files(revisionScope.roots().collect { String source -> rootProject.file(source) }) inputs.property('revisionScope', revisionScope.toString()) inputs.files(catalogFile, kernelDependencies, kernelAgentJar.flatMap { Jar jar -> jar.archiveFile }) diff --git a/gradle/modded-generation-build-revision.gradle b/gradle/modded-generation-build-revision.gradle index 15b14bc51..7b2264df9 100644 --- a/gradle/modded-generation-build-revision.gradle +++ b/gradle/modded-generation-build-revision.gradle @@ -32,7 +32,7 @@ TaskProvider resolvedRevisionVerification = tasks.register('verifyReso continue } boolean volmLib = (group == 'com.github.VolmitSoftware' && name == 'VolmLib') - || group == 'com.github.VolmitSoftware.VolmLib' + || (group == 'com.github.VolmitSoftware.VolmLib' && name in ['shared', 'volmlib-shared']) || (group == 'art.arcane' && name == 'shared') String key = volmLib ? 'volmlib' : "${group}:${name}".toString() if (artifact.classifier != null) { diff --git a/gradle/native-minecraft-sources.gradle b/gradle/native-minecraft-sources.gradle new file mode 100644 index 000000000..ea1150361 --- /dev/null +++ b/gradle/native-minecraft-sources.gradle @@ -0,0 +1,35 @@ +Configuration nativeMinecraftSources = configurations.create('nativeMinecraftSources') { + canBeConsumed = false + canBeResolved = true +} + +Properties nativeLibraryProperties = new Properties() +file('../../gradle.properties').withInputStream { InputStream stream -> nativeLibraryProperties.load(stream) } +String nativeLibraryCoordinate = providers.gradleProperty('volmLibCoordinate') + .getOrElse(nativeLibraryProperties.getProperty('volmLibCoordinate')) +String nativeSourceLoader = extensions.extraProperties.get('nativeSourceLoader') +String nativeSourceImplementation = 'minecraft26_2' +extensions.extraProperties.set('nativeSourceImplementation', nativeSourceImplementation) +dependencies.add(nativeMinecraftSources.name, + "com.github.VolmitSoftware.VolmLib:native-${nativeSourceImplementation}-sources:${nativeLibraryCoordinate.tokenize(':').last()}") + +TaskProvider unpackNativeMinecraftSources = tasks.register('unpackNativeMinecraftSources', Sync) { + dependsOn(nativeMinecraftSources) + from(provider { nativeMinecraftSources.collect { File artifact -> zipTree(artifact) } }) { + include('art/**', 'modded/**', "${nativeSourceLoader}/**", + 'resources/modded/**', "resources/${nativeSourceLoader}/**") + } + into(layout.buildDirectory.dir('generated/sources/native-minecraft')) +} + +sourceSets.main.java.srcDir(unpackNativeMinecraftSources) + +sourceSets.main.resources.srcDir(layout.buildDirectory.dir('generated/sources/native-minecraft/resources/modded')) +sourceSets.main.resources.srcDir(layout.buildDirectory.dir("generated/sources/native-minecraft/resources/${nativeSourceLoader}")) +tasks.named('processResources').configure { + dependsOn(unpackNativeMinecraftSources) +} + +tasks.matching { Task task -> task.name == 'createMinecraftArtifacts' }.configureEach { + dependsOn(unpackNativeMinecraftSources) +} diff --git a/gradle/volmlib-resolution.settings.gradle b/gradle/volmlib-resolution.settings.gradle index adc6a88e9..02ffef79d 100644 --- a/gradle/volmlib-resolution.settings.gradle +++ b/gradle/volmlib-resolution.settings.gradle @@ -94,6 +94,12 @@ if (irisUseLocalVolmLib && irisLocalVolmLibDirectory != null) { substitute(module('com.github.VolmitSoftware.VolmLib:shared')).using(project(':shared')) substitute(module('com.github.VolmitSoftware.VolmLib:packaging')).using(project(':packaging')) substitute(module('com.github.VolmitSoftware.VolmLib:volmlib-shared')).using(project(':shared')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-api')).using(project(':native-api')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-common')).using(project(':native-common')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-terrain-api')).using(project(':native-terrain-api')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-v26_2_R1')).using(project(':native:v26_2_R1')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-v26_3_R1')).using(project(':native:v26_3_R1')) + substitute(module('com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources')).using(project(':native-minecraft26_2-sources')) } } } diff --git a/packaging/shrink.pro b/packaging/shrink.pro index a04610c2a..7b5d88fb8 100644 --- a/packaging/shrink.pro +++ b/packaging/shrink.pro @@ -12,3 +12,9 @@ public static org.bukkit.event.HandlerList getHandlerList(); public org.bukkit.event.HandlerList getHandlers(); } + +-keep class art.arcane.volmlib.nativelib.**.scoreboard.NativeScoreboardPackets { public (); } +-keep @art.arcane.volmlib.nativelib.NativeBinding interface * { *; } +-keep class art.arcane.volmlib.nativelib.**.terrain.Native* { public (); } + +-keep class art.arcane.volmlib.nativelib.**.server.NativeServerDiagnosticsImpl { public (); } diff --git a/probe/src/main/java/art/arcane/iris/probe/GenerationOrderProbe.java b/probe/src/main/java/art/arcane/iris/probe/GenerationOrderProbe.java index 8c62fe392..1146cb570 100644 --- a/probe/src/main/java/art/arcane/iris/probe/GenerationOrderProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/GenerationOrderProbe.java @@ -1,8 +1,8 @@ package art.arcane.iris.probe; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import java.io.File; @@ -284,8 +284,8 @@ public final class GenerationOrderProbe { static ChunkHash hashChunk( ChunkCoordinate coordinate, - Hunk blocks, - Hunk biomes, + Hunk blocks, + Hunk biomes, int height ) { MessageDigest blockDigest = sha256(); @@ -300,8 +300,8 @@ public final class GenerationOrderProbe { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < height; y++) { - PlatformBlockState state = blocks.get(x, y, z); - PlatformBiome biome = biomes.get(x, y, z); + NativeBlockState state = blocks.get(x, y, z); + NativeBiome biome = biomes.get(x, y, z); String blockKey = state == null ? "minecraft:air" : state.key(); String biomeKey = biome == null ? "" : biome.key(); blockCounts.merge(blockKey, 1, Integer::sum); @@ -492,8 +492,8 @@ public final class GenerationOrderProbe { boolean multicore ) throws Exception { int height = engine.getTarget().getHeight(); - Hunk blocks = Hunk.newArrayHunk(16, height, 16); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk blocks = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); engine.generate(coordinate.x() << 4, coordinate.z() << 4, blocks, biomes, multicore); return new ChunkOutput(coordinate, hashChunk(coordinate, blocks, biomes, height)); } diff --git a/probe/src/main/java/art/arcane/iris/probe/GenerationProbe.java b/probe/src/main/java/art/arcane/iris/probe/GenerationProbe.java index 24795bb5f..de492e006 100644 --- a/probe/src/main/java/art/arcane/iris/probe/GenerationProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/GenerationProbe.java @@ -36,8 +36,8 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.world.IrisWorld; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import java.io.File; @@ -399,8 +399,8 @@ public final class GenerationProbe { ChunkCoordinate coordinate = coordinates.get(index); drainReported(); List failures = new ArrayList<>(); - Hunk blocks = Hunk.newArrayHunk(16, height, 16); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk blocks = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); long started = System.nanoTime(); try { engine.generate( @@ -481,7 +481,7 @@ public final class GenerationProbe { } private static void updateSignature(MessageDigest digest, ChunkCoordinate coordinate, - Hunk blocks, Hunk biomes, int height) { + Hunk blocks, Hunk biomes, int height) { MessageDigest blockDigest = sha256(); MessageDigest biomeDigest = sha256(); updateDigest(digest, coordinate.x() + "," + coordinate.z()); @@ -490,8 +490,8 @@ public final class GenerationProbe { for (int x = 0; x < 16; x++) { for (int z = 0; z < 16; z++) { for (int y = 0; y < height; y++) { - PlatformBlockState state = blocks.get(x, y, z); - PlatformBiome biome = biomes.get(x, y, z); + NativeBlockState state = blocks.get(x, y, z); + NativeBiome biome = biomes.get(x, y, z); String stateKey = state == null ? "minecraft:air" : state.key(); String biomeKey = biome == null ? "null" : biome.key(); updateDigest(digest, stateKey); diff --git a/probe/src/main/java/art/arcane/iris/probe/HydrologyBlockProbe.java b/probe/src/main/java/art/arcane/iris/probe/HydrologyBlockProbe.java index fb37fd9f8..90f72cde8 100644 --- a/probe/src/main/java/art/arcane/iris/probe/HydrologyBlockProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/HydrologyBlockProbe.java @@ -7,7 +7,7 @@ import art.arcane.iris.generation.hydrology.HydrologyColumnSample; import art.arcane.iris.generation.hydrology.HydrologyFeatureType; import art.arcane.iris.generation.hydrology.RiverFootprint; import art.arcane.iris.generation.terrain.InferredType; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import com.google.gson.GsonBuilder; import javax.imageio.ImageIO; @@ -73,7 +73,7 @@ public final class HydrologyBlockProbe { } } - record PlannedColumn(int naturalY, boolean ocean, HydrologyColumnLayer surface, PlatformBlockState fluid) { + record PlannedColumn(int naturalY, boolean ocean, HydrologyColumnLayer surface, NativeBlockState fluid) { boolean mouthAt(int seaLevel) { return surface != null && fluid != null && fluid.isWater() && surface.feature().type() == HydrologyFeatureType.MOUTH @@ -150,7 +150,7 @@ public final class HydrologyBlockProbe { return chunks.containsKey(RiverFootprint.pack(Math.floorDiv(x, 16), Math.floorDiv(z, 16))); } - PlatformBlockState block(int x, int y, int z) { + NativeBlockState block(int x, int y, int z) { RealPackProbeSupport.GeneratedChunk chunk = chunks.get( RiverFootprint.pack(Math.floorDiv(x, 16), Math.floorDiv(z, 16))); return chunk == null || y < 0 || y >= height ? null : chunk.blockAt(x, y, z); @@ -203,7 +203,7 @@ public final class HydrologyBlockProbe { int natural = sample == null ? (int) Math.round(complex.getNaturalHeightStream().getDouble(x, z)) : sample.naturalHeight(); HydrologyColumnLayer layer = surfaceForInspection(sample); - PlatformBlockState fluid = layer == null ? null : complex.resolveHydrologyFluid( + NativeBlockState fluid = layer == null ? null : complex.resolveHydrologyFluid( layer.profileKey(), x, z); planned.put(RiverFootprint.pack(x, z), new PlannedColumn(natural, sample == null ? complex.getBridgeStream().get(x, z) == InferredType.SEA : sample.ocean(), layer, fluid)); @@ -274,7 +274,7 @@ public final class HydrologyBlockProbe { continue; } for (int y = 0; y < volume.height; y++) { - PlatformBlockState block = volume.block(x, y, z); + NativeBlockState block = volume.block(x, y, z); if (key(block).equals("minecraft:sugar_cane") && !key(volume.block(x, y - 1, z)).equals("minecraft:sugar_cane")) { inspectCane(volume, block, x, y, z, evidence); @@ -305,7 +305,7 @@ public final class HydrologyBlockProbe { } for (int y = Math.max(0, surface.bedY() + 1); y <= surface.fluidHeadY() && y < volume.height; y++) { evidence.plannedWetVoxels++; - PlatformBlockState actual = volume.block(x, y, z); + NativeBlockState actual = volume.block(x, y, z); boolean matches = column.fluid().isWater() ? water(actual) : key(column.fluid()).equals(key(actual)); if (!matches) { @@ -319,9 +319,9 @@ public final class HydrologyBlockProbe { return evidence; } - private static void inspectCane(Volume volume, PlatformBlockState cane, int x, int y, int z, Evidence evidence) { + private static void inspectCane(Volume volume, NativeBlockState cane, int x, int y, int z, Evidence evidence) { evidence.caneRoots++; - PlatformBlockState substrate = volume.block(x, y - 1, z); + NativeBlockState substrate = volume.block(x, y - 1, z); if (substrate == null || !cane.canPlaceOnto(substrate)) { evidence.invalidCaneSubstrates++; evidence.example("unsupported cane " + x + "," + y + "," + z + " on " + key(substrate)); @@ -335,7 +335,7 @@ public final class HydrologyBlockProbe { censored = true; continue; } - PlatformBlockState adjacent = volume.block(nx, y - 1, nz); + NativeBlockState adjacent = volume.block(nx, y - 1, nz); if (water(adjacent) || key(adjacent).equals("minecraft:frosted_ice")) { evidence.validCaneRoots++; return; @@ -447,7 +447,7 @@ public final class HydrologyBlockProbe { for (int z = volume.minimumZ; z <= volume.maximumZ; z++) { for (int x = volume.minimumX; x <= volume.maximumX; x++) { for (int y = volume.height - 1; y >= 0; y--) { - PlatformBlockState block = volume.block(x, y, z); + NativeBlockState block = volume.block(x, y, z); if (block != null && !block.isAir()) { overhead.setRGB(x - volume.minimumX, z - volume.minimumZ, color(block)); break; @@ -482,7 +482,7 @@ public final class HydrologyBlockProbe { return List.copyOf(artifacts); } - private static int color(PlatformBlockState block) { + private static int color(NativeBlockState block) { if (block == null || block.isAir()) { return 0xC9E3F0; } @@ -508,11 +508,11 @@ public final class HydrologyBlockProbe { return 0x888C90; } - private static boolean water(PlatformBlockState block) { + private static boolean water(NativeBlockState block) { return block != null && (block.isWater() || block.isWaterLogged()); } - private static String key(PlatformBlockState block) { + private static String key(NativeBlockState block) { if (block == null) { return "minecraft:air"; } diff --git a/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java b/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java index 7eb0917a1..583ab85d0 100644 --- a/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/HydrologyPackProbe.java @@ -36,8 +36,8 @@ import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.biome.IrisBiomeCustom; import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.generation.hydrology.IrisRiverBlendStyle; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.matter.MatterUpdate; import art.arcane.volmlib.util.math.RNG; @@ -3653,7 +3653,7 @@ public final class HydrologyPackProbe { throw new IllegalStateException("Generated witness differs from the accepted composed hydrology layer."); } HydrologyColumnLayer layer = witness.layer(); - PlatformBlockState expectedFluid = engine.getComplex().resolveHydrologyFluid( + NativeBlockState expectedFluid = engine.getComplex().resolveHydrologyFluid( layer.profileKey(), position.x(), position.z() @@ -3668,7 +3668,7 @@ public final class HydrologyPackProbe { requireNoUpdateMarker(engine, position); } String blockStateKey = stateKey(generated.blockAt(position.x(), position.y(), position.z())); - PlatformBiome biome = generated.biomeAt( + NativeBiome biome = generated.biomeAt( position.x(), Math.min(position.y(), generated.height() - 1), position.z() @@ -3698,7 +3698,7 @@ public final class HydrologyPackProbe { HydrologyColumnSample sample, HydrologyColumnLayer layer, CavePosition position, - PlatformBlockState expectedFluid + NativeBlockState expectedFluid ) { if (sample.terrainHeight() != layer.bedY()) { throw new IllegalStateException("Accepted surface witness does not own the composed terrain height."); @@ -3706,7 +3706,7 @@ public final class HydrologyPackProbe { if (layer.fluidHeadY() >= sample.naturalHeight()) { throw new IllegalStateException("Generated surface witness is not recessed below natural terrain."); } - PlatformBlockState bed = generated.blockAt(position.x(), layer.bedY(), position.z()); + NativeBlockState bed = generated.blockAt(position.x(), layer.bedY(), position.z()); if (bed == null || bed.isAirOrFluid() || matchesConfiguredFluid(bed, expectedFluid, true)) { throw new IllegalStateException("Generated surface witness has no non-fluid channel bed: state=" + stateKey(bed) @@ -3715,7 +3715,7 @@ public final class HydrologyPackProbe { + ", sampleLayers=" + sample.layers()); } requireNonVegetatedBed(bed, "surface"); - PlatformBlockState fluid = generated.blockAt(position.x(), position.y(), position.z()); + NativeBlockState fluid = generated.blockAt(position.x(), position.y(), position.z()); requireMatchingFluid( fluid, expectedFluid, @@ -3724,7 +3724,7 @@ public final class HydrologyPackProbe { ); int composedFluidHead = composedSurfaceFluidHead(sample); if (composedFluidHead + 1 < generated.height()) { - PlatformBlockState above = generated.blockAt(position.x(), composedFluidHead + 1, position.z()); + NativeBlockState above = generated.blockAt(position.x(), composedFluidHead + 1, position.z()); if (above != null && matchesConfiguredFluid(above, expectedFluid, true)) { throw new IllegalStateException("Generated surface witness contains " + stateKey(above) + " above its accepted head at " @@ -3732,7 +3732,7 @@ public final class HydrologyPackProbe { + ": layer=" + layer + ", sampleLayers=" + sample.layers() + "."); } } - PlatformBiome biome = generated.biomeAt(position.x(), layer.fluidHeadY(), position.z()); + NativeBiome biome = generated.biomeAt(position.x(), layer.fluidHeadY(), position.z()); IrisBiome surfaceBiome = engine.getComplex().getTrueBiomeStream().get(position.x(), position.z()); String expectedBiomeKey = generatedSurfaceBiomeKey( surfaceBiome, @@ -3784,7 +3784,7 @@ public final class HydrologyPackProbe { IrisHydrologyRuntime runtime, RealPackProbeSupport.GeneratedChunk generated, GeneratedWitness witness, - PlatformBlockState expectedFluid, + NativeBlockState expectedFluid, VerificationFamily family ) { CavePosition position = witness.position(); @@ -3828,10 +3828,10 @@ public final class HydrologyPackProbe { + ", sampleLayers=" + (debugSample == null ? "null" : debugSample.layers()) + ", layer=" + witness.layer() + "."); } - PlatformBlockState state = generated.blockAt(position.x(), position.y(), position.z()); + NativeBlockState state = generated.blockAt(position.x(), position.y(), position.z()); if (cell.isWet()) { requireMatchingFluid(state, expectedFluid, "cave", false); - PlatformBlockState bed = generated.blockAt( + NativeBlockState bed = generated.blockAt( position.x(), witness.layer().bedY(), position.z() @@ -3960,7 +3960,7 @@ public final class HydrologyPackProbe { } } - private static void requireNonVegetatedBed(PlatformBlockState bed, String label) { + private static void requireNonVegetatedBed(NativeBlockState bed, String label) { String key = stateKey(bed).toLowerCase(Locale.ROOT); if (key.endsWith(":grass_block") || key.endsWith(":moss_block")) { throw new IllegalStateException("Generated " + label + " hydrology rests on " + key + "."); @@ -3984,8 +3984,8 @@ public final class HydrologyPackProbe { } private static void requireMatchingFluid( - PlatformBlockState actual, - PlatformBlockState expected, + NativeBlockState actual, + NativeBlockState expected, String label, boolean allowWaterColumnReplacement ) { @@ -3999,8 +3999,8 @@ public final class HydrologyPackProbe { } static boolean matchesConfiguredFluid( - PlatformBlockState actual, - PlatformBlockState expected, + NativeBlockState actual, + NativeBlockState expected, boolean allowWaterColumnReplacement ) { if (actual == null || expected == null) { @@ -4014,7 +4014,7 @@ public final class HydrologyPackProbe { && (actual.isWaterLogged() || WATER_COLUMN_REPLACEMENTS.contains(materialKey(actual))); } - private static String materialKey(PlatformBlockState state) { + private static String materialKey(NativeBlockState state) { String cached = state.materialKey(); if (cached != null) { return cached; @@ -4024,7 +4024,7 @@ public final class HydrologyPackProbe { return bracket < 0 ? key : key.substring(0, bracket); } - private static String stateKey(PlatformBlockState state) { + private static String stateKey(NativeBlockState state) { return state == null ? "minecraft:air" : state.key(); } diff --git a/probe/src/main/java/art/arcane/iris/probe/RealPackProbeSupport.java b/probe/src/main/java/art/arcane/iris/probe/RealPackProbeSupport.java index b8f821d68..b8c224931 100644 --- a/probe/src/main/java/art/arcane/iris/probe/RealPackProbeSupport.java +++ b/probe/src/main/java/art/arcane/iris/probe/RealPackProbeSupport.java @@ -20,8 +20,8 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.world.IrisWorld; import art.arcane.iris.spi.IrisPlatforms; import art.arcane.iris.spi.IrisServices; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import java.io.File; @@ -147,8 +147,8 @@ final class RealPackProbeSupport { static GeneratedChunk generateChunk(Engine engine, int chunkX, int chunkZ) { int height = engine.getTarget().getHeight(); - Hunk blocks = Hunk.newArrayHunk(16, height, 16); - Hunk biomes = Hunk.newArrayHunk(16, height, 16); + Hunk blocks = Hunk.newArrayHunk(16, height, 16); + Hunk biomes = Hunk.newArrayHunk(16, height, 16); try { engine.generate(chunkX << 4, chunkZ << 4, blocks, biomes, false); } catch (Exception failure) { @@ -161,8 +161,8 @@ final class RealPackProbeSupport { int chunkX, int chunkZ, int height, - Hunk blocks, - Hunk biomes + Hunk blocks, + Hunk biomes ) { GeneratedChunk { if (height < 1 || blocks == null || biomes == null) { @@ -170,12 +170,12 @@ final class RealPackProbeSupport { } } - PlatformBlockState blockAt(int blockX, int y, int blockZ) { + NativeBlockState blockAt(int blockX, int y, int blockZ) { validatePosition(blockX, y, blockZ); return blocks.get(Math.floorMod(blockX, 16), y, Math.floorMod(blockZ, 16)); } - PlatformBiome biomeAt(int blockX, int y, int blockZ) { + NativeBiome biomeAt(int blockX, int y, int blockZ) { validatePosition(blockX, y, blockZ); return biomes.get(Math.floorMod(blockX, 16), y, Math.floorMod(blockZ, 16)); } diff --git a/probe/src/main/java/art/arcane/iris/probe/StubPlatform.java b/probe/src/main/java/art/arcane/iris/probe/StubPlatform.java index 5fed1f3da..7ddba95bf 100644 --- a/probe/src/main/java/art/arcane/iris/probe/StubPlatform.java +++ b/probe/src/main/java/art/arcane/iris/probe/StubPlatform.java @@ -24,16 +24,16 @@ import art.arcane.iris.structure.object.IrisObjectRotation; import art.arcane.iris.generation.block.TileData; import art.arcane.iris.spi.IrisPlatform; import art.arcane.iris.spi.LogLevel; -import art.arcane.iris.spi.PlatformBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; import art.arcane.iris.spi.PlatformBiomeWriter; -import art.arcane.iris.spi.PlatformBlockProperty; -import art.arcane.iris.spi.PlatformBlockState; -import art.arcane.iris.spi.PlatformEntityType; -import art.arcane.iris.spi.PlatformItem; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; +import art.arcane.volmlib.nativelib.entity.NativeEntityType; +import art.arcane.volmlib.nativelib.item.NativeItem; import art.arcane.iris.spi.PlatformRegistries; import art.arcane.iris.spi.PlatformScheduler; import art.arcane.iris.spi.PlatformStructureHooks; -import art.arcane.iris.spi.PlatformWorld; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; import art.arcane.iris.generation.geometry.IrisBlockVector; import java.io.File; @@ -66,15 +66,15 @@ public final class StubPlatform implements IrisPlatform { TileData.bindPlatformFactory(StubTileData::fromProperties); } - static PlatformBlockState rotateForTest(IrisObjectRotation rotation, PlatformBlockState state) { + static NativeBlockState rotateForTest(IrisObjectRotation rotation, NativeBlockState state) { return rotateState(rotation, state, 0, 0, 0); } - static PlatformBlockState mergeForTest(PlatformBlockState base, PlatformBlockState update) { + static NativeBlockState mergeForTest(NativeBlockState base, NativeBlockState update) { return mergeStates(base, update); } - static PlatformBlockState blockStateForTest(String key) { + static NativeBlockState blockStateForTest(String key) { return StubBlockState.of(key); } @@ -92,7 +92,7 @@ public final class StubPlatform implements IrisPlatform { this.dataFolder = dataFolder; } - private static final class StubBlockState implements PlatformBlockState { + private static final class StubBlockState implements NativeBlockState { private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); private final String key; private final String blockKey; @@ -225,7 +225,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public boolean canPlaceOnto(PlatformBlockState onto) { + public boolean canPlaceOnto(NativeBlockState onto) { if (!blockKey.equals("minecraft:sugar_cane")) { return true; } @@ -242,7 +242,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public boolean matches(PlatformBlockState state) { + public boolean matches(NativeBlockState state) { return equals(state); } @@ -252,7 +252,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public PlatformBlockState withProperty(String name, String value) { + public NativeBlockState withProperty(String name, String value) { String propertyName = normalizeProperty(name); String propertyValue = normalizeProperty(value); if (propertyValue.equals(properties.get(propertyName))) { @@ -277,7 +277,7 @@ public final class StubPlatform implements IrisPlatform { } } - private static PlatformBlockState rotateState(IrisObjectRotation rotation, PlatformBlockState state, + private static NativeBlockState rotateState(IrisObjectRotation rotation, NativeBlockState state, int spinX, int spinY, int spinZ) { if (state == null || rotation == null || !rotation.canRotate()) { return state; @@ -311,7 +311,7 @@ public final class StubPlatform implements IrisPlatform { return StubBlockState.of(parsed); } - private static PlatformBlockState mergeStates(PlatformBlockState base, PlatformBlockState update) { + private static NativeBlockState mergeStates(NativeBlockState base, NativeBlockState update) { if (base == null) { return update; } @@ -331,7 +331,7 @@ public final class StubPlatform implements IrisPlatform { return original.equals(parsedBase.properties()) ? base : StubBlockState.of(parsedBase); } - private static ParsedState parsedState(PlatformBlockState state) { + private static ParsedState parsedState(NativeBlockState state) { return state instanceof StubBlockState stub ? stub.parsed() : ParsedState.parse(state.key()); } @@ -486,7 +486,7 @@ public final class StubPlatform implements IrisPlatform { } } - private static final class StubBiome implements PlatformBiome { + private static final class StubBiome implements NativeBiome { private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); private final String key; @@ -522,7 +522,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public void region(PlatformWorld world, int chunkX, int chunkZ, Runnable task) { + public void region(NativeWorld world, int chunkX, int chunkZ, Runnable task) { task.run(); } @@ -536,7 +536,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public void laterRegion(PlatformWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { + public void laterRegion(NativeWorld world, int chunkX, int chunkZ, Runnable task, int ticks) { } } @@ -562,22 +562,22 @@ public final class StubPlatform implements IrisPlatform { } @Override - public List reachableStructureKeys(PlatformWorld world) { + public List reachableStructureKeys(NativeWorld world) { return List.of(); } @Override - public List possibleBiomeKeys(PlatformWorld world) { + public List possibleBiomeKeys(NativeWorld world) { return List.of(); } @Override - public boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed) { + public boolean placeFeature(NativeWorld world, int x, int y, int z, String featureKey, long seed) { return false; } @Override - public int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { + public int[] placeStructure(NativeWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { return null; } @@ -594,12 +594,12 @@ public final class StubPlatform implements IrisPlatform { } @Override - public List allBiomes() { + public List allBiomes() { return List.of(); } } - private record StubEntityType(String key) implements PlatformEntityType { + private record StubEntityType(String key) implements NativeEntityType { @Override public String namespace() { return "minecraft"; @@ -618,42 +618,42 @@ public final class StubPlatform implements IrisPlatform { private static final class StubRegistries implements PlatformRegistries { @Override - public PlatformBlockState block(String key) { + public NativeBlockState block(String key) { return StubBlockState.of(key); } @Override - public PlatformBlockState blockOrNull(String key) { + public NativeBlockState blockOrNull(String key) { return StubBlockState.of(key); } @Override - public PlatformBlockState blockOrNull(String key, boolean warn) { + public NativeBlockState blockOrNull(String key, boolean warn) { return StubBlockState.of(key); } @Override - public PlatformBlockState air() { + public NativeBlockState air() { return StubBlockState.of("minecraft:air"); } @Override - public PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore) { + public NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore) { return ore; } @Override - public PlatformBiome biome(String key) { + public NativeBiome biome(String key) { return StubBiome.of(key); } @Override - public PlatformItem item(String key) { + public NativeItem item(String key) { return null; } @Override - public PlatformEntityType entity(String key) { + public NativeEntityType entity(String key) { return key != null && key.startsWith("minecraft:") ? new StubEntityType(key) : null; } @@ -708,7 +708,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public Map> blockStateProperties() { + public Map> blockStateProperties() { return Map.of(); } } @@ -777,7 +777,7 @@ public final class StubPlatform implements IrisPlatform { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { return false; } diff --git a/probe/src/main/java/art/arcane/iris/probe/StubTileData.java b/probe/src/main/java/art/arcane/iris/probe/StubTileData.java index db1aaf9d2..b77741038 100644 --- a/probe/src/main/java/art/arcane/iris/probe/StubTileData.java +++ b/probe/src/main/java/art/arcane/iris/probe/StubTileData.java @@ -1,7 +1,7 @@ package art.arcane.iris.probe; import art.arcane.iris.generation.block.TileData; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.collection.KMap; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -48,7 +48,7 @@ public final class StubTileData extends TileData { } } - static StubTileData fromProperties(PlatformBlockState state, KMap properties) { + static StubTileData fromProperties(NativeBlockState state, KMap properties) { if (state == null) { throw new IllegalArgumentException("Probe tile data requires a block state"); } diff --git a/probe/src/main/java/art/arcane/iris/probe/Terrain3DProbe.java b/probe/src/main/java/art/arcane/iris/probe/Terrain3DProbe.java index ae3372519..bf8233f4c 100644 --- a/probe/src/main/java/art/arcane/iris/probe/Terrain3DProbe.java +++ b/probe/src/main/java/art/arcane/iris/probe/Terrain3DProbe.java @@ -5,7 +5,7 @@ import art.arcane.iris.generation.stage.IrisTerrainNormalActuator; import art.arcane.iris.generation.runtime.Engine; import art.arcane.iris.generation.biome.IrisBiome; import art.arcane.iris.generation.terrain.Terrain3DColumn; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.generation.context.ChunkContext; import art.arcane.volmlib.util.hunk.Hunk; import com.google.gson.GsonBuilder; @@ -377,7 +377,7 @@ public final class Terrain3DProbe { failReported("before chunk " + chunkX + "," + chunkZ, RealPackProbeSupport.drainReported()); RealPackProbeSupport.GeneratedChunk generated = RealPackProbeSupport.generateChunk(engine, chunkX, chunkZ); int height = generated.height(); - Hunk terrain = Hunk.newArrayHunk(16, height, 16); + Hunk terrain = Hunk.newArrayHunk(16, height, 16); IrisComplex complex = engine.getComplex(); int blockX = chunkX << 4; int blockZ = chunkZ << 4; @@ -400,8 +400,8 @@ public final class Terrain3DProbe { for (int y = 0; y < height; y++) { boolean owned = additionalTerrain && engine.isAdditionalTerrainOwned(worldX, y, worldZ); excluded.set(y, owned); - PlatformBlockState rawBlock = terrain.get(x, y, z); - PlatformBlockState finalBlock = generated.blocks().get(x, y, z); + NativeBlockState rawBlock = terrain.get(x, y, z); + NativeBlockState finalBlock = generated.blocks().get(x, y, z); raw[y] = y <= rootTop && !owned && solid(rawBlock); retained[y] = raw[y] && finalBlock != null && rawBlock.matches(finalBlock); } @@ -422,16 +422,16 @@ public final class Terrain3DProbe { } } - private static ColumnEvidence inspectShapedColumn(Terrain3DColumn column, Hunk terrain, - Hunk generated, int x, int z) { + private static ColumnEvidence inspectShapedColumn(Terrain3DColumn column, Hunk terrain, + Hunk generated, int x, int z) { int height = terrain.getHeight(); boolean[] predicted = new boolean[height]; boolean[] raw = new boolean[height]; boolean[] complete = new boolean[height]; boolean[] retained = new boolean[height]; for (int y = 0; y < height; y++) { - PlatformBlockState rawBlock = terrain.get(x, y, z); - PlatformBlockState finalBlock = generated.get(x, y, z); + NativeBlockState rawBlock = terrain.get(x, y, z); + NativeBlockState finalBlock = generated.get(x, y, z); predicted[y] = column.isSolid(y); raw[y] = solid(rawBlock); complete[y] = finalBlock != null && !finalBlock.isAir(); @@ -440,7 +440,7 @@ public final class Terrain3DProbe { return inspectColumn((int) Math.round(column.baseHeight()), predicted, raw, complete, retained); } - private static boolean solid(PlatformBlockState block) { + private static boolean solid(NativeBlockState block) { return block != null && !block.isAir() && !block.isFluid(); } @@ -680,7 +680,7 @@ public final class Terrain3DProbe { } } - private void column(int worldX, int localZ, Hunk terrain, Hunk generated, + private void column(int worldX, int localZ, Hunk terrain, Hunk generated, int localX, Terrain3DColumn shape, int baseHeight, ColumnEvidence observation) { BufferedImage image = images[localZ]; int pixelX = LEFT + worldX - (configuration.minimumChunkX() << 4); @@ -696,7 +696,7 @@ public final class Terrain3DProbe { } } - private int color(PlatformBlockState block, Terrain3DColumn shape, int baseHeight, int y) { + private int color(NativeBlockState block, Terrain3DColumn shape, int baseHeight, int y) { if (block == null || block.isAir()) { return shape != null && y <= shape.topY() && !shape.isSolid(y) ? 0xd8b8ce : 0xeaf1f7; } diff --git a/probe/src/test/java/art/arcane/iris/probe/DimensionStackSuperflatProbeTest.java b/probe/src/test/java/art/arcane/iris/probe/DimensionStackSuperflatProbeTest.java index 2964f8d50..335a8ed4c 100644 --- a/probe/src/test/java/art/arcane/iris/probe/DimensionStackSuperflatProbeTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/DimensionStackSuperflatProbeTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.probe; import art.arcane.iris.generation.runtime.DimensionStackContext; import art.arcane.iris.generation.runtime.DimensionStackLayout; import art.arcane.iris.generation.runtime.Engine; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.IrisRuntimeState; import org.junit.AfterClass; import org.junit.Test; @@ -410,7 +410,7 @@ public final class DimensionStackSuperflatProbeTest { expected, blockKey(chunk.blockAt(blockX, y, blockZ))); } - private static String blockKey(PlatformBlockState state) { + private static String blockKey(NativeBlockState state) { return state == null || state.isAir() ? AIR : state.key(); } diff --git a/probe/src/test/java/art/arcane/iris/probe/GenerationOrderProbeTest.java b/probe/src/test/java/art/arcane/iris/probe/GenerationOrderProbeTest.java index 2ea6aec09..aa99b349a 100644 --- a/probe/src/test/java/art/arcane/iris/probe/GenerationOrderProbeTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/GenerationOrderProbeTest.java @@ -1,7 +1,7 @@ package art.arcane.iris.probe; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.Test; @@ -117,19 +117,19 @@ public final class GenerationOrderProbeTest { @Test public void completeChunkHashSeparatesBlockAndBiomeChanges() { StubPlatform platform = new StubPlatform(new File("/tmp/iris-order-probe-test")); - PlatformBlockState stone = StubPlatform.blockStateForTest("minecraft:stone"); - PlatformBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=0]"); - PlatformBiome plains = platform.registries().biome("minecraft:plains"); - PlatformBiome forest = platform.registries().biome("minecraft:forest"); - Hunk baselineBlocks = Hunk.newArrayHunk(16, 2, 16); - Hunk baselineBiomes = Hunk.newArrayHunk(16, 2, 16); + NativeBlockState stone = StubPlatform.blockStateForTest("minecraft:stone"); + NativeBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=0]"); + NativeBiome plains = platform.registries().biome("minecraft:plains"); + NativeBiome forest = platform.registries().biome("minecraft:forest"); + Hunk baselineBlocks = Hunk.newArrayHunk(16, 2, 16); + Hunk baselineBiomes = Hunk.newArrayHunk(16, 2, 16); baselineBlocks.set(0, 0, 0, stone); baselineBiomes.set(0, 0, 0, plains); GenerationOrderProbe.ChunkCoordinate coordinate = new GenerationOrderProbe.ChunkCoordinate(0, 0); GenerationOrderProbe.ChunkHash baseline = GenerationOrderProbe.hashChunk( coordinate, baselineBlocks, baselineBiomes, 2); - Hunk changedBiomes = Hunk.newArrayHunk(16, 2, 16); + Hunk changedBiomes = Hunk.newArrayHunk(16, 2, 16); changedBiomes.set(0, 0, 0, forest); GenerationOrderProbe.ChunkHash biomeChange = GenerationOrderProbe.hashChunk( coordinate, baselineBlocks, changedBiomes, 2); @@ -137,7 +137,7 @@ public final class GenerationOrderProbeTest { assertNotEquals(baseline.biomes(), biomeChange.biomes()); assertNotEquals(baseline.combined(), biomeChange.combined()); - Hunk changedBlocks = Hunk.newArrayHunk(16, 2, 16); + Hunk changedBlocks = Hunk.newArrayHunk(16, 2, 16); changedBlocks.set(0, 0, 0, water); GenerationOrderProbe.ChunkHash blockChange = GenerationOrderProbe.hashChunk( coordinate, changedBlocks, baselineBiomes, 2); diff --git a/probe/src/test/java/art/arcane/iris/probe/HydrologyBlockProbeTest.java b/probe/src/test/java/art/arcane/iris/probe/HydrologyBlockProbeTest.java index 4c3b3e752..3fe1dbbf2 100644 --- a/probe/src/test/java/art/arcane/iris/probe/HydrologyBlockProbeTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/HydrologyBlockProbeTest.java @@ -6,8 +6,8 @@ import art.arcane.iris.generation.hydrology.HydrologyFeatureRef; import art.arcane.iris.generation.hydrology.HydrologyFeatureType; import art.arcane.iris.generation.hydrology.RiverFootprint; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.AfterClass; @@ -211,8 +211,8 @@ public final class HydrologyBlockProbeTest { private static Map chunks(int minimumX, int maximumX) { Map chunks = new HashMap<>(); for (int x = minimumX; x <= maximumX; x++) { - Hunk blocks = Hunk.newArrayHunk(16, 16, 16); - Hunk biomes = Hunk.newArrayHunk(16, 16, 16); + Hunk blocks = Hunk.newArrayHunk(16, 16, 16); + Hunk biomes = Hunk.newArrayHunk(16, 16, 16); chunks.put(RiverFootprint.pack(x, 0), new RealPackProbeSupport.GeneratedChunk(x, 0, 16, blocks, biomes)); } return chunks; @@ -223,7 +223,7 @@ public final class HydrologyBlockProbeTest { .blocks().set(Math.floorMod(x, 16), y, Math.floorMod(z, 16), state(key)); } - private static PlatformBlockState state(String key) { + private static NativeBlockState state(String key) { return IrisPlatforms.get().registries().block("minecraft:" + key); } diff --git a/probe/src/test/java/art/arcane/iris/probe/HydrologyPackProbeTest.java b/probe/src/test/java/art/arcane/iris/probe/HydrologyPackProbeTest.java index 16ce0f889..baf9640ff 100644 --- a/probe/src/test/java/art/arcane/iris/probe/HydrologyPackProbeTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/HydrologyPackProbeTest.java @@ -19,8 +19,8 @@ import art.arcane.iris.generation.terrain.IrisDimension; import art.arcane.iris.pack.loading.IrisData; import art.arcane.iris.world.history.GenerationRegistryContractFactory; import art.arcane.volmlib.util.collection.KList; -import art.arcane.iris.spi.PlatformBiome; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.Rule; import org.junit.Test; @@ -182,10 +182,10 @@ public final class HydrologyPackProbeTest { @Test public void generatedChunkMapsNegativeWorldCoordinatesToLocalBuffers() { StubPlatform platform = new StubPlatform(new File("/tmp/iris-hydrology-pack-probe-test")); - PlatformBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=8]"); - PlatformBiome biome = platform.registries().biome("minecraft:river"); - Hunk blocks = Hunk.newArrayHunk(16, 2, 16); - Hunk biomes = Hunk.newArrayHunk(16, 2, 16); + NativeBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=8]"); + NativeBiome biome = platform.registries().biome("minecraft:river"); + Hunk blocks = Hunk.newArrayHunk(16, 2, 16); + Hunk biomes = Hunk.newArrayHunk(16, 2, 16); blocks.set(15, 1, 15, water); biomes.set(15, 1, 15, biome); RealPackProbeSupport.GeneratedChunk chunk = new RealPackProbeSupport.GeneratedChunk( @@ -199,12 +199,12 @@ public final class HydrologyPackProbeTest { @Test public void generatedSurfaceFluidAcceptsOnlyWaterBearingDecorantsForWaterProfiles() { - PlatformBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=0]"); - PlatformBlockState lava = StubPlatform.blockStateForTest("minecraft:lava[level=0]"); - PlatformBlockState kelp = StubPlatform.blockStateForTest("minecraft:kelp_plant"); - PlatformBlockState coral = StubPlatform.blockStateForTest( + NativeBlockState water = StubPlatform.blockStateForTest("minecraft:water[level=0]"); + NativeBlockState lava = StubPlatform.blockStateForTest("minecraft:lava[level=0]"); + NativeBlockState kelp = StubPlatform.blockStateForTest("minecraft:kelp_plant"); + NativeBlockState coral = StubPlatform.blockStateForTest( "minecraft:brain_coral_fan[waterlogged=true]"); - PlatformBlockState stone = StubPlatform.blockStateForTest("minecraft:stone"); + NativeBlockState stone = StubPlatform.blockStateForTest("minecraft:stone"); assertTrue(HydrologyPackProbe.matchesConfiguredFluid(water, water, false)); assertTrue(HydrologyPackProbe.matchesConfiguredFluid(kelp, water, true)); diff --git a/probe/src/test/java/art/arcane/iris/probe/StubPlatformStateTest.java b/probe/src/test/java/art/arcane/iris/probe/StubPlatformStateTest.java index 8e248806b..2f5bf9034 100644 --- a/probe/src/test/java/art/arcane/iris/probe/StubPlatformStateTest.java +++ b/probe/src/test/java/art/arcane/iris/probe/StubPlatformStateTest.java @@ -3,7 +3,7 @@ package art.arcane.iris.probe; import art.arcane.iris.generation.decoration.IrisSpeleothems; import art.arcane.iris.structure.object.IrisObjectRotation; import art.arcane.iris.spi.IrisPlatforms; -import art.arcane.iris.spi.PlatformBlockState; +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; import art.arcane.iris.testsupport.PlatformLeakGuard; import art.arcane.volmlib.util.hunk.Hunk; import org.junit.AfterClass; @@ -46,20 +46,20 @@ public final class StubPlatformStateTest { @Test public void propertyUpdatesAndMergesPreserveCanonicalState() { - PlatformBlockState base = state("minecraft:oak_leaves[distance=7,persistent=false]"); - PlatformBlockState updated = base.withProperty("distance", "2").withProperty("persistent", "true"); + NativeBlockState base = state("minecraft:oak_leaves[distance=7,persistent=false]"); + NativeBlockState updated = base.withProperty("distance", "2").withProperty("persistent", "true"); assertEquals("minecraft:oak_leaves[distance=2,persistent=true]", updated.key()); - PlatformBlockState merged = StubPlatform.mergeForTest( + NativeBlockState merged = StubPlatform.mergeForTest( base, state("minecraft:oak_leaves[persistent=true]")); assertEquals("minecraft:oak_leaves[distance=7,persistent=true]", merged.key()); } @Test public void noOpStateTransformsReuseTheCanonicalInstance() { - PlatformBlockState stone = state("minecraft:stone"); - PlatformBlockState leaves = state("minecraft:oak_leaves[distance=7,persistent=false]"); + NativeBlockState stone = state("minecraft:stone"); + NativeBlockState leaves = state("minecraft:oak_leaves[distance=7,persistent=false]"); assertSame(stone, StubPlatform.rotateForTest(IrisObjectRotation.of(0, 90, 0), stone)); assertSame(leaves, leaves.withProperty("distance", "7")); @@ -71,9 +71,9 @@ public final class StubPlatformStateTest { @Test public void classifiesVanillaFluidStates() { - PlatformBlockState water = state("minecraft:water[level=0]"); - PlatformBlockState lava = state("minecraft:lava[level=0]"); - PlatformBlockState stone = state("minecraft:stone"); + NativeBlockState water = state("minecraft:water[level=0]"); + NativeBlockState lava = state("minecraft:lava[level=0]"); + NativeBlockState stone = state("minecraft:stone"); assertTrue(water.isFluid()); assertTrue(water.isWater()); @@ -94,8 +94,8 @@ public final class StubPlatformStateTest { @Test public void resolvesSpikeSupportWithoutServerClasses() { - Hunk blocks = Hunk.newArrayHunk(1, 3, 1); - PlatformBlockState spike = state("minecraft:pointed_dripstone[vertical_direction=up,thickness=tip]"); + Hunk blocks = Hunk.newArrayHunk(1, 3, 1); + NativeBlockState spike = state("minecraft:pointed_dripstone[vertical_direction=up,thickness=tip]"); blocks.set(0, 0, 0, state("minecraft:stone")); assertTrue(IrisSpeleothems.isSupported(spike, blocks, 0, 0, 1)); blocks.set(0, 0, 0, state("minecraft:water[level=0]")); @@ -106,7 +106,7 @@ public final class StubPlatformStateTest { @Test public void sugarCaneUsesNativeSubstratesInGeneratedChunkProbes() { - PlatformBlockState cane = state("minecraft:sugar_cane[age=0]"); + NativeBlockState cane = state("minecraft:sugar_cane[age=0]"); for (String substrate : new String[]{"sugar_cane", "grass_block", "dirt", "coarse_dirt", "podzol", "mycelium", "rooted_dirt", "moss_block", "pale_moss_block", "mud", "muddy_mangrove_roots", "sand", "red_sand", "suspicious_sand"}) { @@ -117,7 +117,7 @@ public final class StubPlatformStateTest { } } - private PlatformBlockState state(String key) { + private NativeBlockState state(String key) { return IrisPlatforms.get().registries().block(key); } diff --git a/spi/build.gradle b/spi/build.gradle index 39b8096ca..cfd4f6732 100644 --- a/spi/build.gradle +++ b/spi/build.gradle @@ -12,6 +12,9 @@ java { } dependencies { + api("com.github.VolmitSoftware.VolmLib:native-api:${providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last()}") { + transitive = false + } testImplementation('junit:junit:4.13.2') } diff --git a/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java b/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java index a928e9eaf..1fa643441 100644 --- a/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java +++ b/spi/src/main/java/art/arcane/iris/spi/IrisPlatform.java @@ -18,6 +18,8 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import java.io.File; /** @@ -27,7 +29,7 @@ import java.io.File; * ({@link #registries()}, {@link #scheduler()}, {@link #structureHooks()}, {@link #biomeWriter()}, the * version and path methods) are called from generation threads and must not block on the server thread. * The mutating methods ({@link #callEvent(Object)}, {@link #dispatchConsoleCommand(String)}, - * {@link #spawnEntity(PlatformWorld, String, double, double, double)}) touch live server state and are + * {@link #spawnEntity(NativeWorld, String, double, double, double)}) touch live server state and are * expected to be invoked on the thread that owns it - the global/server thread, or the region thread * owning the target chunk on regionized platforms. Use {@link #scheduler()} to get there. *

@@ -179,14 +181,14 @@ public interface IrisPlatform { /** * Spawns a vanilla entity by namespaced key at the given block-space position. *

- * Adapters unwrap {@link PlatformWorld#nativeHandle()} to reach the host world, so {@code world} must - * be a {@link PlatformWorld} produced by the active adapter. Returns false - never throws - when + * Adapters unwrap {@link NativeWorld#nativeHandle()} to reach the host world, so {@code world} must + * be a {@link NativeWorld} produced by the active adapter. Returns false - never throws - when * {@code world} or {@code entityKey} is null, the world belongs to a different adapter, the key does * not parse, the entity type is unknown, or the platform refuses the spawn. *

* Invoke on the thread owning the target chunk. */ - boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z); + boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z); /** * Routes a log line to the host logger at {@code level}. Safe from any thread. Prefer diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformBiome.java b/spi/src/main/java/art/arcane/iris/spi/PlatformBiome.java deleted file mode 100644 index 6f7d44e55..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformBiome.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Neutral handle for a resolved biome backed by an adapter-owned native handle. - *

- * Immutable and safe to share across threads. Internal to Iris; not a published integration surface. - * - * @see PlatformRegistries#biome(String) - */ -public interface PlatformBiome { - /** - * Canonical {@code namespace:path} biome key. Never null. - */ - String key(); - - /** - * Namespace half of {@link #key()}. Never null. - */ - String namespace(); - - /** - * The adapter's backing biome object - {@code org.bukkit.block.Biome} on Bukkit, a {@code Biome} registry - * value on a mod loader. Never null. Only code inside the owning adapter may cast it. - */ - Object nativeHandle(); -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformBiomeWriter.java b/spi/src/main/java/art/arcane/iris/spi/PlatformBiomeWriter.java index 58f5455fe..9238f6316 100644 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformBiomeWriter.java +++ b/spi/src/main/java/art/arcane/iris/spi/PlatformBiomeWriter.java @@ -18,6 +18,8 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeBiome; + import java.util.List; /** @@ -42,5 +44,5 @@ public interface PlatformBiomeWriter { /** * Every biome in the host registry, including datapack and mod biomes. Never null. */ - List allBiomes(); + List allBiomes(); } diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformBlockProperty.java b/spi/src/main/java/art/arcane/iris/spi/PlatformBlockProperty.java deleted file mode 100644 index 098a8658d..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformBlockProperty.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -import java.util.List; - -/** - * One block state property as JSON schema vocabulary, so pack schema generation can describe block keys without - * knowing platform property types. - *

- * Immutable. Produced by {@link PlatformRegistries#blockStateProperties()} and consumed only by schema - * generation, never on the generation path. Internal to Iris; not a published integration surface. - * - * @param name the property name as it appears in a block key, for example {@code waterlogged} - * @param jsonType JSON schema type: {@code boolean}, {@code integer} or {@code string}. Never null or empty - * @param defaultValue the value the host's default state carries, boxed as its JSON representation - * @param allowedValues every legal value, empty when the adapter cannot enumerate them. Never null - * @param numericRange bounds for a numeric property, null for {@code boolean} and {@code string} - */ -public record PlatformBlockProperty(String name, String jsonType, Object defaultValue, List allowedValues, PlatformNumericRange numericRange) { - /** - * Whether {@link #numericRange()} is present, and therefore whether schema output should emit bounds. - */ - public boolean hasNumericRange() { - return numericRange != null; - } -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformBlockState.java b/spi/src/main/java/art/arcane/iris/spi/PlatformBlockState.java deleted file mode 100644 index 23361e451..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformBlockState.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Neutral handle for a resolved block state; the canonical key is the config currency and the native handle is adapter-owned. - *

- * Instances are immutable value handles, interned by the adapter, and read from generation threads in bulk - - * every predicate here must be a cached field read or cheap computation, never a registry or world lookup. - * Compare with {@link #matches(PlatformBlockState)} rather than {@code equals}; only interned singletons such as - * {@link PlatformRegistries#air()} are safe to compare by identity. - *

- * Internal to Iris; not a published integration surface. - * - * @see PlatformRegistries#block(String) - */ -public interface PlatformBlockState { - /** - * Canonical {@code namespace:path[prop=value,...]} key, the form packs and objects store. Never null. - */ - String key(); - - /** - * Namespace half of {@link #key()}, for example {@code minecraft}. Never null. - */ - String namespace(); - - /** - * The key with any property block stripped, memoized by implementors that intern their states. - * Returning null means "not memoized"; callers must then derive it from {@link #key()}. - */ - default String materialKey() { - return null; - } - - /** - * Whether this is air, including cave and void air. - */ - boolean isAir(); - - /** - * Whether the host treats this as a solid collision block. - */ - boolean isSolid(); - - /** - * Whether this blocks light fully. Drives Iris's own light and surface reasoning. - */ - boolean isOccluding(); - - /** - * Whether this state came from a custom-content provider rather than a vanilla registry entry. Custom - * states carry a {@link #deferredPlacementKey()} and a {@link #placementBaseState()}. - */ - boolean isCustom(); - - /** - * For a custom state, the provider key to hand back after the block is written, so the provider can finish - * placement once the chunk exists. Null for ordinary states. - */ - default String deferredPlacementKey() { - return null; - } - - /** - * The vanilla state to actually write for a custom state - the placeholder the provider later replaces. - * Returns {@code this} for ordinary states. Never null. - */ - default PlatformBlockState placementBaseState() { - return this; - } - - /** - * Whether this is a fluid block, water or lava. - */ - boolean isFluid(); - - /** - * Whether this is water specifically. - */ - boolean isWater(); - - /** - * Whether this state carries a {@code waterlogged=true} property. - */ - boolean isWaterLogged(); - - /** - * Whether this state carries a {@code lit=true} property. - */ - boolean isLit(); - - /** - * Whether the host needs a block update after this is placed - stairs, fences, redstone and other states - * that resolve their shape from neighbours. - */ - boolean isUpdatable(); - - /** - * Whether this is plant foliage: grass, ferns, flowers and similar decoration. - */ - boolean isFoliage(); - - /** - * Whether this is part of a tree - tagged as a log or as leaves. - */ - boolean isTreeBlock(); - - /** - * Whether foliage can be planted on top of this block. - */ - boolean isFoliagePlantable(); - - /** - * Whether this is a decorant: a thin block that sits on a surface and cannot support anything. - */ - boolean isDecorant(); - - /** - * Whether this block has an inventory Iris can fill from a loot table. - */ - boolean isStorage(); - - /** - * Whether this is specifically a chest, which needs the pairing and orientation handling chests require. - */ - boolean isStorageChest(); - - /** - * Whether this is an ore block, and therefore a candidate for - * {@link PlatformRegistries#deepSlateOre(PlatformBlockState, PlatformBlockState)}. - */ - boolean isOre(); - - /** - * Whether this is deepslate or a deepslate variant. - */ - boolean isDeepSlate(); - - /** - * Whether this is a vine-like block that attaches to a face and hangs. - */ - boolean isVineBlock(); - - /** - * Whether this block can be placed onto {@code onto} - the support check Iris runs before writing - * decoration. Passing a state from a different adapter fails at runtime. - */ - boolean canPlaceOnto(PlatformBlockState onto); - - /** - * Whether the two states are the same block with the same properties. Prefer this to {@code equals}, which - * is identity-based on some adapters. - */ - boolean matches(PlatformBlockState state); - - /** - * Convenience for the common "nothing solid here" test. - */ - default boolean isAirOrFluid() { - return isAir() || isFluid(); - } - - /** - * Whether the host attaches a block entity to this block - signs, chests, spawners and similar. Such blocks - * need their tile data applied after the block write. - */ - boolean hasTileEntity(); - - /** - * This state with one property overridden, merged into {@link #key()} and re-resolved. Never mutates the - * receiver. - *

- * Strict: a property name or value the block does not accept throws rather than being dropped. Validate - * against {@link PlatformRegistries#blockStateProperties()} first, or resolve the full key through - * {@link PlatformRegistries#blockOrNull(String)} instead. - */ - PlatformBlockState withProperty(String name, String value); - - /** - * The adapter's backing state object - {@code org.bukkit.block.data.BlockData} on Bukkit, - * {@code BlockState} on a mod loader. Never null. Only code inside the owning adapter may cast it. - */ - Object nativeHandle(); -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformEntityType.java b/spi/src/main/java/art/arcane/iris/spi/PlatformEntityType.java deleted file mode 100644 index 939f2fcd4..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformEntityType.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Neutral handle for a resolved entity type backed by an adapter-owned native handle. - *

- * Immutable and safe to share across threads. Internal to Iris; not a published integration surface. - * - * @see PlatformRegistries#entity(String) - */ -public interface PlatformEntityType { - /** - * Canonical {@code namespace:path} entity type key. Never null. - */ - String key(); - - /** - * Namespace half of {@link #key()}. Never null. - */ - String namespace(); - - /** - * The host's spawn category, lowercased - {@code monster}, {@code creature}, {@code ambient} and so on. - * Iris matches it against pack spawn rules, so the string form is the contract rather than any enum. Never - * null. - */ - String spawnCategory(); - - /** - * The adapter's backing entity type object - {@code org.bukkit.entity.EntityType} on Bukkit, - * {@code EntityType} on a mod loader. Never null. Only code inside the owning adapter may cast it. - */ - Object nativeHandle(); -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java b/spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java index 7ae4bf70b..1a5241fb5 100644 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java +++ b/spi/src/main/java/art/arcane/iris/spi/PlatformGenerationRegistry.java @@ -1,10 +1,12 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeGenerationRegistry; + import java.util.List; import java.util.Objects; import java.util.regex.Pattern; -public interface PlatformGenerationRegistry { +public interface PlatformGenerationRegistry extends NativeGenerationRegistry { Pattern SHA_256_PATTERN = Pattern.compile("[0-9a-f]{64}"); String runtimeIdentity(); @@ -25,16 +27,6 @@ public interface PlatformGenerationRegistry { String dimensionTypeResourceKey(String packName, String dimensionKey, String dimensionTypeKey); - Definition definition(String registryKey, String resourceKey); - - default Definition generatedDefinition(String registryKey, String resourceKey) { - return definition(registryKey, resourceKey); - } - - default Definition canonicalDefinition(String registryKey, String resourceKey, String sourceJson) { - return Definition.exactJson(sourceJson); - } - static String contentAddressedCustomBiomeResourceKey(String identitySha256) { String requiredFingerprint = Objects.requireNonNull(identitySha256, "identitySha256"); if (!SHA_256_PATTERN.matcher(requiredFingerprint).matches()) { @@ -43,42 +35,4 @@ public interface PlatformGenerationRegistry { return "iris:biomes/" + requiredFingerprint; } - enum Encoding { - JSON, - CONSERVATIVE_IDENTITY - } - - record Definition(Encoding encoding, String value) { - public Definition { - encoding = Objects.requireNonNull(encoding, "encoding"); - value = Objects.requireNonNull(value, "value"); - if (value.isBlank()) { - throw new IllegalArgumentException("Platform registry definition must not be blank."); - } - } - - public static Definition exactJson(String json) { - return new Definition(Encoding.JSON, json); - } - - public static Definition conservativeIdentity(String identity) { - return new Definition(Encoding.CONSERVATIVE_IDENTITY, identity); - } - - public static Definition resourceIdentity(String registryKey, String resourceKey) { - String requiredRegistryKey = requireText(registryKey, "registryKey"); - String requiredResourceKey = requireText(resourceKey, "resourceKey"); - return conservativeIdentity( - "registry-resource-key-v1|" + requiredRegistryKey + "|" + requiredResourceKey - ); - } - - private static String requireText(String value, String label) { - String required = Objects.requireNonNull(value, label).trim(); - if (required.isEmpty()) { - throw new IllegalArgumentException(label + " must not be blank."); - } - return required; - } - } } diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformItem.java b/spi/src/main/java/art/arcane/iris/spi/PlatformItem.java deleted file mode 100644 index ff86970c6..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformItem.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Neutral handle for a resolved item type backed by an adapter-owned native handle. - *

- * Describes an item type, not a stack - no count, no components. Immutable and safe to share across threads. - * Internal to Iris; not a published integration surface. - * - * @see PlatformRegistries#item(String) - */ -public interface PlatformItem { - /** - * Canonical {@code namespace:path} item key. Never null. - */ - String key(); - - /** - * Namespace half of {@link #key()}. Never null. - */ - String namespace(); - - /** - * The adapter's backing item object - {@code org.bukkit.Material} on Bukkit, an {@code Item} registry value - * on a mod loader. Never null. Only code inside the owning adapter may cast it. - */ - Object nativeHandle(); -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformNumericRange.java b/spi/src/main/java/art/arcane/iris/spi/PlatformNumericRange.java deleted file mode 100644 index 23cedab4e..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformNumericRange.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Inclusive-or-exclusive numeric bounds for a {@link PlatformBlockProperty}, mirroring JSON schema's - * {@code minimum}/{@code maximum} pair. - *

- * Immutable. Bounds are carried as {@code double} regardless of the property's JSON type; an - * {@code integer} property's bounds are whole numbers and are narrowed by the schema writer. Internal to Iris; - * not a published integration surface. - * - * @param minimum lower bound - * @param maximum upper bound - * @param exclusiveMinimum whether {@code minimum} itself is disallowed - * @param exclusiveMaximum whether {@code maximum} itself is disallowed - */ -public record PlatformNumericRange(double minimum, double maximum, boolean exclusiveMinimum, boolean exclusiveMaximum) { -} diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformRegistries.java b/spi/src/main/java/art/arcane/iris/spi/PlatformRegistries.java index 2cb954418..08a0d49dc 100644 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformRegistries.java +++ b/spi/src/main/java/art/arcane/iris/spi/PlatformRegistries.java @@ -18,6 +18,16 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; + +import art.arcane.volmlib.nativelib.entity.NativeEntityType; + +import art.arcane.volmlib.nativelib.item.NativeItem; + +import art.arcane.volmlib.nativelib.terrain.NativeBiome; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockState; + import java.util.List; import java.util.Map; @@ -43,7 +53,7 @@ public interface PlatformRegistries { * falls back to air; a key that cannot be parsed at all may come back null. Callers that need to tell * absence from air use {@link #blockOrNull(String)}. */ - PlatformBlockState block(String key); + NativeBlockState block(String key); /** * Resolves a block key, returning null instead of an air fallback when it does not resolve. Silent. @@ -52,40 +62,40 @@ public interface PlatformRegistries { * sees keys the underlying lookup could not answer at all, so an unregistered key resolves to air through * {@link #block(String)} on every platform - the Bukkit-only legacy rewrite table cannot fork generation output. */ - PlatformBlockState blockOrNull(String key); + NativeBlockState blockOrNull(String key); /** * {@link #blockOrNull(String)} with control over whether an unresolved key is logged. Pass * {@code warn = false} for speculative lookups. */ - PlatformBlockState blockOrNull(String key, boolean warn); + NativeBlockState blockOrNull(String key, boolean warn); /** * The interned air state. Never null; identity-comparable across calls. */ - PlatformBlockState air(); + NativeBlockState air(); /** * The deepslate variant of {@code ore} when {@code block} is deepslate, otherwise {@code ore} unchanged. * Lets ore placement follow the host stone without the pack enumerating variants. */ - PlatformBlockState deepSlateOre(PlatformBlockState block, PlatformBlockState ore); + NativeBlockState deepSlateOre(NativeBlockState block, NativeBlockState ore); /** * Resolves a biome key against the live biome registry, including datapack and mod biomes. Null when the * key does not parse or is not registered. */ - PlatformBiome biome(String key); + NativeBiome biome(String key); /** * Resolves an item key. Null when unknown. */ - PlatformItem item(String key); + NativeItem item(String key); /** * Resolves an entity type key. Null when unknown. */ - PlatformEntityType entity(String key); + NativeEntityType entity(String key); /** * Every registered block state key, properties included. Drives schema completion and command @@ -147,5 +157,5 @@ public interface PlatformRegistries { * Block key to its declared state properties, used to generate pack schema enums and numeric ranges. * Keyed by material-level block key. Never null. */ - Map> blockStateProperties(); + Map> blockStateProperties(); } diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformScheduler.java b/spi/src/main/java/art/arcane/iris/spi/PlatformScheduler.java index 2f1c25ffa..61e6cea23 100644 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformScheduler.java +++ b/spi/src/main/java/art/arcane/iris/spi/PlatformScheduler.java @@ -18,6 +18,8 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + /** * Platform task dispatch; region scheduling targets the owning region thread on regionized platforms and the global thread elsewhere. *

@@ -42,7 +44,7 @@ public interface PlatformScheduler { * touches blocks or entities in a known chunk. Adapters that cannot resolve a region owner fall back to * {@link #global(Runnable)}. */ - void region(PlatformWorld world, int chunkX, int chunkZ, Runnable task); + void region(NativeWorld world, int chunkX, int chunkZ, Runnable task); /** * Runs {@code task} on a pooled thread off the server thread. Must not touch world state. @@ -56,9 +58,9 @@ public interface PlatformScheduler { void laterGlobal(Runnable task, int ticks); /** - * {@link #region(PlatformWorld, int, int, Runnable)} delayed by {@code ticks}, with the same non-positive-delay + * {@link #region(NativeWorld, int, int, Runnable)} delayed by {@code ticks}, with the same non-positive-delay * degradation as {@link #laterGlobal(Runnable, int)}. Adapters without regions fall back to the delayed global * queue. */ - void laterRegion(PlatformWorld world, int chunkX, int chunkZ, Runnable task, int ticks); + void laterRegion(NativeWorld world, int chunkX, int chunkZ, Runnable task, int ticks); } diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformStructureHooks.java b/spi/src/main/java/art/arcane/iris/spi/PlatformStructureHooks.java index ff5dfa9e3..0938f80d8 100644 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformStructureHooks.java +++ b/spi/src/main/java/art/arcane/iris/spi/PlatformStructureHooks.java @@ -18,7 +18,10 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import java.util.List; +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; /** * Neutral access to the host platform's structure, structure-set and configured-feature registries plus placement entry points. @@ -84,18 +87,18 @@ public interface PlatformStructureHooks { * Structure keys actually reachable in {@code world}, after its dimension's structure sets and biome filter * are applied. Narrower than {@link #structureKeys()}. Never null. */ - List reachableStructureKeys(PlatformWorld world); + List reachableStructureKeys(NativeWorld world); /** * Biome keys the world's biome source can emit. Never null. */ - List possibleBiomeKeys(PlatformWorld world); + List possibleBiomeKeys(NativeWorld world); /** * Places a configured feature at world coordinates with the given seed. Returns false when the key is * unknown or the feature declines to place. Mutates the world; see the threading note on this interface. */ - boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed); + boolean placeFeature(NativeWorld world, int x, int y, int z, String featureKey, long seed); /** * Generates and places a structure anchored at the given chunk. @@ -104,30 +107,13 @@ public interface PlatformStructureHooks { * @return the placed bounding box as {@code {minX, minY, minZ, maxX, maxY, maxZ}}, or null when the key is * unknown, the structure produced no valid start, or the box exceeded {@code maxSpan} */ - int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan); + int[] placeStructure(NativeWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan); /** - * Whether this adapter implements {@link #placeStructure(PlatformWorld, int, int, String, long, int)}. + * Whether this adapter implements {@link #placeStructure(NativeWorld, int, int, String, long, int)}. * Check before offering structure capture; adapters without the required host access return false. */ boolean supportsStructurePlacement(); - record JigsawSourceMetadata(int maxDistanceHorizontal, int referenceExpansion, - int maxStartElementHorizontalSpan) { - public JigsawSourceMetadata(int maxDistanceHorizontal, int referenceExpansion) { - this(maxDistanceHorizontal, referenceExpansion, 0); - } - public JigsawSourceMetadata { - if (maxDistanceHorizontal < 1 || maxDistanceHorizontal > 128) { - throw new IllegalArgumentException("Jigsaw horizontal distance must be between 1 and 128"); - } - if (referenceExpansion < 0) { - throw new IllegalArgumentException("Jigsaw reference expansion must not be negative"); - } - if (maxStartElementHorizontalSpan < 0) { - throw new IllegalArgumentException("Jigsaw start element horizontal span must not be negative"); - } - } - } } diff --git a/spi/src/main/java/art/arcane/iris/spi/PlatformWorld.java b/spi/src/main/java/art/arcane/iris/spi/PlatformWorld.java deleted file mode 100644 index e72f33c44..000000000 --- a/spi/src/main/java/art/arcane/iris/spi/PlatformWorld.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Iris is a World Generator for Minecraft Bukkit Servers - * Copyright (c) 2026 Arcane Arts (Volmit Software) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package art.arcane.iris.spi; - -/** - * Neutral view of a loaded world for edit and lifecycle paths; never used on the generation hot path. - *

- * Metadata reads ({@link #name()}, {@link #seed()}, the height bounds) are cheap and safe from any thread. The - * block, biome and weather accessors read or mutate live world state and must be called on the thread that - * owns the target chunk - the region thread on regionized platforms, the server thread elsewhere. Reach that - * thread with {@link PlatformScheduler#region(PlatformWorld, int, int, Runnable)}. - *

- * Internal to Iris; not a published integration surface. - */ -public interface PlatformWorld { - /** - * The host's world name. Never null. - */ - String name(); - - /** - * The world seed, as the host reports it. - */ - long seed(); - - /** - * Lowest buildable Y, inclusive. Usually negative. - */ - int minHeight(); - - /** - * Highest buildable Y, exclusive. - */ - int maxHeight(); - - /** - * The block state at world coordinates. Loads the chunk if it is not resident, so treat it as blocking. - * Adapters delegate straight to the host, so a coordinate outside {@link #minHeight()}/{@link #maxHeight()} - * gets whatever the host does with it - clamp, air, or throw. Bounds-check first. - */ - PlatformBlockState getBlock(int x, int y, int z); - - /** - * Writes a block state at world coordinates. Same out-of-bounds caveat as {@link #getBlock(int, int, int)}. - * - * @param flags platform update flags; bit 0 requests neighbour/physics updates, higher bits are - * adapter-specific. Pass 0 for a silent write - */ - void setBlock(int x, int y, int z, PlatformBlockState block, int flags); - - /** - * The biome at world coordinates. Loads the chunk if it is not resident. - */ - PlatformBiome getBiome(int x, int y, int z); - - /** - * Whether the chunk is currently resident. Cheap; the only accessor here that never triggers a load. - */ - boolean isChunkLoaded(int chunkX, int chunkZ); - - /** - * The world's time of day in ticks. - */ - long getTime(); - - /** - * Whether it is raining or snowing. - */ - boolean isStorming(); - - /** - * Whether a thunderstorm is active. - */ - boolean isThundering(); - - /** - * The adapter's backing world object - {@code org.bukkit.World} on Bukkit, {@code ServerLevel} on a mod - * loader. Never null. Only code inside the owning adapter may cast it; core must not. - */ - Object nativeHandle(); -} diff --git a/spi/src/test/java/art/arcane/iris/spi/IrisPlatformsBindContractTest.java b/spi/src/test/java/art/arcane/iris/spi/IrisPlatformsBindContractTest.java index 4a34bced2..db42bc4d2 100644 --- a/spi/src/test/java/art/arcane/iris/spi/IrisPlatformsBindContractTest.java +++ b/spi/src/test/java/art/arcane/iris/spi/IrisPlatformsBindContractTest.java @@ -1,5 +1,7 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -153,7 +155,7 @@ public class IrisPlatformsBindContractTest { } @Override - public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) { + public boolean spawnEntity(NativeWorld world, String entityKey, double x, double y, double z) { return false; } diff --git a/spi/src/test/java/art/arcane/iris/spi/PlatformValueContractTest.java b/spi/src/test/java/art/arcane/iris/spi/PlatformValueContractTest.java index 52e24a3a0..499c7d73d 100644 --- a/spi/src/test/java/art/arcane/iris/spi/PlatformValueContractTest.java +++ b/spi/src/test/java/art/arcane/iris/spi/PlatformValueContractTest.java @@ -1,5 +1,13 @@ package art.arcane.iris.spi; +import art.arcane.volmlib.nativelib.terrain.NativeWorld; + +import art.arcane.volmlib.nativelib.terrain.NativeNumericRange; + +import art.arcane.volmlib.nativelib.terrain.NativeBlockProperty; + +import art.arcane.volmlib.nativelib.terrain.JigsawSourceMetadata; + import org.junit.Test; import java.util.List; @@ -14,10 +22,10 @@ public class PlatformValueContractTest { @Test public void jigsawMetadataAcceptsTheWholeDocumentedRange() { - PlatformStructureHooks.JigsawSourceMetadata smallest = - new PlatformStructureHooks.JigsawSourceMetadata(1, 0, 0); - PlatformStructureHooks.JigsawSourceMetadata largest = - new PlatformStructureHooks.JigsawSourceMetadata(128, 7, 96); + JigsawSourceMetadata smallest = + new JigsawSourceMetadata(1, 0, 0); + JigsawSourceMetadata largest = + new JigsawSourceMetadata(128, 7, 96); assertEquals(1, smallest.maxDistanceHorizontal()); assertEquals(128, largest.maxDistanceHorizontal()); @@ -27,8 +35,8 @@ public class PlatformValueContractTest { @Test public void jigsawMetadataDefaultsTheStartElementSpanToZero() { - PlatformStructureHooks.JigsawSourceMetadata metadata = - new PlatformStructureHooks.JigsawSourceMetadata(64, 3); + JigsawSourceMetadata metadata = + new JigsawSourceMetadata(64, 3); assertEquals(0, metadata.maxStartElementHorizontalSpan()); } @@ -36,13 +44,13 @@ public class PlatformValueContractTest { @Test public void jigsawMetadataRejectsOutOfRangeGeometry() { assertThrows(IllegalArgumentException.class, - () -> new PlatformStructureHooks.JigsawSourceMetadata(0, 0, 0)); + () -> new JigsawSourceMetadata(0, 0, 0)); assertThrows(IllegalArgumentException.class, - () -> new PlatformStructureHooks.JigsawSourceMetadata(129, 0, 0)); + () -> new JigsawSourceMetadata(129, 0, 0)); assertThrows(IllegalArgumentException.class, - () -> new PlatformStructureHooks.JigsawSourceMetadata(64, -1, 0)); + () -> new JigsawSourceMetadata(64, -1, 0)); assertThrows(IllegalArgumentException.class, - () -> new PlatformStructureHooks.JigsawSourceMetadata(64, 0, -1)); + () -> new JigsawSourceMetadata(64, 0, -1)); } @Test @@ -112,11 +120,11 @@ public class PlatformValueContractTest { @Test public void blockPropertiesReportWhetherTheyCarryANumericRange() { - PlatformNumericRange range = new PlatformNumericRange(0D, 15D, false, true); - PlatformBlockProperty numeric = - new PlatformBlockProperty("level", "integer", 0, List.of(0, 15), range); - PlatformBlockProperty enumerated = - new PlatformBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null); + NativeNumericRange range = new NativeNumericRange(0D, 15D, false, true); + NativeBlockProperty numeric = + new NativeBlockProperty("level", "integer", 0, List.of(0, 15), range); + NativeBlockProperty enumerated = + new NativeBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null); assertTrue(numeric.hasNumericRange()); assertEquals(range, numeric.numericRange()); @@ -150,22 +158,22 @@ public class PlatformValueContractTest { } @Override - public List reachableStructureKeys(PlatformWorld world) { + public List reachableStructureKeys(NativeWorld world) { return List.of(); } @Override - public List possibleBiomeKeys(PlatformWorld world) { + public List possibleBiomeKeys(NativeWorld world) { return List.of(); } @Override - public boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed) { + public boolean placeFeature(NativeWorld world, int x, int y, int z, String featureKey, long seed) { return false; } @Override - public int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { + public int[] placeStructure(NativeWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) { return new int[0]; }