From 113eb04e0d4e6fa25ff608347a0138a6b9c8a124 Mon Sep 17 00:00:00 2001 From: Waterpicker Date: Sat, 6 May 2023 05:27:48 -0500 Subject: [PATCH] Yes. >< --- .../org/dimdev/dimdoors/DimensionalDoors.java | 12 + .../api/block/AfterMoveCollidableBlock.java | 14 +- .../dimdoors/api/block/CustomBreakBlock.java | 24 +- .../api/block/ExplosionConvertibleBlock.java | 12 +- .../api/client/DimensionalPortalRenderer.java | 66 ++-- .../api/client/RenderLayerFactory.java | 11 +- .../api/entity/LastPositionProvider.java | 4 +- .../api/event/UseItemOnBlockCallback.java | 25 +- .../dimdoors/api/util/BlockPlacementType.java | 35 +- .../dimdev/dimdoors/api/util/Location.java | 50 ++- .../dimdoors/api/util/RotatedLocation.java | 24 +- .../dimdoors/block/DoorSoundProvider.java | 10 +- .../org/dimdev/dimdoors/block/ModBlocks.java | 12 +- .../client/CustomBreakBlockHandler.java | 70 ++++ .../DetachedRiftBlockEntityRenderer.java | 90 +++++ .../dimdoors/client/DimensionRenderering.java | 108 ++++++ .../DimensionalDoorModelVariantProvider.java | 53 +++ .../DimensionalDoorsClientInitializer.java | 54 +++ .../EntranceRiftBlockEntityRenderer.java | 35 ++ .../org/dimdev/dimdoors/client/MaskModel.java | 20 + .../dimdev/dimdoors/client/MaskRenderer.java | 11 + .../dimdoors/client/ModEntityModelLayers.java | 17 + .../dimdev/dimdoors/client/ModShaders.java | 18 + .../dimdev/dimdoors/client/MonolithModel.java | 79 ++++ .../dimdoors/client/MonolithRenderer.java | 64 ++++ .../dimdev/dimdoors/client/MyRenderLayer.java | 82 ++++ .../dimdoors/client/RiftCrackRenderer.java | 68 ++++ .../dimdev/dimdoors/client/RiftCurves.java | 353 ++++++++++++++++++ .../dimdev/dimdoors/client/ToolTipHelper.java | 18 + .../client/UnderlaidChildItemRenderer.java | 57 +++ .../dimdoors/client/config/ModMenu.java | 13 + .../dimdoors/client/config/ModMenuImpl.java | 15 + .../client/screen/TesselatingLoomScreen.java | 105 ++++++ .../dimdoors/client/tesseract/Plane.java | 77 ++++ .../dimdoors/client/tesseract/Tesseract.java | 193 ++++++++++ .../client/wthit/EntranceRiftProvider.java | 38 ++ .../dimdoors/client/wthit/WthitPlugin.java | 15 + .../dimdoors/command/DimTeleportCommand.java | 65 ++++ .../dimdev/dimdoors/command/ModCommands.java | 12 + .../dimdoors/command/PocketCommand.java | 126 +++++++ .../dimdoors/command/WorldeditHelper.java | 62 +++ .../BlockPlacementTypeArgumentType.java | 20 + .../command/arguments/EnumArgumentType.java | 46 +++ .../arguments/PocketTemplateArgumentType.java | 60 +++ .../dimdev/dimdoors/criteria/ModCriteria.java | 13 + .../PocketSpawnPointSetCondition.java | 34 ++ .../criteria/RiftTrackedCriterion.java | 34 ++ .../criteria/TagBlockBreakCriteria.java | 51 +++ .../dimdev/dimdoors/entity/MaskEntity.java | 36 ++ .../dimdoors/entity/ModEntityTypes.java | 60 +++ .../dimdoors/entity/MonolithEntity.java | 337 +++++++++++++++++ .../dimdoors/entity/ai/MonolithAggroGoal.java | 113 ++++++ .../entity/limbo/LimboEntranceSource.java | 33 ++ .../entity/limbo/LimboExitReason.java | 23 ++ .../dimdev/dimdoors/entity/stat/ModStats.java | 28 ++ .../org/dimdev/dimdoors/item/ModItems.java | 103 ++--- .../dimdev/dimdoors/item/RaycastHelper.java | 36 +- .../dimdev/dimdoors/item/RiftBladeItem.java | 102 +++-- .../org/dimdev/dimdoors/item/RiftKeyItem.java | 5 - .../dimdev/dimdoors/item/RiftRemoverItem.java | 55 ++- .../dimdoors/item/RiftSignatureItem.java | 129 +++---- .../dimdoors/item/RiftStabilizerItem.java | 54 ++- .../item/StabilizedRiftSignatureItem.java | 94 ++--- .../item/door/DimensionalTrapdoorItem.java | 35 +- .../dimdoors/mixin/ArgumentTypesMixin.java | 28 ++ .../org/dimdev/dimdoors/mixin/BlockMixin.java | 23 ++ .../dimdev/dimdoors/mixin/DoorBlockMixin.java | 31 ++ .../dimdev/dimdoors/mixin/EntityMixin.java | 24 ++ .../dimdev/dimdoors/mixin/ExplosionMixin.java | 44 +++ ...ExtendedServerPlayNetworkhandlerMixin.java | 26 ++ .../org/dimdev/dimdoors/mixin/ItemMixin.java | 37 ++ .../dimdoors/mixin/PlayerEntityMixin.java | 52 +++ .../mixin/ServerPlayNetworkHandlerMixin.java | 68 ++++ .../mixin/ServerPlayerEntityMixin.java | 155 ++++++++ .../ServerPlayerInteractionManagerMixin.java | 27 ++ .../dimdoors/mixin/ServerWorldMixin.java | 19 + .../dimdev/dimdoors/mixin/TrapDoorMixin.java | 32 ++ .../org/dimdev/dimdoors/mixin/WorldMixin.java | 86 +++++ .../accessor/ChunkGeneratorAccessor.java | 8 + .../accessor/CraftingInventoryAccessor.java | 15 + .../accessor/DefaultParticleTypeAccessor.java | 13 + .../mixin/accessor/DirectionAccessor.java | 13 + .../mixin/accessor/EntityAccessor.java | 11 + .../accessor/GenerationSettingsAccessor.java | 17 + .../mixin/accessor/ListTagAccessor.java | 16 + .../accessor/RecipesProviderAccessor.java | 23 ++ .../accessor/RedstoneWireBlockAccessor.java | 15 + .../mixin/client/BackgroundRendererMixin.java | 22 ++ .../client/ClientPlayNetworkHandlerMixin.java | 20 + .../ClientPlayerInteractionManagerMixin.java | 42 +++ ...ExtendedClientPlayNetworkHandlerMixin.java | 30 ++ .../mixin/client/GameRendererMixin.java | 36 ++ .../dimdoors/mixin/client/InGameHudMixin.java | 36 ++ .../mixin/client/PostProcessShaderMixin.java | 28 ++ .../mixin/client/WorldRendererMixin.java | 88 +++++ .../accessor/WorldRendererAccessor.java | 11 + .../ExtendedServerPlayNetworkHandler.java | 14 + .../dimdoors/network/ServerPacketHandler.java | 166 ++++++++ .../network/ServerPacketListener.java | 10 + .../dimdev/dimdoors/network/SimplePacket.java | 16 + .../network/client/ClientPacketHandler.java | 156 ++++++++ .../network/client/ClientPacketListener.java | 19 + .../ExtendedClientPlayNetworkHandler.java | 12 + .../packet/c2s/HitBlockWithItemC2SPacket.java | 74 ++++ .../NetworkHandlerInitializedC2SPacket.java | 33 ++ .../s2c/MonolithAggroParticlesPacket.java | 54 +++ .../s2c/MonolithTeleportParticlesPacket.java | 39 ++ .../PlayerInventorySlotUpdateS2CPacket.java | 65 ++++ .../packet/s2c/RenderBreakBlockS2CPacket.java | 65 ++++ .../packet/s2c/SyncPocketAddonsS2CPacket.java | 92 +++++ .../pockets/DefaultDungeonDestinations.java | 84 +++++ .../pockets/PocketGenerationContext.java | 19 + .../dimdoors/pockets/PocketGenerator.java | 84 +++++ .../dimdev/dimdoors/pockets/PocketLoader.java | 147 ++++++++ .../dimdoors/pockets/PocketTemplate.java | 98 +++++ .../dimdoors/pockets/TemplateUtils.java | 177 +++++++++ .../pockets/generator/ChunkGenerator.java | 245 ++++++++++++ .../generator/LazyPocketGenerator.java | 149 ++++++++ .../pockets/generator/PocketGenerator.java | 333 +++++++++++++++++ .../pockets/generator/SchematicGenerator.java | 182 +++++++++ .../pockets/generator/VoidGenerator.java | 99 +++++ .../AbsoluteRiftBlockEntityModifier.java | 122 ++++++ .../AbstractLazyCompatibleModifier.java | 34 ++ .../modifier/AbstractLazyModifier.java | 34 ++ .../pockets/modifier/AbstractModifier.java | 34 ++ .../modifier/DimensionalDoorModifier.java | 176 +++++++++ .../modifier/LazyCompatibleModifier.java | 39 ++ .../pockets/modifier/LazyModifier.java | 9 + .../dimdoors/pockets/modifier/Modifier.java | 135 +++++++ .../pockets/modifier/OffsetModifier.java | 75 ++++ .../modifier/PocketEntranceModifier.java | 69 ++++ .../modifier/RelativeReferenceModifier.java | 106 ++++++ .../pockets/modifier/RiftDataModifier.java | 115 ++++++ .../pockets/modifier/RiftManager.java | 103 +++++ .../pockets/modifier/ShellModifier.java | 254 +++++++++++++ .../virtual/AbstractVirtualPocket.java | 34 ++ .../virtual/ImplementedVirtualPocket.java | 158 ++++++++ .../pockets/virtual/VirtualPocket.java | 78 ++++ .../pockets/virtual/VirtualPocketList.java | 115 ++++++ .../virtual/reference/IdReference.java | 69 ++++ .../reference/PocketGeneratorReference.java | 222 +++++++++++ .../virtual/reference/TagReference.java | 116 ++++++ .../selection/AbstractVirtualPocketList.java | 64 ++++ .../selection/ConditionalSelector.java | 117 ++++++ .../virtual/selection/PathSelector.java | 46 +++ .../src/main/resources/dimdoors.accesswidener | 5 +- 146 files changed, 9112 insertions(+), 463 deletions(-) create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/CustomBreakBlockHandler.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/DetachedRiftBlockEntityRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/DimensionRenderering.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorModelVariantProvider.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorsClientInitializer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/EntranceRiftBlockEntityRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/MaskModel.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/MaskRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/ModEntityModelLayers.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/ModShaders.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/MonolithModel.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/MonolithRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/MyRenderLayer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/RiftCrackRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/RiftCurves.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/ToolTipHelper.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/UnderlaidChildItemRenderer.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/config/ModMenu.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/config/ModMenuImpl.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/screen/TesselatingLoomScreen.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/tesseract/Plane.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/tesseract/Tesseract.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/wthit/EntranceRiftProvider.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/client/wthit/WthitPlugin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/DimTeleportCommand.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/ModCommands.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/PocketCommand.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/WorldeditHelper.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/arguments/BlockPlacementTypeArgumentType.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/arguments/EnumArgumentType.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/command/arguments/PocketTemplateArgumentType.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/criteria/ModCriteria.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/criteria/PocketSpawnPointSetCondition.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/criteria/RiftTrackedCriterion.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/criteria/TagBlockBreakCriteria.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/MaskEntity.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/ModEntityTypes.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/MonolithEntity.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/ai/MonolithAggroGoal.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboEntranceSource.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboExitReason.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/entity/stat/ModStats.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ArgumentTypesMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/BlockMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/DoorBlockMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ExtendedServerPlayNetworkhandlerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ItemMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/PlayerEntityMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerEntityMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerInteractionManagerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/ServerWorldMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/TrapDoorMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ChunkGeneratorAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/CraftingInventoryAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DefaultParticleTypeAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DirectionAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/EntityAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/GenerationSettingsAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ListTagAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RecipesProviderAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RedstoneWireBlockAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/BackgroundRendererMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayNetworkHandlerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayerInteractionManagerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/ExtendedClientPlayNetworkHandlerMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/GameRendererMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/InGameHudMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/PostProcessShaderMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/WorldRendererMixin.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/mixin/client/accessor/WorldRendererAccessor.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/ExtendedServerPlayNetworkHandler.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/ServerPacketHandler.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/ServerPacketListener.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/SimplePacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketHandler.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketListener.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/client/ExtendedClientPlayNetworkHandler.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/HitBlockWithItemC2SPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/NetworkHandlerInitializedC2SPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithAggroParticlesPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithTeleportParticlesPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/PlayerInventorySlotUpdateS2CPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/RenderBreakBlockS2CPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/SyncPocketAddonsS2CPacket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/DefaultDungeonDestinations.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerationContext.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/PocketLoader.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/PocketTemplate.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/TemplateUtils.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/generator/ChunkGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/generator/LazyPocketGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/generator/PocketGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/generator/SchematicGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/generator/VoidGenerator.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbsoluteRiftBlockEntityModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyCompatibleModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/DimensionalDoorModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyCompatibleModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/Modifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/OffsetModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/PocketEntranceModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RelativeReferenceModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftDataModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftManager.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/modifier/ShellModifier.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/AbstractVirtualPocket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/ImplementedVirtualPocket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocket.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocketList.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/IdReference.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/PocketGeneratorReference.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/TagReference.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/AbstractVirtualPocketList.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/ConditionalSelector.java create mode 100644 common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/PathSelector.java diff --git a/common/src/main/java/org/dimdev/dimdoors/DimensionalDoors.java b/common/src/main/java/org/dimdev/dimdoors/DimensionalDoors.java index f8dd1ce8..fb38a75c 100644 --- a/common/src/main/java/org/dimdev/dimdoors/DimensionalDoors.java +++ b/common/src/main/java/org/dimdev/dimdoors/DimensionalDoors.java @@ -1,10 +1,22 @@ package org.dimdev.dimdoors; +import dev.architectury.utils.GameInstance; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.level.Level; public class DimensionalDoors { public static final String MOD_ID = "dimdoors"; public static ResourceLocation id(String id) { return new ResourceLocation(MOD_ID, id); } + + public static MinecraftServer getServer() { + return GameInstance.getServer(); + } + + public static Level getWorld(ResourceKey world) { + return getServer().getLevel(world); + } } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java b/common/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java index 6bc6d847..8ae1d3c6 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/block/AfterMoveCollidableBlock.java @@ -1,13 +1,13 @@ package org.dimdev.dimdoors.api.block; -import net.minecraft.block.BlockState; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.Vec3; public interface AfterMoveCollidableBlock { // only triggers on servers - ActionResult onAfterMovePlayerCollision(BlockState state, ServerWorld world, BlockPos pos, ServerPlayerEntity player, Vec3d positionChange); + InteractionResult onAfterMovePlayerCollision(BlockState state, ServerLevel world, BlockPos pos, ServerPlayer player, Vec3 positionChange); } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java b/common/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java index b4c2f180..e0a96bbb 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/block/CustomBreakBlock.java @@ -1,21 +1,21 @@ package org.dimdev.dimdoors.api.block; -import java.util.function.Consumer; +import com.mojang.datafixers.util.Pair; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +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.level.material.FluidState; -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.Entity; -import net.minecraft.fluid.FluidState; -import net.minecraft.util.Pair; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import java.util.function.Consumer; /** * Only works in cases where {@link net.minecraft.block.AbstractBlock#getStateForNeighborUpdate AbstractBlock#getStateForNeighborUpdate} returns an air {@link BlockState} */ public interface CustomBreakBlock { - TypedActionResult>> customBreakBlock(World world, BlockPos pos, BlockState blockState, Entity breakingEntity); + InteractionResultHolder>> customBreakBlock(Level world, BlockPos pos, BlockState blockState, Entity breakingEntity); /* If this causes any issue mixin into FluidState instead. @@ -26,13 +26,13 @@ public interface CustomBreakBlock { private final Consumer blockEntityConsumer; public HackyFluidState(BlockState blockState, Consumer blockEntityConsumer) { - super(blockState.getFluidState().getFluid(), blockState.getFluidState().getEntries(), blockState.getFluidState().codec); + super(blockState.getFluidState().getType(), blockState.getFluidState().getValues(), blockState.getFluidState().propertiesCodec); this.blockState = blockState; this.blockEntityConsumer = blockEntityConsumer; } @Override - public BlockState getBlockState() { + public BlockState createLegacyBlock() { return blockState; } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java b/common/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java index 7ba930fa..980114b0 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/block/ExplosionConvertibleBlock.java @@ -1,11 +1,11 @@ package org.dimdev.dimdoors.api.block; -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; public interface ExplosionConvertibleBlock { - ActionResult explode(World world, BlockPos pos, BlockState state, BlockEntity blockEntity); + InteractionResult explode(Level world, BlockPos pos, BlockState state, BlockEntity blockEntity); } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/client/DimensionalPortalRenderer.java b/common/src/main/java/org/dimdev/dimdoors/api/client/DimensionalPortalRenderer.java index 757399e3..6de7de7d 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/client/DimensionalPortalRenderer.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/client/DimensionalPortalRenderer.java @@ -1,73 +1,71 @@ package org.dimdev.dimdoors.api.client; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.blockentity.TheEndPortalRenderer; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.client.ModShaders; + import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import net.minecraft.client.model.ModelPart; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.RenderPhase; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.VertexFormat; -import net.minecraft.client.render.VertexFormats; -import net.minecraft.client.render.block.entity.EndPortalBlockEntityRenderer; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.Direction; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -import org.dimdev.dimdoors.DimensionalDoors; -import org.dimdev.dimdoors.client.ModShaders; - @Environment(EnvType.CLIENT) public final class DimensionalPortalRenderer { - public static final Identifier WARP_PATH; - private static final RenderPhase.ShaderProgram DIMENSIONAL_PORTAL_SHADER; - private static final RenderLayer RENDER_LAYER; + public static final ResourceLocation WARP_PATH; + private static final RenderStateShard.ShaderStateShard DIMENSIONAL_PORTAL_SHADER; + private static final RenderType RENDER_LAYER; private static final ModelPart MODEL; private static final ModelPart TALL_MODEL; - public static void renderDimensionalPortal(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay) { + public static void renderDimensionalPortal(PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay) { renderDimensionalPortal(matrixStack, vertexConsumerProvider, transformer, tickDelta, light, overlay, true); } - public static void renderDimensionalPortal(MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay, boolean tall) { + public static void renderDimensionalPortal(PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay, boolean tall) { ModelPart model = tall ? TALL_MODEL : MODEL; renderModelWithPortalShader(model, matrixStack, vertexConsumerProvider, transformer, tickDelta, light, overlay); } - public static void renderModelWithPortalShader(ModelPart model, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay) { + public static void renderModelWithPortalShader(ModelPart model, PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, Transformer transformer, float tickDelta, int light, int overlay) { transformer.transform(matrixStack); model.render(matrixStack, vertexConsumerProvider.getBuffer(RENDER_LAYER), light, overlay); } static { WARP_PATH = DimensionalDoors.id("textures/other/warp.png"); - DIMENSIONAL_PORTAL_SHADER = new RenderPhase.ShaderProgram(ModShaders::getDimensionalPortal); + DIMENSIONAL_PORTAL_SHADER = new RenderStateShard.ShaderStateShard(ModShaders::getDimensionalPortal); RENDER_LAYER = RenderLayerFactory.create( "dimensional_portal", - VertexFormats.POSITION, - VertexFormat.DrawMode.QUADS, + DefaultVertexFormat.POSITION, + VertexFormat.Mode.QUADS, 256, false, false, - RenderLayer.MultiPhaseParameters.builder() - .program(DIMENSIONAL_PORTAL_SHADER) - .texture( - RenderPhase.Textures.create() - .add(EndPortalBlockEntityRenderer.SKY_TEXTURE, false, false) + RenderType.CompositeState.builder() + .setShaderState(DIMENSIONAL_PORTAL_SHADER) + .setTextureState( + RenderStateShard.MultiTextureStateShard.builder() + .add(TheEndPortalRenderer.END_SKY_LOCATION, false, false) .add(WARP_PATH, false, false) .build() ) - .build(false) + .createCompositeState(false) ); Set directions = new HashSet<>(List.of(Direction.values())); - ModelPart.Cuboid small = new ModelPart.Cuboid(0, 0, 0.2f, 0.2f, -0.1f, 15.8f, 15.8f, 0.01F, 0, 0, 0, false, 1024, 1024, directions); + ModelPart.Cube small = new ModelPart.Cube(0, 0, 0.2f, 0.2f, -0.1f, 15.8f, 15.8f, 0.01F, 0, 0, 0, false, 1024, 1024, directions); MODEL = new ModelPart(Collections.singletonList(small), Collections.emptyMap()); - ModelPart.Cuboid big = new ModelPart.Cuboid(0, 0, 0.2f, 0.2f, -0.1f, 15.8f, 31.8f, 0.01F, 0, 0, 0, false, 1024, 1024, directions); + ModelPart.Cube big = new ModelPart.Cube(0, 0, 0.2f, 0.2f, -0.1f, 15.8f, 31.8f, 0.01F, 0, 0, 0, false, 1024, 1024, directions); TALL_MODEL = new ModelPart(Collections.singletonList(big), Collections.emptyMap()); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/client/RenderLayerFactory.java b/common/src/main/java/org/dimdev/dimdoors/api/client/RenderLayerFactory.java index f746e277..2332a4df 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/client/RenderLayerFactory.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/client/RenderLayerFactory.java @@ -1,16 +1,13 @@ package org.dimdev.dimdoors.api.client; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.VertexFormat; - +import com.mojang.blaze3d.vertex.VertexFormat; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; - -import org.dimdev.dimdoors.mixin.client.accessor.RenderLayerAccessor; +import net.minecraft.client.renderer.RenderType; @Environment(EnvType.CLIENT) public class RenderLayerFactory { - public static RenderLayer create(String name, VertexFormat vertexFormat, VertexFormat.DrawMode drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, RenderLayer.MultiPhaseParameters phases) { - return RenderLayerAccessor.callOf(name, vertexFormat, drawMode, expectedBufferSize, hasCrumbling, translucent, phases); + public static RenderType create(String name, VertexFormat vertexFormat, VertexFormat.Mode drawMode, int expectedBufferSize, boolean hasCrumbling, boolean translucent, RenderType.CompositeState phases) { + return RenderType.create(name, vertexFormat, drawMode, expectedBufferSize, hasCrumbling, translucent, phases); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java b/common/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java index 61c093b8..b4ea5bee 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/entity/LastPositionProvider.java @@ -1,7 +1,7 @@ package org.dimdev.dimdoors.api.entity; -import net.minecraft.util.math.Vec3d; +import net.minecraft.world.phys.Vec3; public interface LastPositionProvider { - Vec3d getLastPos(); + Vec3 getLastPos(); } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/event/UseItemOnBlockCallback.java b/common/src/main/java/org/dimdev/dimdoors/api/event/UseItemOnBlockCallback.java index 21971a35..675a4f32 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/event/UseItemOnBlockCallback.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/event/UseItemOnBlockCallback.java @@ -1,28 +1,27 @@ package org.dimdev.dimdoors.api.event; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.world.World; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; +import dev.architectury.event.Event; +import dev.architectury.event.EventFactory; +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.phys.BlockHitResult; public interface UseItemOnBlockCallback { - Event EVENT = EventFactory.createArrayBacked(UseItemOnBlockCallback.class, + Event EVENT = EventFactory.of( listeners -> (player, world, hand, hitresult) -> { for (UseItemOnBlockCallback event : listeners) { - ActionResult result = event.useItemOnBlock(player, world, hand, hitresult); + InteractionResult result = event.useItemOnBlock(player, world, hand, hitresult); - if (result != ActionResult.PASS) { + if (result != InteractionResult.PASS) { return result; } } - return ActionResult.PASS; + return InteractionResult.PASS; } ); - ActionResult useItemOnBlock(PlayerEntity player, World world, Hand hand, BlockHitResult hitResult); + InteractionResult useItemOnBlock(Player player, Level world, InteractionHand hand, BlockHitResult hitResult); } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/util/BlockPlacementType.java b/common/src/main/java/org/dimdev/dimdoors/api/util/BlockPlacementType.java index c9e75dd6..e1dd892f 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/util/BlockPlacementType.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/util/BlockPlacementType.java @@ -1,26 +1,27 @@ package org.dimdev.dimdoors.api.util; +import com.mojang.serialization.Codec; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.TickTask; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + import java.util.HashMap; import java.util.Map; import java.util.function.BiConsumer; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.ServerTask; -import net.minecraft.util.StringIdentifiable; -import net.minecraft.world.World; - -public enum BlockPlacementType implements StringIdentifiable { +public enum BlockPlacementType implements StringRepresentable { // TODO: do we need some update fluids only option? SECTION_NO_UPDATE_QUEUE_BLOCK_ENTITY("section_no_update_queue_block_entity", true, false, BlockPlacementType::queueBlockEntity), - SECTION_NO_UPDATE("section_no_update", true, false, World::addBlockEntity), - SECTION_UPDATE("section_update", true, true, World::addBlockEntity), - SET_BLOCK_STATE("set_block_state", false, false, World::addBlockEntity), + SECTION_NO_UPDATE("section_no_update", true, false, Level::setBlockEntity), + SECTION_UPDATE("section_update", true, true, Level::setBlockEntity), + SET_BLOCK_STATE("set_block_state", false, false, Level::setBlockEntity), SET_BLOCK_STATE_QUEUE_BLOCK_ENTITY("set_block_state_queue_block_entity", false, false, BlockPlacementType::queueBlockEntity); private final static Map idMap = new HashMap<>(); - public static final Codec CODEC = StringIdentifiable.createCodec(BlockPlacementType::values); + public static final Codec CODEC = StringRepresentable.fromEnum(BlockPlacementType::values); static { for (BlockPlacementType type : BlockPlacementType.values()) { @@ -31,10 +32,10 @@ public enum BlockPlacementType implements StringIdentifiable { final String id; final boolean useSection; final boolean markForUpdate; - final BiConsumer blockEntityPlacer; + final BiConsumer blockEntityPlacer; - BlockPlacementType(String id, boolean useSection, boolean markForUpdate, BiConsumer blockEntityPlacer) { + BlockPlacementType(String id, boolean useSection, boolean markForUpdate, BiConsumer blockEntityPlacer) { this.id = id; this.useSection = useSection; this.markForUpdate = markForUpdate; @@ -49,7 +50,7 @@ public enum BlockPlacementType implements StringIdentifiable { return markForUpdate; } - public BiConsumer getBlockEntityPlacer() { + public BiConsumer getBlockEntityPlacer() { return blockEntityPlacer; } @@ -61,13 +62,13 @@ public enum BlockPlacementType implements StringIdentifiable { return idMap.get(id); } - private static void queueBlockEntity(World world, BlockEntity blockEntity) { + private static void queueBlockEntity(Level world, BlockEntity blockEntity) { MinecraftServer server = world.getServer(); - server.send(new ServerTask(server.getTicks(), () -> world.addBlockEntity(blockEntity))); + server.tell(new TickTask(server.getTickCount(), () -> world.setBlockEntity(blockEntity))); } @Override - public String asString() { + public String getSerializedName() { return id; } } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/util/Location.java b/common/src/main/java/org/dimdev/dimdoors/api/util/Location.java index 13fe2c96..f8f4930b 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/util/Location.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/util/Location.java @@ -2,43 +2,41 @@ package org.dimdev.dimdoors.api.util; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; - -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.fluid.FluidState; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +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.level.material.FluidState; import org.dimdev.dimdoors.DimensionalDoors; public class Location { public static final Codec CODEC = RecordCodecBuilder.create(instance -> { - return instance.group(World.CODEC.fieldOf("world").forGetter(location -> { + return instance.group(Level.RESOURCE_KEY_CODEC.fieldOf("world").forGetter(location -> { return location.world; }), BlockPos.CODEC.fieldOf("pos").forGetter(location -> { return location.pos; })).apply(instance, Location::new); }); - public final RegistryKey world; + public final ResourceKey world; public final BlockPos pos; - public Location(RegistryKey world, BlockPos pos) { + public Location(ResourceKey world, BlockPos pos) { this.world = world; this.pos = pos; } - public Location(ServerWorld world, int x, int y, int z) { + public Location(ServerLevel world, int x, int y, int z) { this(world, new BlockPos(x, y, z)); } - public Location(ServerWorld world, BlockPos pos) { - this(world.getRegistryKey(), pos); + public Location(ServerLevel world, BlockPos pos) { + this(world.dimension(), pos); } public int getX() { @@ -81,25 +79,25 @@ public class Location { return this.world.hashCode() * 31 + this.pos.hashCode(); } - public RegistryKey getWorldId() { + public ResourceKey getWorldId() { return this.world; } - public ServerWorld getWorld() { - return DimensionalDoors.getServer().getWorld(this.world); + public ServerLevel getWorld() { + return DimensionalDoors.getServer().getLevel(this.world); } - public static NbtCompound toNbt(Location location) { - NbtCompound nbt = new NbtCompound(); - nbt.putString("world", location.world.getValue().toString()); + public static CompoundTag toNbt(Location location) { + CompoundTag nbt = new CompoundTag(); + nbt.putString("world", location.world.location().toString()); nbt.putIntArray("pos", new int[]{location.getX(), location.getY(), location.getZ()}); return nbt; } - public static Location fromNbt(NbtCompound nbt) { + public static Location fromNbt(CompoundTag nbt) { int[] pos = nbt.getIntArray("pos"); return new Location( - RegistryKey.of(RegistryKeys.WORLD, new Identifier(nbt.getString("world"))), + ResourceKey.create(Registries.DIMENSION, new ResourceLocation(nbt.getString("world"))), new BlockPos(pos[0], pos[1], pos[2]) ); } diff --git a/common/src/main/java/org/dimdev/dimdoors/api/util/RotatedLocation.java b/common/src/main/java/org/dimdev/dimdoors/api/util/RotatedLocation.java index 5ca01c33..d46c1982 100644 --- a/common/src/main/java/org/dimdev/dimdoors/api/util/RotatedLocation.java +++ b/common/src/main/java/org/dimdev/dimdoors/api/util/RotatedLocation.java @@ -1,35 +1,35 @@ package org.dimdev.dimdoors.api.util; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; public class RotatedLocation extends Location { public final float yaw; public final float pitch; - public RotatedLocation(RegistryKey world, BlockPos pos, float yaw, float pitch) { + public RotatedLocation(ResourceKey world, BlockPos pos, float yaw, float pitch) { super(world, pos); this.yaw = yaw; this.pitch = pitch; } - public static NbtCompound serialize(RotatedLocation location) { - NbtCompound nbt = new NbtCompound(); - nbt.putString("world", location.world.getValue().toString()); + public static CompoundTag serialize(RotatedLocation location) { + CompoundTag nbt = new CompoundTag(); + nbt.putString("world", location.world.location().toString()); nbt.putIntArray("pos", new int[]{location.getX(), location.getY(), location.getZ()}); nbt.putFloat("yaw", location.pitch); nbt.putFloat("pitch", location.pitch); return nbt; } - public static RotatedLocation deserialize(NbtCompound nbt) { + public static RotatedLocation deserialize(CompoundTag nbt) { int[] pos = nbt.getIntArray("pos"); return new RotatedLocation( - RegistryKey.of(RegistryKeys.WORLD, new Identifier(nbt.getString("world"))), + ResourceKey.create(Registries.DIMENSION, new ResourceLocation(nbt.getString("world"))), new BlockPos(pos[0], pos[1], pos[2]), nbt.getFloat("yaw"), nbt.getFloat("pitch") diff --git a/common/src/main/java/org/dimdev/dimdoors/block/DoorSoundProvider.java b/common/src/main/java/org/dimdev/dimdoors/block/DoorSoundProvider.java index c3496854..e88e6f9e 100644 --- a/common/src/main/java/org/dimdev/dimdoors/block/DoorSoundProvider.java +++ b/common/src/main/java/org/dimdev/dimdoors/block/DoorSoundProvider.java @@ -1,18 +1,18 @@ package org.dimdev.dimdoors.block; -import net.minecraft.block.BlockSetType; -import net.minecraft.sound.SoundEvent; -import net.minecraft.sound.SoundEvents; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.state.properties.BlockSetType; public interface DoorSoundProvider { public static final DoorSoundProvider DUMMY = new DoorSoundProvider() {}; // TODO: remove these two public default SoundEvent getOpenSound() { - return SoundEvents.BLOCK_WOODEN_DOOR_OPEN; + return SoundEvents.WOODEN_DOOR_OPEN; } public default SoundEvent getCloseSound() { - return SoundEvents.BLOCK_WOODEN_DOOR_CLOSE; + return SoundEvents.WOODEN_DOOR_CLOSE; } default BlockSetType getSetType() { diff --git a/common/src/main/java/org/dimdev/dimdoors/block/ModBlocks.java b/common/src/main/java/org/dimdev/dimdoors/block/ModBlocks.java index ad13ff9e..02dee071 100644 --- a/common/src/main/java/org/dimdev/dimdoors/block/ModBlocks.java +++ b/common/src/main/java/org/dimdev/dimdoors/block/ModBlocks.java @@ -1,7 +1,11 @@ package org.dimdev.dimdoors.block; +import dev.architectury.registry.client.rendering.RenderTypeRegistry; import dev.architectury.registry.registries.DeferredRegister; import dev.architectury.registry.registries.RegistrySupplier; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.renderer.RenderType; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.DyeColor; @@ -231,11 +235,11 @@ public final class ModBlocks { return block; } -// @Environment(EnvType.CLIENT) -// public static void initClient() { -// BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ModBlocks.QUARTZ_DOOR, ModBlocks.GOLD_DOOR); + @Environment(EnvType.CLIENT) + public static void initClient() { + RenderTypeRegistry.register(RenderType.cutout(), ModBlocks.QUARTZ_DOOR.get(), ModBlocks.GOLD_DOOR.get()); // DoorData.DOORS.forEach(door -> BlockRenderLayerMap.INSTANCE.putBlock(door, RenderLayer.getCutout())); -// } + } public static RegistrySupplier ancientFabricFromDye(DyeColor color) { return ANCIENT_FABRIC_BLOCKS.get(color); diff --git a/common/src/main/java/org/dimdev/dimdoors/client/CustomBreakBlockHandler.java b/common/src/main/java/org/dimdev/dimdoors/client/CustomBreakBlockHandler.java new file mode 100644 index 00000000..bed76e65 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/CustomBreakBlockHandler.java @@ -0,0 +1,70 @@ +package org.dimdev.dimdoors.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.core.BlockPos; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +@Environment(EnvType.CLIENT) +public class CustomBreakBlockHandler { + private static final Map customBreakBlockMap = new ConcurrentHashMap<>(); + private static final ReentrantLock lock = new ReentrantLock(); + + public static Map getCustomBreakBlockMap(int ticks) { + lock.lock(); + Set expired = customBreakBlockMap.entrySet().stream().filter(entry -> ticks - entry.getValue().getLastUpdateTick() > 400).map(entry -> entry.getKey()).collect(Collectors.toSet()); + expired.forEach(customBreakBlockMap::remove); + Map copy = new HashMap<>(customBreakBlockMap); + lock.unlock(); + return copy; + } + + public static void customBreakBlock(BlockPos pos, int stage, int ticks) { + lock.lock(); + if (stage < 0 || stage > 10) { + customBreakBlockMap.remove(pos); + } else { + if (customBreakBlockMap.containsKey(pos)) { + + BreakBlockInfo info = customBreakBlockMap.get(pos); + info.setStage(stage); + info.setLastUpdateTick(ticks); + } else { + customBreakBlockMap.put(pos, new BreakBlockInfo(stage, ticks)); + } + } + lock.unlock(); + } + + public static class BreakBlockInfo { + private int stage; + private int lastUpdateTick; + + private BreakBlockInfo(int stage, int lastUpdateTick) { + this.stage = stage; + this.lastUpdateTick = lastUpdateTick; + } + + public void setStage(int stage) { + this.stage = stage; + } + + public int getStage() { + return stage; + } + + public void setLastUpdateTick(int lastUpdateTick) { + this.lastUpdateTick = lastUpdateTick; + } + + public int getLastUpdateTick() { + return lastUpdateTick; + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/DetachedRiftBlockEntityRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/DetachedRiftBlockEntityRenderer.java new file mode 100644 index 00000000..f3325487 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/DetachedRiftBlockEntityRenderer.java @@ -0,0 +1,90 @@ +package org.dimdev.dimdoors.client; + +import com.flowpowered.math.TrigMath; +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import dev.architectury.utils.GameInstance; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.text.Text; +import net.minecraft.util.Hand; +import net.minecraft.world.InteractionHand; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.RGBA; +import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.client.tesseract.Tesseract; +import org.dimdev.dimdoors.item.ModItems; +import org.dimdev.dimdoors.rift.targets.IdMarker; + +import java.util.Objects; + +@Environment(EnvType.CLIENT) +public class DetachedRiftBlockEntityRenderer implements BlockEntityRenderer { + public static final ResourceLocation TESSERACT_PATH = DimensionalDoors.id("textures/other/tesseract.png"); + private static final RGBA DEFAULT_COLOR = new RGBA(1, 0.5f, 1, 1); + + private static final Tesseract TESSERACT = new Tesseract(); + private static final RiftCurves.PolygonInfo CURVE = RiftCurves.CURVES.get(0); + + @Override + public void render(DetachedRiftBlockEntity rift, float tickDelta, PoseStack matrices, MultiBufferSource vcs, int breakProgress, int alpha) { + if(GameInstance.getClient().player != null && GameInstance.getClient().player.getItemInHand(InteractionHand.MAIN_HAND).is(ModItems.RIFT_CONFIGURATION_TOOL.get()) && rift.getData().getDestination() instanceof IdMarker idMarker) { + matrices.pushPose(); + matrices.translate(0.5, 0.5, 0.5); + + GameInstance.getClient().font.drawInBatch(Component.literal(String.valueOf(idMarker.getId())), 0f,0f, 0xffffffff, false, matrices.last().pose(), vcs, TextRenderer.TextLayerType.NORMAL, 0x000000, LightmapTextureManager.MAX_LIGHT_COORDINATE); + + matrices.popPose(); + } + + if (DimensionalDoors.getConfig().getGraphicsConfig().showRiftCore) { + this.renderTesseract(vcs.getBuffer(MyRenderLayer.TESSERACT), rift, matrices, tickDelta); + } else { + long timeLeft = RiftBlockEntity.showRiftCoreUntil - System.currentTimeMillis(); + if (timeLeft >= 0) { + this.renderTesseract(vcs.getBuffer(MyRenderLayer.TESSERACT), rift, matrices, tickDelta); + } + } + + this.renderCrack(vcs.getBuffer(MyRenderLayer.CRACK), matrices, rift); + } + + private void renderCrack(VertexConsumer vc, PoseStack matrices, DetachedRiftBlockEntity rift) { + matrices.pushPose(); + matrices.translate(0.5, 0.5, 0.5); + RiftCrackRenderer.drawCrack(matrices.last().pose(), vc, 0, CURVE, DimensionalDoors.getConfig().getGraphicsConfig().riftSize * rift.size / 150, 0);//0xF1234568L * rift.hashCode()); + matrices.popPose(); + } + + private void renderTesseract(VertexConsumer vc, DetachedRiftBlockEntity rift, PoseStack matrices, float tickDelta) { + double radian = this.nextAngle(rift, tickDelta) * TrigMath.DEG_TO_RAD; + RGBA color = rift.getColor(); + if (Objects.equals(color, RGBA.NONE)) { + color = DEFAULT_COLOR; + } + + matrices.pushPose(); + + matrices.translate(0.5, 0.5, 0.5); + matrices.scale(0.25f, 0.25f, 0.25f); + + TESSERACT.draw(matrices.last().pose(), vc, color, radian); + + matrices.popPose(); + } + + private double nextAngle(DetachedRiftBlockEntity rift, float tickDelta) { + rift.renderAngle = (rift.renderAngle + 5 * tickDelta) % 360; + return rift.renderAngle; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/DimensionRenderering.java b/common/src/main/java/org/dimdev/dimdoors/client/DimensionRenderering.java new file mode 100644 index 00000000..039c53da --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/DimensionRenderering.java @@ -0,0 +1,108 @@ +package org.dimdev.dimdoors.client; + +import java.util.List; + +import com.mojang.blaze3d.systems.RenderSystem; +import org.joml.Matrix4f; + +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +import net.fabricmc.fabric.api.client.rendering.v1.DimensionRenderingRegistry; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.listener.pocket.PocketListenerUtil; +import org.dimdev.dimdoors.world.ModDimensions; +import org.dimdev.dimdoors.world.pocket.type.addon.SkyAddon; + +public class DimensionRenderering { + private static final Identifier MOON_RENDER_PATH = DimensionalDoors.id("textures/other/limbo_moon.png"); + private static final Identifier SUN_RENDER_PATH = DimensionalDoors.id("textures/other/limbo_sun.png"); + + public static void initClient() { + DimensionRenderingRegistry.CloudRenderer noCloudRenderer = context -> { + }; + DimensionRenderingRegistry.registerCloudRenderer(ModDimensions.LIMBO, noCloudRenderer); + DimensionRenderingRegistry.registerCloudRenderer(ModDimensions.DUNGEON, noCloudRenderer); + DimensionRenderingRegistry.registerCloudRenderer(ModDimensions.PERSONAL, noCloudRenderer); + DimensionRenderingRegistry.registerCloudRenderer(ModDimensions.PUBLIC, noCloudRenderer); + + DimensionRenderingRegistry.registerSkyRenderer(ModDimensions.LIMBO, context -> renderLimboSky(context.matrixStack())); + + DimensionRenderingRegistry.SkyRenderer pocketRenderer = context -> { + ClientWorld world = context.world(); + MatrixStack matrices = context.matrixStack(); + List skyAddons = PocketListenerUtil.applicableAddonsClient(SkyAddon.class, world, context.camera().getBlockPos()); + SkyAddon skyAddon = null; + if (skyAddons.size() > 0) { + // There should really only be one of these. + // If anyone needs to use multiple SkyAddons then go ahead and change this. + skyAddon = skyAddons.get(0); + } + + if (skyAddon != null) { + RegistryKey key = skyAddon.getWorld(); + + DimensionRenderingRegistry.SkyRenderer skyRenderer = DimensionRenderingRegistry.getSkyRenderer(key); + + if (skyRenderer != null) { + skyRenderer.render(context); + } else { + + if (key.equals(World.END)) { + context.gameRenderer().getClient().worldRenderer.renderEndSky(matrices); + } else if (key.equals(ModDimensions.LIMBO)) { + renderLimboSky(matrices); + } + } + } + }; + + DimensionRenderingRegistry.registerSkyRenderer(ModDimensions.DUNGEON, pocketRenderer); + DimensionRenderingRegistry.registerSkyRenderer(ModDimensions.PERSONAL, pocketRenderer); + DimensionRenderingRegistry.registerSkyRenderer(ModDimensions.PUBLIC, pocketRenderer); + } + + private static void renderLimboSky(MatrixStack matrices) { + Matrix4f matrix4f = matrices.peek().getPositionMatrix(); + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferBuilder = tessellator.getBuffer(); + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.depthMask(false); + RenderSystem.setShader(GameRenderer::getPositionTexColorProgram); + RenderSystem.setShaderColor(1, 1, 1, 1); + + float s = 30.0F; + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.setShaderTexture(0, SUN_RENDER_PATH); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); + bufferBuilder.vertex(matrix4f, -s, 100.0F, -s).texture(0.0F, 0.0F).next(); + bufferBuilder.vertex(matrix4f, s, 100.0F, -s).texture(1.0F, 0.0F).next(); + bufferBuilder.vertex(matrix4f, s, 100.0F, s).texture(1.0F, 1.0F).next(); + bufferBuilder.vertex(matrix4f, -s, 100.0F, s).texture(0.0F, 1.0F).next(); + tessellator.draw(); +// BufferRenderer.draw(bufferBuilder); + RenderSystem.setShaderTexture(0, MOON_RENDER_PATH); + bufferBuilder.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE); + bufferBuilder.vertex(matrix4f, -s, -100.0F, -s).texture(0.0F, 0.0F).next(); + bufferBuilder.vertex(matrix4f, s, -100.0F, -s).texture(1.0F, 0.0F).next(); + bufferBuilder.vertex(matrix4f, s, -100.0F, s).texture(1.0F, 1.0F).next(); + bufferBuilder.vertex(matrix4f, -s, -100.0F, s).texture(0.0F, 1.0F).next(); + tessellator.draw(); +// BufferRenderer.draw(bufferBuilder); + + RenderSystem.depthMask(true); +// RenderSystem.enableTexture(); + RenderSystem.disableBlend(); + } + +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorModelVariantProvider.java b/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorModelVariantProvider.java new file mode 100644 index 00000000..647aef74 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorModelVariantProvider.java @@ -0,0 +1,53 @@ +package org.dimdev.dimdoors.client; + +import java.util.ArrayList; +import java.util.Set; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.block.Block; +import net.minecraft.client.render.model.UnbakedModel; +import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.registry.Registries; +import net.minecraft.state.property.Property; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.client.model.ModelProviderContext; +import net.fabricmc.fabric.api.client.model.ModelProviderException; +import net.fabricmc.fabric.api.client.model.ModelVariantProvider; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.block.door.DimensionalDoorBlockRegistrar; +import org.dimdev.dimdoors.item.door.DimensionalDoorItemRegistrar; + +public class DimensionalDoorModelVariantProvider implements ModelVariantProvider { //TODO: Move to fabric if needed still + private static final Identifier childItem = DimensionalDoors.id("item/child_item"); + + @Override + public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) throws ModelProviderException { + Identifier identifier = new Identifier(modelId.getNamespace(), modelId.getPath()); + + DimensionalDoorBlockRegistrar blockRegistrar = DimensionalDoors.getDimensionalDoorBlockRegistrar(); + if (blockRegistrar.isMapped(identifier)) { + Identifier mapped = blockRegistrar.get(identifier); + //ModelIdentifier newId = new ModelIdentifier(mapped, modelId.getVariant()); + //UnbakedModel model = context.loadModel(newId); + //if (model != null) return model; + + Block original = Registries.BLOCK.get(mapped); + Set originalProperties = original.getStateManager().getProperties().stream().map(Property::getName).collect(Collectors.toSet()); + + ArrayList variantArray = new ArrayList<>(); + for (String part : modelId.getVariant().split(",")) { + if (originalProperties.contains(part.split("=")[0])) variantArray.add(part); + } + String variant = String.join(",", variantArray); + ModelIdentifier newId = new ModelIdentifier(mapped, variant); + return context.loadModel(newId); + } else if (identifier.getPath().startsWith(DimensionalDoorItemRegistrar.PREFIX)) { + return context.loadModel(childItem); + } + return null; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorsClientInitializer.java b/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorsClientInitializer.java new file mode 100644 index 00000000..2eff6e36 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/DimensionalDoorsClientInitializer.java @@ -0,0 +1,54 @@ +package org.dimdev.dimdoors.client; + +import dev.architectury.event.events.client.ClientPlayerEvent; +import dev.architectury.registry.client.rendering.BlockEntityRendererRegistry; +import dev.architectury.registry.menu.MenuRegistry; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.dimdev.dimdoors.block.ModBlocks; +import org.dimdev.dimdoors.block.entity.ModBlockEntityTypes; +import org.dimdev.dimdoors.client.screen.TesselatingLoomScreen; +import org.dimdev.dimdoors.entity.ModEntityTypes; +import org.dimdev.dimdoors.fluid.ModFluids; +import org.dimdev.dimdoors.network.client.ExtendedClientPlayNetworkHandler; +import org.dimdev.dimdoors.particle.ModParticleTypes; +import org.dimdev.dimdoors.screen.ModScreenHandlerTypes; + +@Environment(EnvType.CLIENT) +public class DimensionalDoorsClientInitializer implements ClientModInitializer { + @Override + public void onInitializeClient() { +// ModelLoadingRegistry.INSTANCE.registerVariantProvider((manager) -> new DimensionalDoorModelVariantProvider()); + + MenuRegistry.registerScreenFactory(ModScreenHandlerTypes.TESSELATING_LOOM, TesselatingLoomScreen::new); + ModEntityTypes.initClient(); + ModFluids.initClient(); + ModBlockEntityTypes.initClient(); + BlockEntityRendererRegistry.register(ModBlockEntityTypes.ENTRANCE_RIFT, new BlockEntityRendererProvider<>() { + @Override + public BlockEntityRenderer create(Context context) { + return new EntranceRiftBlockEntityRenderer(context); + return null; + } + }); + BlockEntityRendererRegistry.register(ModBlockEntityTypes.DETACHED_RIFT, ctx -> new DetachedRiftBlockEntityRenderer()); + ModBlocks.initClient(); + ModEntityModelLayers.initClient(); + ModParticleTypes.initClient(); + + DimensionRenderering.initClient(); + + registerListeners(); + } + + private void registerListeners() { + ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> ((ExtendedClientPlayNetworkHandler) player).getDimDoorsPacketHandler().init()); + + ClientPlayerEvent.CLIENT_PLAYER_QUIT.register(player -> ((ExtendedClientPlayNetworkHandler) player).getDimDoorsPacketHandler().unregister()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/EntranceRiftBlockEntityRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/EntranceRiftBlockEntityRenderer.java new file mode 100644 index 00000000..1586e347 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/EntranceRiftBlockEntityRenderer.java @@ -0,0 +1,35 @@ +package org.dimdev.dimdoors.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import org.dimdev.dimdoors.api.client.DimensionalPortalRenderer; +import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import org.dimdev.dimdoors.item.ModItems; +import org.dimdev.dimdoors.rift.targets.IdMarker; + +@Environment(EnvType.CLIENT) +public class EntranceRiftBlockEntityRenderer implements BlockEntityRenderer { + @Override + public void render(EntranceRiftBlockEntity blockEntity, float tickDelta, PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, int light, int overlay) { + if (Minecraft.getInstance().player != null) + if (Minecraft.getInstance().player.getItemInHand(InteractionHand.MAIN_HAND).is(ModItems.RIFT_CONFIGURATION_TOOL.get())) + if (blockEntity.getData().getDestination() instanceof IdMarker idMarker) { + matrixStack.pushPose(); + matrixStack.translate(0.5, 0.5, 0.5); + + Minecraft.getInstance().font.drawInBatch(Component.literal(String.valueOf(idMarker.getId())), 0f, 0f, 0xffffffff, false, matrixStack.last().pose(), vertexConsumerProvider, Font.DisplayMode.NORMAL, 0x000000, LightTexture.FULL_BRIGHT); + + matrixStack.popPose(); + } + + DimensionalPortalRenderer.renderDimensionalPortal(matrixStack, vertexConsumerProvider, blockEntity.getTransformer(), tickDelta, light, overlay, blockEntity.isTall()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/MaskModel.java b/common/src/main/java/org/dimdev/dimdoors/client/MaskModel.java new file mode 100644 index 00000000..cf1f801b --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/MaskModel.java @@ -0,0 +1,20 @@ +package org.dimdev.dimdoors.client; + +//import software.bernie.geckolib3.model.AnimatedGeoModel; + +//public class MaskModel extends AnimatedGeoModel { +// @Override +// public Identifier getModelLocation(MaskEntity object) { +// return Util.id("geo/mob/mask/mask.geo.json"); +// } +// +// @Override +// public Identifier getTextureLocation(MaskEntity object) { +// return Util.id("textures/mob/mask/mask.png"); +// } +// +// @Override +// public Identifier getAnimationFileLocation(MaskEntity animatable) { +// return Util.id("animations/mob/mask/mask.animation.json"); +// } +//} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/MaskRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/MaskRenderer.java new file mode 100644 index 00000000..40088921 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/MaskRenderer.java @@ -0,0 +1,11 @@ +package org.dimdev.dimdoors.client; + +//import software.bernie.geckolib3.renderers.geo.GeoEntityRenderer; + +//@Environment(EnvType.CLIENT) +//public class MaskRenderer extends GeoEntityRenderer { +// public MaskRenderer(EntityRendererFactory.Context ctx) { +// super(ctx, new MaskModel()); +// this.shadowRadius = 0.7f; +// } +//} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/ModEntityModelLayers.java b/common/src/main/java/org/dimdev/dimdoors/client/ModEntityModelLayers.java new file mode 100644 index 00000000..eacdb4b9 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/ModEntityModelLayers.java @@ -0,0 +1,17 @@ +package org.dimdev.dimdoors.client; + +import dev.architectury.registry.client.level.entity.EntityModelLayerRegistry; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.render.entity.model.EntityModelLayer; + +import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; + +import org.dimdev.dimdoors.DimensionalDoors; + +public class ModEntityModelLayers { + public static ModelLayerLocation MONOLITH = new ModelLayerLocation(DimensionalDoors.id("monolith"), "body"); + + public static void initClient() { + EntityModelLayerRegistry.register(MONOLITH, MonolithModel::getTexturedModelData); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/ModShaders.java b/common/src/main/java/org/dimdev/dimdoors/client/ModShaders.java new file mode 100644 index 00000000..2f2a3771 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/ModShaders.java @@ -0,0 +1,18 @@ +package org.dimdev.dimdoors.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.renderer.ShaderInstance; + +@Environment(EnvType.CLIENT) +public class ModShaders { + private static ShaderInstance DIMENSIONAL_PORTAL = null; + + public static void setDimensionalPortal(ShaderInstance dimensionalPortal) { + DIMENSIONAL_PORTAL = dimensionalPortal; + } + + public static ShaderInstance getDimensionalPortal() { + return DIMENSIONAL_PORTAL; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/MonolithModel.java b/common/src/main/java/org/dimdev/dimdoors/client/MonolithModel.java new file mode 100644 index 00000000..730ac0f5 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/MonolithModel.java @@ -0,0 +1,79 @@ +package org.dimdev.dimdoors.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.model.EntityModel; +import net.minecraft.client.model.geom.ModelPart; +import net.minecraft.client.model.geom.PartPose; +import net.minecraft.client.model.geom.builders.CubeListBuilder; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.minecraft.client.model.geom.builders.MeshDefinition; +import net.minecraft.client.model.geom.builders.PartDefinition; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import org.dimdev.dimdoors.entity.MonolithEntity; + +@Environment(EnvType.CLIENT) +public class MonolithModel extends EntityModel { + private final ModelPart body; + + public MonolithModel(EntityRendererProvider.Context context) { + super(MyRenderLayer::getMonolith); + this.body = context.bakeLayer(ModEntityModelLayers.MONOLITH); + } + + public static LayerDefinition getTexturedModelData() { + MeshDefinition modelData = new MeshDefinition(); + PartDefinition modelPartData = modelData.getRoot(); + modelPartData.addOrReplaceChild("body", CubeListBuilder.create().texOffs(0, 0).addBox(-23.5F, -23.5F, 0, 49.0F, 49.0F, 1.0F, false), PartPose.ZERO); + return LayerDefinition.create(modelData, 102, 51); + } + + @Override + public void renderToBuffer(PoseStack matrixStack, VertexConsumer consumer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + matrixStack.pushPose(); + matrixStack.scale(3.0625f, 3.0625f, 3.0625f); + + PoseStack.Pose entry = matrixStack.last(); + + consumer.vertex(entry.pose(), -1, -0.5f, 0) + .color(red, green, blue, alpha) + .uv(0,0) + .overlayCoords(packedOverlay) + .uv2(packedLight) + .normal(entry.normal(), 0, 0, 1) + .endVertex(); + consumer.vertex(entry.pose(), -1, 0.5f, 0) + .color(red, green, blue, alpha) + .uv(0, 1) + .overlayCoords(packedOverlay) + .uv2(packedLight) + .normal(entry.normal(), 0, 0, 1) + .endVertex(); + consumer.vertex(entry.pose(), 1, 0.5f, 0) + .color(red, green, blue, alpha) + .uv(1,1) + .overlayCoords(packedOverlay) + .uv2(packedLight) + .normal(entry.normal(), 0, 0, 1) + .endVertex(); + consumer.vertex(entry.pose(), 1, -0.5f, 0) + .color(red, green, blue, alpha) + .uv(1, 0) + .overlayCoords(packedOverlay) + .uv2(packedLight) + .normal(entry.normal(), 0, 0, 1) + .endVertex(); + + matrixStack.popPose(); + +// this.body.render(matrixStack, consumer, packedLight, packedOverlay); + } + + @Override + public void setupAnim(MonolithEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + this.body.yRot = netHeadYaw * 0.017453292F; + this.body.xRot = headPitch * 0.017453292F; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/MonolithRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/MonolithRenderer.java new file mode 100644 index 00000000..b0847aab --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/MonolithRenderer.java @@ -0,0 +1,64 @@ +package org.dimdev.dimdoors.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.client.renderer.entity.MobRenderer; +import net.minecraft.resources.ResourceLocation; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.entity.MonolithEntity; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Environment(EnvType.CLIENT) +public class MonolithRenderer extends MobRenderer { + public static final List MONOLITH_TEXTURES = Stream.of( + DimensionalDoors.id("textures/mob/monolith/monolith0.png"), + DimensionalDoors.id("textures/mob/monolith/monolith1.png"), + DimensionalDoors.id("textures/mob/monolith/monolith2.png"), + DimensionalDoors.id("textures/mob/monolith/monolith3.png"), + DimensionalDoors.id("textures/mob/monolith/monolith4.png"), + DimensionalDoors.id("textures/mob/monolith/monolith5.png"), + DimensionalDoors.id("textures/mob/monolith/monolith6.png"), + DimensionalDoors.id("textures/mob/monolith/monolith7.png"), + DimensionalDoors.id("textures/mob/monolith/monolith8.png"), + DimensionalDoors.id("textures/mob/monolith/monolith9.png"), + DimensionalDoors.id("textures/mob/monolith/monolith10.png"), + DimensionalDoors.id("textures/mob/monolith/monolith11.png"), + DimensionalDoors.id("textures/mob/monolith/monolith12.png"), + DimensionalDoors.id("textures/mob/monolith/monolith13.png"), + DimensionalDoors.id("textures/mob/monolith/monolith14.png"), + DimensionalDoors.id("textures/mob/monolith/monolith15.png"), + DimensionalDoors.id("textures/mob/monolith/monolith16.png"), + DimensionalDoors.id("textures/mob/monolith/monolith17.png"), + DimensionalDoors.id("textures/mob/monolith/monolith18.png") + ).collect(Collectors.toList()); + + private static MonolithModel INSTANCE; + + public MonolithRenderer(EntityRendererProvider.Context ctx) { + super(ctx, INSTANCE = new MonolithModel(ctx), 0); + } + + public static MonolithModel getInstance() { + return INSTANCE; + } + + @Override + protected void scale(MonolithEntity entity, PoseStack matrices, float amount) { + matrices.scale(entity.getScale(), entity.getScale(), entity.getScale()); + } + + @Override + protected boolean shouldShowName(MonolithEntity mobEntity) { + return false; + } + + @Override + public ResourceLocation getTextureLocation(MonolithEntity entity) { + return MonolithRenderer.MONOLITH_TEXTURES.get(entity.getTextureState()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/MyRenderLayer.java b/common/src/main/java/org/dimdev/dimdoors/client/MyRenderLayer.java new file mode 100644 index 00000000..f42091e2 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/MyRenderLayer.java @@ -0,0 +1,82 @@ +package org.dimdev.dimdoors.client; + +import com.flowpowered.math.vector.VectorNi; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.RenderStateShard; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.RandomSource; +import org.dimdev.dimdoors.DimensionalDoors; + +import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_DST_COLOR; +import static org.lwjgl.opengl.GL11.GL_ZERO; + +@Environment(EnvType.CLIENT) +public class MyRenderLayer extends RenderType { + public static final ResourceLocation WARP_PATH = DimensionalDoors.id("textures/other/warp.png"); + public static final VectorNi COLORLESS = new VectorNi(255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255); + private static final ResourceLocation KEY_PATH = DimensionalDoors.id("textures/other/keyhole.png"); + private static final ResourceLocation KEYHOLE_LIGHT = DimensionalDoors.id("textures/other/keyhole_light.png"); + private static final RandomSource RANDOM = RandomSource.create(31100L); + + public MyRenderLayer(String string, VertexFormat vertexFormat, VertexFormat.Mode drawMode, int j, boolean bl, boolean bl2, Runnable runnable, Runnable runnable2) { + super(string, vertexFormat, drawMode, j, bl, bl2, runnable, runnable2); + } + + public static RenderType CRACK = RenderType.create("crack", + DefaultVertexFormat.POSITION_COLOR, + VertexFormat.Mode.QUADS, + 256, + false, + false, + CompositeState.builder() + .setCullState(CullStateShard.NO_CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setTextureState(RenderStateShard.NO_TEXTURE) + .setTransparencyState(new TransparencyStateShard("crack_transparency", + () -> { + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO); + }, + () -> { + RenderSystem.disableBlend(); + RenderSystem.defaultBlendFunc(); + }) + ) + .setShaderState(RenderStateShard.POSITION_COLOR_SHADER) + .createCompositeState(false) + ); + + public static RenderType TESSERACT = RenderType.create("tesseract", + DefaultVertexFormat.POSITION_COLOR_TEX, + VertexFormat.Mode.QUADS, + 256, + false, + false, + CompositeState.builder() + .setCullState(RenderStateShard.NO_CULL) + .setLightmapState(RenderStateShard.NO_LIGHTMAP) + .setTextureState(new TextureStateShard(DetachedRiftBlockEntityRenderer.TESSERACT_PATH, + false, + false) + ) + .setShaderState(RenderStateShard.POSITION_COLOR_TEX_SHADER) + .createCompositeState(false) + ); + + public static RenderType getMonolith(ResourceLocation texture) { + RenderType.CompositeState multiPhaseParameters = RenderType.CompositeState.builder().setTextureState(new TextureStateShard(texture, false, false)) + .setShaderState(new ShaderStateShard(GameRenderer::getRendertypeEntityTranslucentShader)) + .setTransparencyState(RenderStateShard.TRANSLUCENT_TRANSPARENCY) + .setCullState(RenderStateShard.NO_CULL) + .setLightmapState(RenderStateShard.LIGHTMAP) + .setDepthTestState(RenderStateShard.NO_DEPTH_TEST) + .setOverlayState(RenderStateShard.OVERLAY).createCompositeState(false); + return RenderType.create("monolith", DefaultVertexFormat.NEW_ENTITY, VertexFormat.Mode.QUADS, 256, true, true, multiPhaseParameters); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/RiftCrackRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/RiftCrackRenderer.java new file mode 100644 index 00000000..e2aeae89 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/RiftCrackRenderer.java @@ -0,0 +1,68 @@ +package org.dimdev.dimdoors.client; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.Util; +import org.joml.Matrix4f; + +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.util.Util; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; + +@Environment(EnvType.CLIENT) +public final class RiftCrackRenderer { + public static void drawCrack(Matrix4f model, VertexConsumer vc, float riftRotation, RiftCurves.PolygonInfo poly, double size, long riftRandom) { + // Calculate the proper size for the rift render + double scale = size / (poly.maxX - poly.minX); + + // Calculate the midpoint of the fractal bounding box + double offsetX = (poly.maxX + poly.minX) / 2d; + double offsetY = (poly.maxY + poly.minY) / 2d; + double offsetZ = 0; + + // Changes how far the triangles move + // TODO: Actually seems to control the glow around the rift + float motionMagnitude = 0.6F; + + // Changes how quickly the triangles move + float motionSpeed = 0.014f; + + // Number of individual jitter waveforms to generate + // changes how "together" the overall motions are + int jCount = 10; + + float time = ((Util.getEpochMillis() + riftRandom) % 2000000) * motionSpeed; + double[] jitters = new double[jCount]; + + double jitterScale = DimensionalDoors.getConfig().getGraphicsConfig().riftJitter * size * size * size / 2000f; + // We use random constants here on purpose just to get different wave forms + double xJitter = jitterScale * Math.sin(1.1f * time*size) * Math.sin(0.8f * time); + double yJitter = jitterScale * Math.sin(1.2f * time*size) * Math.sin(0.9f * time); + double zJitter = jitterScale * Math.sin(1.3f * time*size) * Math.sin(0.7f * time); + + // generate a series of waveforms + for (int i = 0; i < jCount; i += 1) { + jitters[i] = Math.sin((1F + i / 10F) * time) * Math.cos(1F - i / 10F * time) * motionMagnitude; + } + + // Draw the rift + for (RiftCurves.Point p : poly.points) { + // Reduces most overlap between triangles inside the rift's center + int jIndex = Math.abs((p.x + p.y) * (p.x + p.y + 1) / 2 + p.y); + + double x = (p.x + jitters[(jIndex + 1) % jCount] - offsetX) * Math.cos(Math.toRadians(riftRotation)) - jitters[(jIndex + 2) % jCount] * Math.sin(Math.toRadians(riftRotation)); + double y = p.y + jitters[jIndex % jCount] - offsetY; + double z = (p.x + jitters[(jIndex + 2) % jCount] - offsetZ) * Math.sin(Math.toRadians(riftRotation)) + jitters[(jIndex + 2) % jCount] * Math.cos(Math.toRadians(riftRotation)); + + // Scale the rift + x *= scale; + y *= scale; + z *= scale; + + vc.vertex(model, (float) (x + xJitter), (float) (y + yJitter), (float) (z + zJitter)).color(0.08f, 0.08f, 0.08f, .3f).endVertex(); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/RiftCurves.java b/common/src/main/java/org/dimdev/dimdoors/client/RiftCurves.java new file mode 100644 index 00000000..cb204a92 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/RiftCurves.java @@ -0,0 +1,353 @@ +package org.dimdev.dimdoors.client; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.poly2tri.Poly2Tri; +import org.poly2tri.geometry.polygon.Polygon; +import org.poly2tri.geometry.polygon.PolygonPoint; +import org.poly2tri.triangulation.TriangulationPoint; +import org.poly2tri.triangulation.delaunay.DelaunayTriangle; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +// https://en.wikipedia.org/wiki/L-system +// +// System: {rules, angle, start_string} +// Rules: rule1:rule_2:...:rule_n +// Rule: from_substring>to_substring +// +// Each iteration, rules are applied to the string. After, string +// is interpreted as commands for a pencil on a sheet of paper: +// F - move forward by one step +// + - turn clockwise by angle +// - - turn counter-clockwise by angle +// [ - save state (push to stack) +// ] - restore state (pop from stack) +@Environment(EnvType.CLIENT) +public final class RiftCurves { + public static final List CURVES = new ArrayList<>(); + + public static final String[] TERDRAGON = {"F>+F----F++++F-", "60", "F"}; + public static final String[] DRAGON = {"X>X+YF:Y>FX-Y", "90", "FX"}; + public static final String[] TWINDRAGON = {"X>X+YF:Y>FX-Y", "90", "FX--FX"}; + public static final String[] VORTEX = {"X>X+YF:Y>FX-Y", "90", "FX---FX"}; + + static { + CURVES.add(generate(TERDRAGON, 4)); + CURVES.add(generate(TERDRAGON, 5)); + CURVES.add(generate(TERDRAGON, 7)); + CURVES.add(generate(VORTEX, 9)); + CURVES.add(generate(VORTEX, 10)); + CURVES.add(generate(VORTEX, 11)); + CURVES.add(generate(TWINDRAGON, 7)); + CURVES.add(generate(TWINDRAGON, 8)); + CURVES.add(generate(TWINDRAGON, 9)); + CURVES.add(generate(TWINDRAGON, 10)); + CURVES.add(generate(DRAGON, 8)); + CURVES.add(generate(DRAGON, 9)); + CURVES.add(generate(DRAGON, 10)); + CURVES.add(generate(DRAGON, 11)); + } + + private static PolygonInfo generate(String[] args, int steps) { + //Parse the rules from the first index + String[] rules = args[0].split(":"); + HashMap lSystemsRule = new HashMap<>(); + + for (String rule : rules) { + String[] parts = rule.split(">"); + lSystemsRule.put(parts[0], parts[1]); + } + + //get the angle for each turn + int angle = Integer.parseInt(args[1]); + + //String to hold the output + //Initialize with starting string + String output; + + //generate the l-system + output = generate(args[2], steps, lSystemsRule); + + //get the boundary of the polygon + List polygon = getBoundary(convertToPoints(angle, output, steps)); + + //replace the boundary of the polygon with a series of points representing triangles for rendering + return new PolygonInfo(tesselate(polygon)); + } + + /** + * Takes an unordered list of points comprising a fractal curve and builds a + * closed polygon around it + */ + public static List getBoundary(ArrayList input) { + // store max x and y values to create bounding box + int maxY = Integer.MIN_VALUE; + int maxX = Integer.MIN_VALUE; + int minY = Integer.MAX_VALUE; + int minX = Integer.MAX_VALUE; + + // store possible singles here + HashSet singles = new HashSet<>(); + + // list to store confirmed singles and output in the correct order + ArrayList output = new ArrayList<>(); + + // sort into Hashmaps and hashsets to make contains operations possible, + // while testing for duplicates + for (double[] point : input) { + int xCoord = (int) Math.round(point[0]); + int yCoord = (int) Math.round(point[1]); + + if (xCoord > maxX) maxX = xCoord; + if (xCoord < minX) minX = xCoord; + if (yCoord > maxY) maxY = yCoord; + if (yCoord < minY) minY = yCoord; + + singles.add(new Point(xCoord, yCoord)); + } + + // find a suitable starting point + Point startPoint = new Point(minX, minY); + Point prevPoint = startPoint; + + while (startPoint.y < maxY) { + if (singles.contains(startPoint)) { + break; + } + + startPoint = new Point(startPoint.x, startPoint.y + 1); + } + + // record the first point so we know where to stop + final Point firstPoint = startPoint; + + // determine the direction to start searching from + Point direction = getVector(prevPoint, startPoint); + + //output.add(startPoint); + + // loop around in a clockwise circle, jumping to the next point when we + // find it and resetting the direction to start seaching from + // to the last found point. This ensures we always find the next + // *outside* point + do { + // get the next point + direction = rotateCounterClockwise(direction); + Point target = new Point(startPoint.x + direction.x, startPoint.y + direction.y); + + // see if that point is part of our fractal curve + if (singles.contains(target)) { + if (target.equals(firstPoint)) { + output.remove(output.get(output.size() - 1)); + break; + } + // get the vector to start from for the next cycle + direction = getVector(startPoint, target); + + // prune zero width spikes + if (output.size() > 1 && output.get(output.size() - 2).equals(target)) { + output.remove(output.size() - 1); + } else { + + if (output.contains(target) && !target.equals(output.get(0))) { + int index = output.indexOf(target); + while (output.size() > index) { + output.remove(output.size() - 1); + } + } + output.add(target); + } + startPoint = target; + } + } + while (!(output.get(output.size() - 1).equals(firstPoint) && output.size() > 1) && output.size() < singles.size()); + + return output; + } + + /** + * using a point as a 2d vector, normalize it (sorta) + */ + public static Point getVector(Point origin, Point destination) { + int[] normals = {origin.x - destination.x, origin.y - destination.y}; + + for (int i = 0; i < normals.length; i++) { + if (normals[i] > 0) { + normals[i] = 1; + } else if (normals[i] == 0) { + normals[i] = 0; + } else if (normals[i] < 0) { + normals[i] = -1; + } + } + return new Point(normals[0], normals[1]); + } + + /** + * rotate a normal around the origin + */ + public static Point rotateCounterClockwise(Point previous) { + return new Point( + (int) (previous.x * Math.cos(Math.toRadians(90)) - previous.y * Math.sin(Math.toRadians(90))), + (int) (previous.x * Math.sin(Math.toRadians(90)) + previous.y * Math.cos(Math.toRadians(90))) + ); + } + + /** + * Take an l-system string and convert it into a series of points on a + * cartesian grid. Designed to keep terdragons oriented the same direction + * regardless of iterations + */ + public static ArrayList convertToPoints(double angle, String system, int generations) { + + // determine the starting point and rotation to begin drawing from + int rotation = generations % 2 == 0 ? 2 : 4; + double[] currentState = {(generations + rotation) % 4 * 90, 0, 0}; + + // the output for a totally unordered list of points defining the curve + ArrayList output = new ArrayList<>(); + + // the stack used to deal with branching l-systems that use [ and ] + ArrayDeque state = new ArrayDeque<>(); + + // perform the rules corresponding to each symbol in the l-system + for (Character ch : system.toCharArray()) { + double motion = 1; + + // move forward + if (ch == 'F') { + currentState[1] -= Math.cos(Math.toRadians(currentState[0])) * motion; + currentState[2] -= Math.sin(Math.toRadians(currentState[0])) * motion; + output.add(new double[]{currentState[1], currentState[2]}); + } + // start branch + if (ch == '[') { + + state.push(currentState.clone()); + } + // turn left + if (ch == '-') { + currentState = new double[]{(currentState[0] - angle) % 360, currentState[1], currentState[2]}; + } + // turn right + if (ch == '+') { + currentState[0] = (currentState[0] + angle) % 360; + } + // end branch and return to previous fork + if (ch == ']') { + currentState = state.pop(); + } + } + return output; + } + + /** + * grow and l-system string based on the rules provided in the args + */ + public static String generate(String start, int steps, HashMap lSystemsRule) { + + while (steps > 0) { + StringBuilder output = new StringBuilder(); + + for (Character ch : start.toCharArray()) { + // get the rule applicable for the variable + String data = lSystemsRule.get(ch.toString()); + + // handle constants for rule-less symbols + if (data == null) { + data = ch.toString(); + } + output.append(data); + } + steps--; + start = output.toString(); + } + return start; + } + + // a data container class to transmit the important information about the polygon + public static class PolygonInfo { + public final ArrayList points; + + public int maxX; + public final int maxY; + public final int minX; + public final int minY; + + public PolygonInfo(ArrayList points) { + int minX, minY, maxX, maxY; + minX = minY = maxX = maxY = 0; + + // Find bounding box of the polygon + this.points = points; + if (points.size() > 0) { + Point firstPoint = points.get(0); + minX = firstPoint.x; + minY = firstPoint.y; + maxX = firstPoint.x; + maxY = firstPoint.y; + + for (Point point : points) { + if (point.x < minX) minX = point.x; + if (point.y < minY) minY = point.y; + if (point.x > maxX) maxX = point.x; + if (point.y > maxY) maxY = point.y; + } + } + + this.minX = minX; + this.minY = minY; + this.maxX = maxX; + this.maxY = maxY; + } + } + + // TODO: Use the GLU tesselator (GPU) instead of poly2tri (CPU): http://wiki.lwjgl.org/wiki/Using_GLU_Tesselator.html + public static ArrayList tesselate(List polygon) { + ArrayList points = new ArrayList<>(); + + ArrayList polyPoints = new ArrayList<>(); + + for (Point p : polygon) { + polyPoints.add(new PolygonPoint(p.x, p.y)); + } + + Polygon poly = new Polygon(polyPoints); + Poly2Tri.triangulate(poly); + + ArrayList tris = (ArrayList) poly.getTriangles(); + + for (DelaunayTriangle tri : tris) { + for (TriangulationPoint tpoint : tri.points) { + points.add(new Point((int) tpoint.getX(), (int) tpoint.getY())); + } + } + return points; + } + + public static class Point { + public final int x; + public final int y; + + private Point(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + public boolean equals(Object o) { + return o instanceof Point && ((Point) o).x == this.x && ((Point) o).y == this.y; + } + + @Override + public int hashCode() { + return this.x * 31 + this.y; + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/ToolTipHelper.java b/common/src/main/java/org/dimdev/dimdoors/client/ToolTipHelper.java new file mode 100644 index 00000000..8ba817b8 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/ToolTipHelper.java @@ -0,0 +1,18 @@ +package org.dimdev.dimdoors.client; + +import net.minecraft.client.resources.language.I18n; +import net.minecraft.network.chat.Component; + +import java.util.List; + +public class ToolTipHelper { + public static void processTranslation(List list, String key, Object... args) { + if(I18n.exists(key)) { + list.add(Component.translatable(key, args)); + } else { + for (int i = 0; I18n.exists(key + i); i++) { + list.add(Component.translatable(key + i, args)); + } + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/UnderlaidChildItemRenderer.java b/common/src/main/java/org/dimdev/dimdoors/client/UnderlaidChildItemRenderer.java new file mode 100644 index 00000000..38556bf1 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/UnderlaidChildItemRenderer.java @@ -0,0 +1,57 @@ +package org.dimdev.dimdoors.client; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.json.ModelTransformationMode; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.rendering.v1.BuiltinItemRendererRegistry; + +import org.dimdev.dimdoors.item.door.DimensionalDoorItemRegistrar; + +@Environment(EnvType.CLIENT) +public class UnderlaidChildItemRenderer implements BuiltinItemRendererRegistry.DynamicItemRenderer {//TODO: Move to fabric if needed still + private final ItemStack underlay; + + public UnderlaidChildItemRenderer(Item underlay) { + this.underlay = new ItemStack(underlay); + } + + public UnderlaidChildItemRenderer(ItemStack underlay) { + this.underlay = underlay; + } + + @Override + public void render(ItemStack stack, ModelTransformationMode mode, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) { + if (!(stack.getItem() instanceof DimensionalDoorItemRegistrar.ChildItem childItem)) throw new UnsupportedOperationException("Can only use UnderlaidChildItemRenderer for ChildItems"); + + matrices.push(); + matrices.translate(0.5D, 0.5D, 0.5D); + + ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); + + // TODO: refactor + matrices.push(); + childItem.transform(matrices); + matrices.scale(1, 1, 0.5f); + itemRenderer.renderItem(underlay, ModelTransformationMode.NONE, light, overlay, matrices, vertexConsumers, null, 0); + matrices.pop(); + + ItemStack originalItemStack = new ItemStack( + childItem.getOriginalItem(), + stack.getCount()); + originalItemStack.setNbt(stack.getNbt()); + + matrices.push(); +// childItem.transform(matrices); + itemRenderer.renderItem(originalItemStack, ModelTransformationMode.NONE, light, overlay, matrices, vertexConsumers, null, 0); + matrices.pop(); + + matrices.pop(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenu.java b/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenu.java new file mode 100644 index 00000000..dc5506d0 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenu.java @@ -0,0 +1,13 @@ +package org.dimdev.dimdoors.client.config; + +import me.shedaniel.autoconfig.AutoConfig; + +import net.minecraft.client.gui.screen.Screen; + +import org.dimdev.dimdoors.ModConfig; + +public class ModMenu { + public static Screen getConfigScreen(Screen previous) { + return AutoConfig.getConfigScreen(ModConfig.class, previous).get(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenuImpl.java b/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenuImpl.java new file mode 100644 index 00000000..a0394095 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/config/ModMenuImpl.java @@ -0,0 +1,15 @@ +package org.dimdev.dimdoors.client.config; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class ModMenuImpl implements ModMenuApi {//TODO: Move to fabric + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return ModMenu::getConfigScreen; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/screen/TesselatingLoomScreen.java b/common/src/main/java/org/dimdev/dimdoors/client/screen/TesselatingLoomScreen.java new file mode 100644 index 00000000..9dd9d462 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/screen/TesselatingLoomScreen.java @@ -0,0 +1,105 @@ +package org.dimdev.dimdoors.client.screen; + +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +import net.minecraft.client.gui.components.ImageButton; +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; +import net.minecraft.client.gui.screens.recipebook.RecipeBookComponent; +import net.minecraft.client.gui.screens.recipebook.RecipeUpdateListener; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.ClickType; +import net.minecraft.world.inventory.Slot; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.screen.TesselatingScreenHandler; + +public class TesselatingLoomScreen extends AbstractContainerScreen implements RecipeUpdateListener { + private static final ResourceLocation TEXTURE = DimensionalDoors.id("textures/screen/container/tesselating_loom.png"); + private static final ResourceLocation RECIPE_BUTTON_TEXTURE = new ResourceLocation("textures/gui/recipe_button.png"); + + private final RecipeBookComponent recipeBook = new RecipeBookComponent(); + private boolean narrow; + + public TesselatingLoomScreen(TesselatingScreenHandler handler, Inventory inventory, Component title) { + super(handler, inventory, title); + } + + public void init() { + super.init(); + this.narrow = this.width < 379; + this.recipeBook.init(this.width, this.height, this.minecraft, this.narrow, this.menu); + this.leftPos = this.recipeBook.updateScreenPosition(this.width, this.imageWidth); + this.addRenderableWidget(new ImageButton(this.leftPos + 5, this.height / 2 - 49, 20, 18, 0, 0, 19, RECIPE_BUTTON_TEXTURE, (button) -> { + this.recipeBook.toggleVisibility(); + this.leftPos = this.recipeBook.updateScreenPosition(this.width, this.imageWidth); + button.setPosition(this.leftPos + 5, this.height / 2 - 49); + })); + this.addWidget(this.recipeBook); + this.setInitialFocus(this.recipeBook); + this.titleLabelX = (this.imageWidth - this.font.width(this.title)) / 2; + } + + + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); + if (this.recipeBook.isVisible() && this.narrow) { + this.renderBg(matrices, delta, mouseX, mouseY); + this.recipeBook.render(matrices, mouseX, mouseY, delta); + } else { + this.recipeBook.render(matrices, mouseX, mouseY, delta); + super.render(matrices, mouseX, mouseY, delta); + this.recipeBook.renderGhostRecipe(matrices, this.leftPos, this.topPos, true, delta); + } + + this.renderTooltip(matrices, mouseX, mouseY); + this.recipeBook.renderTooltip(matrices, this.leftPos, this.topPos, mouseX, mouseY); + } + + protected void renderBg(PoseStack matrices, float delta, int mouseX, int mouseY) { + RenderSystem.setShader(GameRenderer::getPositionTexShader); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + RenderSystem.setShaderTexture(0, TEXTURE); + int i = this.leftPos; + int j = (this.height - this.imageHeight) / 2; + GuiComponent.blit(matrices, i, j, 0, 0, this.imageWidth, this.imageWidth); + + if (this.menu.isWeaving()) { + int k = (this.menu).getBurnProgress(22); + GuiComponent.blit(matrices, i + 89, j + 34, 176, 0, k + 1, 16); + } + } + + protected boolean isHovering(int x, int y, int width, int height, double pointX, double pointY) { + return (!this.narrow || !this.recipeBook.isVisible()) && super.isHovering(x, y, width, height, pointX, pointY); + } + + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (this.recipeBook.mouseClicked(mouseX, mouseY, button)) { + this.setFocused(this.recipeBook); + return true; + } else { + return this.narrow && this.recipeBook.isVisible() || super.mouseClicked(mouseX, mouseY, button); + } + } + + protected boolean hasClickedOutside(double mouseX, double mouseY, int left, int top, int button) { + boolean bl = mouseX < (double)left || mouseY < (double)top || mouseX >= (double)(left + this.imageWidth) || mouseY >= (double)(top + this.imageHeight); + return this.recipeBook.hasClickedOutside(mouseX, mouseY, this.leftPos, this.topPos, this.imageWidth, this.imageHeight, button) && bl; + } + + protected void slotClicked(Slot slot, int slotId, int button, ClickType actionType) { + super.slotClicked(slot, slotId, button, actionType); + this.recipeBook.slotClicked(slot); + } + + public void recipesUpdated() { + this.recipeBook.recipesUpdated(); + } + + public RecipeBookComponent getRecipeBookComponent() { + return this.recipeBook; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Plane.java b/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Plane.java new file mode 100644 index 00000000..46e944d4 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Plane.java @@ -0,0 +1,77 @@ +package org.dimdev.dimdoors.client.tesseract; + +import com.flowpowered.math.matrix.Matrix4f; +import com.flowpowered.math.vector.Vector3f; +import com.flowpowered.math.vector.Vector4f; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.render.VertexConsumer; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.api.util.RGBA; + +import static com.flowpowered.math.TrigMath.cos; +import static com.flowpowered.math.TrigMath.sin; + +@Environment(EnvType.CLIENT) +public class Plane { + Vector4f[] vectors; + + public Plane(Vector4f vec1, Vector4f vec2, Vector4f vec3, Vector4f vec4) { + this.vectors = new Vector4f[]{vec1, vec2, vec3, vec4}; + } + + public void draw(org.joml.Matrix4f model, VertexConsumer vc, RGBA color, double radian) { + drawVertex(model, vc, rotYW(this.vectors[0], radian), 0, 0, color); + drawVertex(model, vc, rotYW(this.vectors[1], radian), 0, 1, color); + drawVertex(model, vc, rotYW(this.vectors[2], radian), 1, 1, color); + drawVertex(model, vc, rotYW(this.vectors[3], radian), 1, 0, color); + } + + private static void drawVertex(org.joml.Matrix4f model, VertexConsumer vc, Vector4f vector, int u, int v, RGBA color) { + double scalar = 1d / (vector.getW() + 1); + Vector3f scaled = vector.toVector3().mul(scalar); + vc.vertex(model, scaled.getX(), scaled.getY(), scaled.getZ()) + .color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()) + .texture(u, v) + .next(); + } + + private static Vector4f rotXW(Vector4f v, double angle) { + return Matrix4f.from( + cos(angle), 0, 0, sin(angle), + 0, 1, 0, 0, + 0, 0, 1, 0, + -sin(angle), 0, 0, cos(angle)) + .transform(v); + } + + private static Vector4f rotZW(Vector4f v, double angle) { + return Matrix4f.from( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, cos(angle), -sin(angle), + 0, 0, sin(angle), cos(angle)) + .transform(v); + } + + private static Vector4f rotYW(Vector4f v, double angle) { + return Matrix4f.from( + 1, 0, 0, 0, + 0, cos(angle), 0, sin(angle), + 0, 0, 1, 0, + 0, -sin(angle), 0, cos(angle)) + .transform(v); + } + + private static Vector4f rotXY(Vector4f v, double angle) { + return Matrix4f.from( + cos(angle), -sin(angle), 0, 0, + sin(angle), cos(angle), 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1) + .transform(v); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Tesseract.java b/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Tesseract.java new file mode 100644 index 00000000..b1567c37 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/tesseract/Tesseract.java @@ -0,0 +1,193 @@ +package org.dimdev.dimdoors.client.tesseract; + +import com.flowpowered.math.vector.Vector4f; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.minecraft.client.render.VertexConsumer; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.api.util.RGBA; + +@Environment(EnvType.CLIENT) +public class Tesseract { + private final Plane[] planes = new Plane[24]; + + public Tesseract() { + this.planes[0] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f) + ); + + this.planes[1] = new Plane( + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f) + ); + + this.planes[2] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f) + ); + + this.planes[3] = new Plane( + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f) + ); + + this.planes[4] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f) + ); + + this.planes[5] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f) + ); + + this.planes[6] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[7] = new Plane( + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f) + ); + + this.planes[8] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f) + ); + + this.planes[9] = new Plane( + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f) + ); + + this.planes[10] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[11] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[12] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f) + ); + + this.planes[13] = new Plane( + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[14] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f) + ); + + this.planes[15] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[16] = new Plane( + new Vector4f(-0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, -0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, -0.5f, -0.5f, 0.5f) + ); + + this.planes[17] = new Plane( + new Vector4f(-0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(-0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(-0.5f, 0.5f, -0.5f, 0.5f) + ); + + this.planes[18] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f) + ); + + this.planes[19] = new Plane( + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[20] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f) + ); + + this.planes[21] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f) + ); + + this.planes[22] = new Plane( + new Vector4f(0.5f, -0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, -0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, -0.5f, -0.5f, 0.5f) + ); + + this.planes[23] = new Plane( + new Vector4f(0.5f, 0.5f, -0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, -0.5f), + new Vector4f(0.5f, 0.5f, 0.5f, 0.5f), + new Vector4f(0.5f, 0.5f, -0.5f, 0.5f) + ); + } + + @Environment(EnvType.CLIENT) + public void draw(org.joml.Matrix4f model, VertexConsumer vc, RGBA color, double radian) { + for (Plane plane : this.planes) { + plane.draw(model, vc, color, radian); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/wthit/EntranceRiftProvider.java b/common/src/main/java/org/dimdev/dimdoors/client/wthit/EntranceRiftProvider.java new file mode 100644 index 00000000..76111a79 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/wthit/EntranceRiftProvider.java @@ -0,0 +1,38 @@ +package org.dimdev.dimdoors.client.wthit; + +import java.util.Objects; + +import mcp.mobius.waila.api.IBlockAccessor; +import mcp.mobius.waila.api.IBlockComponentProvider; +import mcp.mobius.waila.api.IPluginConfig; +import mcp.mobius.waila.api.ITooltip; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.Identifier; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; + +// FIXME: is not actually client sided +public enum EntranceRiftProvider implements IBlockComponentProvider { + INSTANCE; + + private static final Identifier ID = DimensionalDoors.id("entrance_rift_provider"); + + @Override + public void appendBody(ITooltip tooltip, IBlockAccessor accessor, IPluginConfig config) { + if (!config.getBoolean(ID)) { + return; + } + EntranceRiftBlockEntity blockEntity = ((EntranceRiftBlockEntity) accessor.getBlockEntity()); + VirtualTarget destination = Objects.requireNonNull(blockEntity).getDestination(); + if (destination != null) { + Text tKey = Text.translatable(destination.getType().getTranslationKey()); + Text main = Text.translatable("dimdoors.destination").append(": ").append(tKey); + tooltip.addLine(main); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/client/wthit/WthitPlugin.java b/common/src/main/java/org/dimdev/dimdoors/client/wthit/WthitPlugin.java new file mode 100644 index 00000000..98f0fa5a --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/client/wthit/WthitPlugin.java @@ -0,0 +1,15 @@ +package org.dimdev.dimdoors.client.wthit; + +import mcp.mobius.waila.api.IRegistrar; +import mcp.mobius.waila.api.IWailaPlugin; +import mcp.mobius.waila.api.TooltipPosition; + +import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; + +// FIXME: is not actually client sided +public class WthitPlugin implements IWailaPlugin { + @Override + public void register(IRegistrar registrar) { + registrar.addComponent(EntranceRiftProvider.INSTANCE, TooltipPosition.BODY, EntranceRiftBlockEntity.class); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/DimTeleportCommand.java b/common/src/main/java/org/dimdev/dimdoors/command/DimTeleportCommand.java new file mode 100644 index 00000000..e2449f85 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/DimTeleportCommand.java @@ -0,0 +1,65 @@ +package org.dimdev.dimdoors.command; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.FloatArgumentType; + +import net.minecraft.command.argument.DimensionArgumentType; +import net.minecraft.command.argument.Vec3ArgumentType; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.EulerAngle; +import net.minecraft.util.math.Vec3d; + +import org.dimdev.dimdoors.api.util.Location; +import org.dimdev.dimdoors.api.util.TeleportUtil; +import org.dimdev.dimdoors.api.util.math.MathUtil; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; + +public class DimTeleportCommand { + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(CommandManager.literal("dimteleport") + .requires(source -> source.hasPermissionLevel(4)) + .then(CommandManager + .argument("dimension", DimensionArgumentType.dimension()) + .executes(ctx -> { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + return teleport(player, DimensionArgumentType.getDimensionArgument(ctx, "dimension"), player.getPos(), MathUtil.entityEulerAngle(player)); + }) + .then(CommandManager + .argument("coordinates", Vec3ArgumentType.vec3()) + .executes(ctx -> { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + return teleport(player, DimensionArgumentType.getDimensionArgument(ctx, "dimension"), Vec3ArgumentType.getVec3(ctx, "coordinates"), MathUtil.entityEulerAngle(player)); + }) + .then(CommandManager + .argument("yaw", FloatArgumentType.floatArg()) + .executes( ctx -> { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + return teleport(player, DimensionArgumentType.getDimensionArgument(ctx, "dimension"), Vec3ArgumentType.getVec3(ctx, "coordinates"), new EulerAngle(player.getPitch(), FloatArgumentType.getFloat(ctx, "yaw"), 0)); + }) + .then(CommandManager + .argument("pitch", FloatArgumentType.floatArg()) + .executes( ctx -> { + ServerPlayerEntity player = ctx.getSource().getPlayer(); + return teleport(player, DimensionArgumentType.getDimensionArgument(ctx, "dimension"), Vec3ArgumentType.getVec3(ctx, "coordinates"), new EulerAngle(FloatArgumentType.getFloat(ctx, "pitch"), FloatArgumentType.getFloat(ctx, "yaw"), 0)); + }) + ) + ) + ) + ) + ); + } + + private static int teleport(Entity entity, ServerWorld dimension, Vec3d pos, EulerAngle angle) { + if(entity instanceof PlayerEntity) { + DimensionalRegistry.getRiftRegistry().setOverworldRift(entity.getUuid(), new Location((ServerWorld) entity.getEntityWorld(), entity.getBlockPos())); + } + TeleportUtil.teleport(entity, dimension, pos, angle, entity.getVelocity()); + return Command.SINGLE_SUCCESS; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/ModCommands.java b/common/src/main/java/org/dimdev/dimdoors/command/ModCommands.java new file mode 100644 index 00000000..4cefd5ca --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/ModCommands.java @@ -0,0 +1,12 @@ +package org.dimdev.dimdoors.command; + +import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; + +public final class ModCommands { + public static void init() { + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, dedicated) -> { + DimTeleportCommand.register(dispatcher); + PocketCommand.register(dispatcher); + }); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/PocketCommand.java b/common/src/main/java/org/dimdev/dimdoors/command/PocketCommand.java new file mode 100644 index 00000000..6fd284e6 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/PocketCommand.java @@ -0,0 +1,126 @@ +package org.dimdev.dimdoors.command; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; + +import net.fabricmc.loader.api.FabricLoader; + +import org.dimdev.dimdoors.api.util.BlockPlacementType; +import org.dimdev.dimdoors.command.arguments.BlockPlacementTypeArgumentType; +import org.dimdev.dimdoors.command.arguments.PocketTemplateArgumentType; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.PocketTemplate; +import org.dimdev.dimdoors.util.schematic.SchematicPlacer; + +import static net.minecraft.server.command.CommandManager.argument; +import static net.minecraft.server.command.CommandManager.literal; + +public class PocketCommand { + private static final Logger LOGGER = LogManager.getLogger(); + + // TODO: probably move somewhere else + public static final Map logSetting = new HashMap<>(); + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register( + literal("pocket") + .requires(source -> source.hasPermissionLevel(2)) + .then( + literal("schematic") + .then( + literal("place") + .then( + argument("pocket_template", new PocketTemplateArgumentType()) + .executes(ctx -> place(ctx.getSource().getPlayer(), PocketTemplateArgumentType.getValue(ctx, "pocket_template"), BlockPlacementType.SECTION_NO_UPDATE)) + .then( + argument("placement_type", new BlockPlacementTypeArgumentType()) + .executes(ctx -> place(ctx.getSource().getPlayer(), PocketTemplateArgumentType.getValue(ctx, "pocket_template"), BlockPlacementTypeArgumentType.getBlockPlacementType(ctx, "placement_type"))) + ) + ) + ) + .then( + literal("load") + .requires(source -> FabricLoader.getInstance().isModLoaded("worldedit")) + .then( + argument("pocket_template", new PocketTemplateArgumentType()) + .executes(ctx -> load(ctx.getSource(), PocketTemplateArgumentType.getValue(ctx, "pocket_template"))) + ) + ) + ) + .then( + literal("log") + // TODO: make command toggle logging of pocket creation to console if used from console + .then(literal("creation") + .requires(commandSource -> commandSource.getEntity() instanceof ServerPlayerEntity) + .executes(ctx -> { + ServerCommandSource commandSource = ctx.getSource(); + UUID playerUUID = commandSource.getPlayer().getUuid(); + if (logSetting.containsKey(playerUUID)) { + logSetting.remove(playerUUID); + commandSource.sendFeedback(Text.translatable("commands.pocket.log.creation.off"), false); + } else { + logSetting.put(playerUUID, commandSource); + commandSource.sendFeedback(Text.translatable("commands.pocket.log.creation.on"), false); + } + return Command.SINGLE_SUCCESS; + }) + ) + + ) + .then( + literal("dump") + .requires(src -> src.hasPermissionLevel(4)) + .executes(ctx -> { + ctx.getSource().sendFeedback(Text.of("Dumping pocket data"), false); + CompletableFuture.runAsync(() -> { + try { + PocketLoader.getInstance().dump(); + } catch (Exception e) { + LOGGER.error("Error dumping pocket data", e); + } + }).thenRun(() -> { + ctx.getSource().getServer().execute(() -> { + ctx.getSource().sendFeedback(Text.of("Dumped pocket data"), false); + }); + }); + return Command.SINGLE_SUCCESS; + }) + ) + ); + } + + private static int load(ServerCommandSource source, PocketTemplate template) throws CommandSyntaxException { + try { + return WorldeditHelper.load(source, template); + } catch (NoClassDefFoundError e) { + return 0; + } + } + + private static int place(ServerPlayerEntity source, PocketTemplate template, BlockPlacementType blockPlacementType) throws CommandSyntaxException { + SchematicPlacer.place( + template.getSchematic(), + source.getWorld(), + source.getBlockPos(), + blockPlacementType + ); + + String id = template.getId().toString(); + source.getCommandSource().sendFeedback(Text.translatable("commands.pocket.placedSchem", id, "" + source.getBlockPos().getX() + ", " + source.getBlockPos().getY() + ", " + source.getBlockPos().getZ(), source.world.getRegistryKey().getValue().toString()), true); + return Command.SINGLE_SUCCESS; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/WorldeditHelper.java b/common/src/main/java/org/dimdev/dimdoors/command/WorldeditHelper.java new file mode 100644 index 00000000..101d803e --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/WorldeditHelper.java @@ -0,0 +1,62 @@ +package org.dimdev.dimdoors.command; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.sk89q.jnbt.NBTInputStream; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.SpongeSchematicReader; +import com.sk89q.worldedit.fabric.FabricAdapter; +import com.sk89q.worldedit.session.ClipboardHolder; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtIo; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.pockets.PocketTemplate; +import org.dimdev.dimdoors.util.schematic.Schematic; + +public class WorldeditHelper { + static int load(ServerCommandSource source, PocketTemplate template) throws CommandSyntaxException { + ServerPlayerEntity player = source.getPlayer(); + boolean async = DimensionalDoors.getConfig().getPocketsConfig().asyncWorldEditPocketLoading; + Consumer taskAcceptor = async ? r -> source.getServer().execute(r) : Runnable::run; + Runnable task = () -> { + NbtCompound nbt = Schematic.toNbt(template.getSchematic()); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + try { + NbtIo.writeCompressed(nbt, stream); + } catch (IOException e) { + throw new RuntimeException(e); // Can't happen, the stream is a ByteArrayOutputStream + } + Clipboard clipboard; + try { + clipboard = new SpongeSchematicReader(new NBTInputStream(new ByteArrayInputStream(stream.toByteArray()))).read(); + } catch (IOException e) { + throw new RuntimeException(e); // Can't happen, the stream is a ByteArrayInputStream + } + taskAcceptor.accept(() -> { + WorldEdit.getInstance().getSessionManager().get(FabricAdapter.adaptPlayer(player)).setClipboard(new ClipboardHolder(clipboard)); + source.sendFeedback(Text.translatable("commands.pocket.loadedSchem", template.getId()), true); + }); + }; + if (async) { + CompletableFuture.runAsync(task); + } else { + task.run(); + } + return Command.SINGLE_SUCCESS; + } + +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/arguments/BlockPlacementTypeArgumentType.java b/common/src/main/java/org/dimdev/dimdoors/command/arguments/BlockPlacementTypeArgumentType.java new file mode 100644 index 00000000..f5e79aa2 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/arguments/BlockPlacementTypeArgumentType.java @@ -0,0 +1,20 @@ +package org.dimdev.dimdoors.command.arguments; + +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.arguments.StringRepresentableArgument; +import org.dimdev.dimdoors.api.util.BlockPlacementType; + +public class BlockPlacementTypeArgumentType extends StringRepresentableArgument { + public BlockPlacementTypeArgumentType() { + super(BlockPlacementType.CODEC, BlockPlacementType::values); + } + + public static StringRepresentableArgument blockPlacementType() { + return new BlockPlacementTypeArgumentType(); + } + + public static BlockPlacementType getBlockPlacementType(CommandContext context, String id) { + return context.getArgument(id, BlockPlacementType.class); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/arguments/EnumArgumentType.java b/common/src/main/java/org/dimdev/dimdoors/command/arguments/EnumArgumentType.java new file mode 100644 index 00000000..617acc61 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/arguments/EnumArgumentType.java @@ -0,0 +1,46 @@ +package org.dimdev.dimdoors.command.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.Component; + +import java.util.*; +import java.util.concurrent.CompletableFuture; + +public class EnumArgumentType> implements ArgumentType { + public static final DynamicCommandExceptionType UNKNOWN_VALUE = new DynamicCommandExceptionType(str -> Component.translatable("commands.generic.unknownValue", str)); + private final Map values; + private final Set valueList; + + public EnumArgumentType(Class enumClass) { + values = new HashMap<>(); + for (T enumConstant : enumClass.getEnumConstants()) { + values.put(enumConstant.name().toLowerCase(), enumConstant); + } + valueList = values.keySet(); + } + + @Override + public T parse(StringReader reader) throws CommandSyntaxException { + String str = reader.readString(); + return Optional.ofNullable(str) + .map(values::get) + .orElseThrow(() -> UNKNOWN_VALUE.create(str)); + } + + @Override + public Collection getExamples() { + return valueList; + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return SharedSuggestionProvider.suggest(valueList, builder); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/command/arguments/PocketTemplateArgumentType.java b/common/src/main/java/org/dimdev/dimdoors/command/arguments/PocketTemplateArgumentType.java new file mode 100644 index 00000000..db7fe325 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/command/arguments/PocketTemplateArgumentType.java @@ -0,0 +1,60 @@ +package org.dimdev.dimdoors.command.arguments; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.DynamicCommandExceptionType; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.minecraft.commands.SharedSuggestionProvider; +import net.minecraft.network.chat.Component; +import org.dimdev.dimdoors.api.util.Path; +import org.dimdev.dimdoors.api.util.SimpleTree; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.PocketTemplate; + +import java.util.Collection; +import java.util.Optional; +import java.util.TreeSet; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +public class PocketTemplateArgumentType implements ArgumentType { + public static final DynamicCommandExceptionType UNKNOWN_POCKET_TEMPLATE = new DynamicCommandExceptionType(s -> Component.translatable("commands.pocket.unknownPocketTemplate",s)); + + @Override + public PocketTemplate parse(StringReader reader) throws CommandSyntaxException { + String strValue = reader.readString(); + Path value = Path.stringPath(strValue); + if (!getPocketTemplates().containsKey(value)) { + throw UNKNOWN_POCKET_TEMPLATE.create(value.toString()); + } + return getPocketTemplates().get(value); + } + + @Override + public CompletableFuture listSuggestions(CommandContext context, SuggestionsBuilder builder) { + return SharedSuggestionProvider.suggest(getExamples(), builder); + } + + @Override + public Collection getExamples() { + return getPocketTemplates() + .keySet() + .parallelStream() + .map(path -> path.reduce(String::concat)) + .filter(Optional::isPresent) + .map(Optional::get) + .map(id -> "\"" + id + "\"") + .collect(Collectors.toCollection(TreeSet::new)); + } + + private SimpleTree getPocketTemplates() { + return PocketLoader.getInstance().getTemplates(); + } + + public static PocketTemplate getValue(CommandContext context, final String name) { + return context.getArgument(name, PocketTemplate.class); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/criteria/ModCriteria.java b/common/src/main/java/org/dimdev/dimdoors/criteria/ModCriteria.java new file mode 100644 index 00000000..01407c1d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/criteria/ModCriteria.java @@ -0,0 +1,13 @@ +package org.dimdev.dimdoors.criteria; + +import net.minecraft.advancements.CriteriaTriggers; + +public class ModCriteria { + public static final RiftTrackedCriterion RIFT_TRACKED = CriteriaTriggers.register(new RiftTrackedCriterion()); + public static final TagBlockBreakCriteria TAG_BLOCK_BREAK = CriteriaTriggers.register(new TagBlockBreakCriteria()); + public static final PocketSpawnPointSetCondition POCKET_SPAWN_POINT_SET = CriteriaTriggers.register(new PocketSpawnPointSetCondition()); + + public static void init() { + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/criteria/PocketSpawnPointSetCondition.java b/common/src/main/java/org/dimdev/dimdoors/criteria/PocketSpawnPointSetCondition.java new file mode 100644 index 00000000..0a95bad7 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/criteria/PocketSpawnPointSetCondition.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.criteria; + +import com.google.gson.JsonObject; +import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance; +import net.minecraft.advancements.critereon.DeserializationContext; +import net.minecraft.advancements.critereon.EntityPredicate; +import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.dimdev.dimdoors.DimensionalDoors; + +public class PocketSpawnPointSetCondition extends SimpleCriterionTrigger { + public static final ResourceLocation ID = DimensionalDoors.id("pocket_spawn_point_set"); + + @Override + protected Conditions createInstance(JsonObject jsonObject, EntityPredicate.Composite composite, DeserializationContext deserializationContext) { + return new Conditions(composite); + } + + public void trigger(ServerPlayer player) { + this.trigger(player, t -> true); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + public static class Conditions extends AbstractCriterionTriggerInstance { + public Conditions(EntityPredicate.Composite playerPredicate) { + super(ID, playerPredicate); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/criteria/RiftTrackedCriterion.java b/common/src/main/java/org/dimdev/dimdoors/criteria/RiftTrackedCriterion.java new file mode 100644 index 00000000..408e3a45 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/criteria/RiftTrackedCriterion.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.criteria; + +import com.google.gson.JsonObject; +import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance; +import net.minecraft.advancements.critereon.DeserializationContext; +import net.minecraft.advancements.critereon.EntityPredicate; +import net.minecraft.advancements.critereon.SimpleCriterionTrigger; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import org.dimdev.dimdoors.DimensionalDoors; + +public class RiftTrackedCriterion extends SimpleCriterionTrigger { + public static final ResourceLocation ID = DimensionalDoors.id("rift_tracked"); + + @Override + protected Conditions createInstance(JsonObject obj, EntityPredicate.Composite playerPredicate, DeserializationContext predicateDeserializer) { + return new Conditions(playerPredicate); + } + + public void trigger(ServerPlayer player) { + this.trigger(player, t -> true); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + public static class Conditions extends AbstractCriterionTriggerInstance { + public Conditions(EntityPredicate.Composite playerPredicate) { + super(ID, playerPredicate); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/criteria/TagBlockBreakCriteria.java b/common/src/main/java/org/dimdev/dimdoors/criteria/TagBlockBreakCriteria.java new file mode 100644 index 00000000..65fdfce2 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/criteria/TagBlockBreakCriteria.java @@ -0,0 +1,51 @@ +package org.dimdev.dimdoors.criteria; + +import com.google.gson.JsonObject; +import net.minecraft.advancements.critereon.*; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.tags.TagKey; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.dimdev.dimdoors.DimensionalDoors; + +import java.util.Objects; + +public class TagBlockBreakCriteria extends SimpleCriterionTrigger { + public static final ResourceLocation ID = DimensionalDoors.id("tag_block_break"); + + @Override + protected Conditions createInstance(JsonObject obj, EntityPredicate.Composite playerPredicate, DeserializationContext predicateDeserializer) { + return new Conditions(playerPredicate, TagKey.create(Registries.BLOCK, ResourceLocation.tryParse(obj.get("tag").getAsString()))); + } + + public void trigger(ServerPlayer player, BlockState block) { + this.trigger(player, c -> block.is(c.getBlockTag())); + } + + @Override + public ResourceLocation getId() { + return ID; + } + + public static class Conditions extends AbstractCriterionTriggerInstance { + private final TagKey blockTag; + + public Conditions(EntityPredicate.Composite playerPredicate, TagKey blockTag) { + super(ID, playerPredicate); + this.blockTag = Objects.requireNonNull(blockTag); + } + + @Override + public JsonObject serializeToJson(SerializationContext predicateSerializer) { + JsonObject json = new JsonObject(); + json.addProperty("tag", blockTag.location().toString()); + return super.serializeToJson(predicateSerializer); + } + + public TagKey getBlockTag() { + return blockTag; + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/MaskEntity.java b/common/src/main/java/org/dimdev/dimdoors/entity/MaskEntity.java new file mode 100644 index 00000000..e531a5ed --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/MaskEntity.java @@ -0,0 +1,36 @@ +package org.dimdev.dimdoors.entity; + +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.level.Level; + +//import software.bernie.geckolib3.core.IAnimatable; +//import software.bernie.geckolib3.core.PlayState; +//import software.bernie.geckolib3.core.builder.AnimationBuilder; +//import software.bernie.geckolib3.core.controller.AnimationController; +//import software.bernie.geckolib3.core.event.predicate.AnimationEvent; +//import software.bernie.geckolib3.core.manager.AnimationData; +//import software.bernie.geckolib3.core.manager.AnimationFactory; + +public class MaskEntity extends PathfinderMob /*implements IAnimatable*/ { // TODO +// private AnimationFactory factory = new AnimationFactory(this); + + protected MaskEntity(EntityType entityType, Level world) { + super(entityType, world); + } + +// private PlayState predicate(AnimationEvent event) { +// event.getController().setAnimation(new AnimationBuilder().addAnimation("animation.mask.hover", true)); +// return PlayState.CONTINUE; +// } + +// @Override +// public void registerControllers(AnimationData data) { +// data.addAnimationController(new AnimationController(this, "controller", 0, this::predicate)); +// } + +// @Override +// public AnimationFactory getFactory() { +// return this.factory; +// } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/ModEntityTypes.java b/common/src/main/java/org/dimdev/dimdoors/entity/ModEntityTypes.java new file mode 100644 index 00000000..c3e872ce --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/ModEntityTypes.java @@ -0,0 +1,60 @@ +package org.dimdev.dimdoors.entity; + +//import org.dimdev.dimdoors.client.MaskRenderer; + +import dev.architectury.registry.client.level.entity.EntityRendererRegistry; +import dev.architectury.registry.client.rendering.BlockEntityRendererRegistry; +import dev.architectury.registry.level.entity.EntityAttributeRegistry; +import dev.architectury.registry.registries.DeferredRegister; +import dev.architectury.registry.registries.RegistrySupplier; +import net.minecraft.core.registries.Registries; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityDimensions; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnGroup; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry; +import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.client.MonolithRenderer; + +public class ModEntityTypes { + public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(DimensionalDoors.MOD_ID, Registries.ENTITY_TYPE); + + public static final RegistrySupplier> MONOLITH = register( + "dimdoors:monolith", + MonolithEntity::new, + 2f, 2.7f, false + ); + + public static final RegistrySupplier> MASK = register( + "dimdoors:mask", + MaskEntity::new, + 0.9375f, 0.9375f, true + ); + + public static void init() { + ENTITY_TYPES.register(); + EntityAttributeRegistry.register(MONOLITH, MonolithEntity::createMobAttributes); + EntityAttributeRegistry.register(MASK, MonolithEntity::createMobAttributes); + } + + @Environment(EnvType.CLIENT) + public static void initClient() { + EntityRendererRegistry.register(MONOLITH, MonolithRenderer::new); +// EntityRendererRegistry.INSTANCE.register(MASK, MaskRenderer::new); + } + + private static RegistrySupplier> register(String id, EntityType.EntityFactory factory, float width, float height, boolean fixed) { + return ENTITY_TYPES.register(id, () -> EntityType.Builder.of(factory, MobCategory.MONSTER).sized(width, height).canSpawnFarFromPlayer().fireImmune().build(null)); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/MonolithEntity.java b/common/src/main/java/org/dimdev/dimdoors/entity/MonolithEntity.java new file mode 100644 index 00000000..1b5b7be3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/MonolithEntity.java @@ -0,0 +1,337 @@ +package org.dimdev.dimdoors.entity; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.core.particles.ParticleTypes; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.syncher.EntityDataAccessor; +import net.minecraft.network.syncher.EntityDataSerializers; +import net.minecraft.network.syncher.SynchedEntityData; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.Mth; +import net.minecraft.util.RandomSource; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.damagesource.DamageTypes; +import net.minecraft.world.entity.*; +import net.minecraft.world.entity.ai.control.LookControl; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.entity.ai.MonolithAggroGoal; +import org.dimdev.dimdoors.item.ModItems; +import org.dimdev.dimdoors.sound.ModSoundEvents; +import org.dimdev.dimdoors.world.ModDimensions; + +import java.util.logging.Level; + +public class MonolithEntity extends Mob { + public static final int MAX_AGGRO = 250; + private static final int MAX_AGGRO_CAP = 100; + private static final int MIN_AGGRO_CAP = 25; + private static final int MAX_TEXTURE_STATE = 18; + private static final int MAX_SOUND_COOLDOWN = 200; + public static final int MAX_AGGRO_RANGE = 35; + private static final EntityDataAccessor AGGRO = SynchedEntityData.defineId(MonolithEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor SCALE = SynchedEntityData.defineId(MonolithEntity.class, EntityDataSerializers.FLOAT); + private static final EntityDataAccessor PITCH = SynchedEntityData.defineId(MonolithEntity.class, EntityDataSerializers.FLOAT); + private static final float EYE_HEIGHT_PERCENTAGE = 0.55f; + @Environment(EnvType.CLIENT) + private static final RandomSource clientRandom = RandomSource.create(); + + private int soundTime = 0; + private final int aggroCap; + + MonolithEntity(Level world) { + this(ModEntityTypes.MONOLITH, world); + } + + public MonolithEntity(EntityType type, Level world) { + super(ModEntityTypes.MONOLITH, world); + this.noPhysics = true; + this.aggroCap = Mth.nextInt(this.getRandom(), MIN_AGGRO_CAP, MAX_AGGRO_CAP); + this.setNoGravity(true); + this.lookControl = new LookControl(this) { + + @Override + protected boolean resetXRotOnTick() { + return false; + } + }; + + this.setInvulnerable(true); + } + + public boolean isDangerous() { + return DimensionalDoors.getConfig().getMonolithsConfig().monolithTeleportation && (ModDimensions.isLimboDimension(this.world) || DimensionalDoors.getConfig().getMonolithsConfig().dangerousLimboMonoliths); + } + + @Override + public boolean hurt(DamageSource source, float amount) { + if (!source.is(DamageTypes.IN_WALL)) { + setAggro(MAX_AGGRO); + } + return false; + } + + @Override + protected int decreaseAirSupply(int i) { + return 10; + } + + @Override + protected int increaseAirSupply(int i) { + return 10; + } + +// @Override +// public Box getCollisionBox() { +// return null; +// } +// +// @Override +// public Box getHardCollisionBox(Entity entity) { +// return null; +// } + + @Override + public boolean requiresCustomPersistence() { + return false; + } + + @Override + public boolean isPushable() { + return false; + } + + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + // Add a short for the aggro level + this.entityData.define(AGGRO, 0); + this.entityData.define(SCALE, 1f); + this.entityData.define(PITCH, 1f); + this.refreshDimensions(); + } + + @Override + public boolean isAlive() { + return false; + } + + @Override + public void move(MoverType moverType, Vec3 vec3) { + + } + + @Override + protected void customServerAiStep() { + // Remove this Monolith if it's not in Limbo or in a pocket dungeon + if (!(ModDimensions.isLimboDimension(this.level) || ModDimensions.isPocketDimension(this.level))) { + this.remove(RemovalReason.DISCARDED); + super.customServerAiStep(); + return; + } + + super.customServerAiStep(); + + // Check for players and update aggro levels even if there are no players in range + } + + public void updateAggroLevel(Player player, boolean visibility) { + // If we're working on the server side, adjust aggro level + // If we're working on the client side, retrieve aggro level from dataWatcher + if (player == null) { + return; + } + + if ((player.getInventory().armor.get(0).getItem() == ModItems.WORLD_THREAD_HELMET && player.getInventory().armor.get(1).getItem() == ModItems.WORLD_THREAD_CHESTPLATE && player.getInventory().armor.get(2).getItem() == ModItems.WORLD_THREAD_LEGGINGS && player.getInventory().armor.get(3).getItem() == ModItems.WORLD_THREAD_BOOTS)) { + return; + } + + if (!this.level.isClientSide) { + if (player.distanceTo(this) > 70) { + return; + } + + int aggro = this.entityData.get(AGGRO); + // Server side... + // Rapidly increase the aggro level if this Monolith can see the player + if (visibility) { + if (ModDimensions.isLimboDimension(this.level)) { + if (this.isDangerous()) { + aggro++; + } else { + aggro += 36; + } + } else { + // Aggro increases faster outside of Limbo + aggro += 3; + } + } else { + if (this.isDangerous()) { + if (aggro > this.aggroCap) { + // Decrease aggro over time + aggro--; + } else if (aggro < this.aggroCap) { + // Increase aggro if a player is within range and aggro < aggroCap + aggro++; + } + } else { + aggro -= 3; + } + } + // Clamp the aggro level + int maxAggro = this.isDangerous() ? MAX_AGGRO : 180; + aggro = (short) Mth.clamp(aggro, 0, maxAggro); + this.entityData.set(AGGRO, aggro); + } + } + + @Environment(EnvType.CLIENT) + public int getTextureState() { + // Determine texture state from aggro progress + return Mth.clamp(MAX_TEXTURE_STATE * this.entityData.get(AGGRO) / MAX_AGGRO, 0, MAX_TEXTURE_STATE); + } + + /** + * Plays sounds at different levels of aggro, using soundTime to prevent too many sounds at once. + * + * @param pos The position to play the sounds at + */ + public void playSounds(Vec3 pos) { + float aggroPercent = this.getAggroProgress(); + float pitch = getPitch(); + if (this.soundTime <= 0) { + this.playSound(ModSoundEvents.MONK.get(), 1F, pitch); + this.soundTime = 100; + } + if (aggroPercent > 0.70 && this.soundTime < 100) { + this.level.playSound(null, new BlockPos(new Vec3i((int) pos.x, (int) pos.y, (int) pos.z)), ModSoundEvents.TEARING.get(), SoundSource.HOSTILE, 1F, (float) (1 + this.getRandom().nextGaussian())); + this.soundTime = 100 + this.getRandom().nextInt(75); + } + if (aggroPercent > 0.80 && this.soundTime < MAX_SOUND_COOLDOWN) { + this.level.playSound(null, new BlockPos(new Vec3i((int) pos.x, (int) pos.y, (int) pos.z)), ModSoundEvents.TEARING.get(), SoundSource.HOSTILE, 7, 1); + this.soundTime = 250; + } + this.soundTime--; + } + + @Override + public float getEyeHeight(Pose pose) { + return getDimensions(pose).height * EYE_HEIGHT_PERCENTAGE; + } + + @Override + protected float getStandingEyeHeight(Pose pose, EntityDimensions dimensions) { + return getDimensions(pose).height * EYE_HEIGHT_PERCENTAGE; + } + + @Environment(EnvType.CLIENT) + public static void spawnParticles(int aggro) { + Player player = Minecraft.getInstance().player; + if (aggro < 120) { + return; + } + int count = 10 * aggro / MAX_AGGRO; + for (int i = 1; i < count; ++i) { + //noinspection ConstantConditions + player.level.addParticle(ParticleTypes.PORTAL, player.getX() + (clientRandom.nextDouble() - 0.5D) * 3.0, + player.getY() + clientRandom.nextDouble() * player.getBbHeight() - 0.75D, + player.getZ() + (clientRandom.nextDouble() - 0.5D) * player.getBbWidth(), + (clientRandom.nextDouble() - 0.5D) * 2.0D, -clientRandom.nextDouble(), + (clientRandom.nextDouble() - 0.5D) * 2.0D); + } + } + + public float getAggroProgress() { + return ((float) getAggro()) / MAX_AGGRO; + } + + @Override + protected void registerGoals() { + super.registerGoals(); + this.goalSelector.addGoal(0, new MonolithAggroGoal(this, MAX_AGGRO_RANGE)); + } + + public void facePlayer(Player player) { + this.lookControl.setLookAt(player, 1.0f, 1.0f); + } + + @Override + public void addAdditionalSaveData(CompoundTag nbt) { + super.addAdditionalSaveData(nbt); + nbt.putInt("Aggro", getAggro()); + nbt.putFloat("scale", getScale()); + nbt.putFloat("pitch", getPitch()); + } + + @Override + public void readAdditionalSaveData(CompoundTag nbt) { + super.readAdditionalSaveData(nbt); + setAggro(nbt.getInt("Aggro")); + if (nbt.contains("scale", Tag.TAG_FLOAT)) { + setScale(nbt.getFloat("scale")); + } + if (nbt.contains("pitch", Tag.TAG_FLOAT)) { + setPitch(nbt.getFloat("pitch")); + } + } + + public int getAggro() { + return this.entityData.get(AGGRO); + } + + public void setAggro(int aggro) { + this.entityData.set(AGGRO, aggro); + } + + @Override + public float getScale() { + return this.entityData.get(SCALE); + } + + public void setScale(float scale) { + this.entityData.set(SCALE, scale); + refreshDimensions(); + } + + public float getPitch() { + return this.entityData.get(PITCH); + } + + public void setPitch(float pitch) { + this.entityData.set(PITCH, pitch); + } + + @Override + protected AABB getBoundingBoxForPose(Pose pose) { + float scale = getScale(); + return super.getBoundingBoxForPose(pose).inflate(scale, scale, scale); + } + + @Override + public void onSyncedDataUpdated(EntityDataAccessor data) { + if (SCALE.equals(data)) { + this.refreshDimensions(); + } + + super.onSyncedDataUpdated(data); + } + + @Override + public boolean checkSpawnRules(LevelAccessor world, MobSpawnType spawnReason) { + if (spawnReason == MobSpawnType.CHUNK_GENERATION) { + return super.checkSpawnRules(world, spawnReason); + } + if (spawnReason == MobSpawnType.NATURAL) { + return this.getRandom().nextInt(32) == 2; + } + return false; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/ai/MonolithAggroGoal.java b/common/src/main/java/org/dimdev/dimdoors/entity/ai/MonolithAggroGoal.java new file mode 100644 index 00000000..b6a9cfce --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/ai/MonolithAggroGoal.java @@ -0,0 +1,113 @@ +package org.dimdev.dimdoors.entity.ai; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.entity.ai.TargetPredicate; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sounds.SoundSource; +import net.minecraft.util.RandomSource; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.util.math.random.Random; +import net.minecraft.world.entity.EntitySelector; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.ai.targeting.TargetingConditions; +import net.minecraft.world.entity.player.Player; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.entity.MonolithEntity; +import org.dimdev.dimdoors.entity.stat.ModStats; +import org.dimdev.dimdoors.item.ModItems; +import org.dimdev.dimdoors.network.ServerPacketHandler; +import org.dimdev.dimdoors.network.packet.s2c.MonolithAggroParticlesPacket; +import org.dimdev.dimdoors.network.packet.s2c.MonolithTeleportParticlesPacket; +import org.dimdev.dimdoors.sound.ModSoundEvents; + +import java.util.EnumSet; + +import static org.dimdev.dimdoors.entity.MonolithEntity.MAX_AGGRO; + +public class MonolithAggroGoal extends Goal { + protected final MonolithEntity mob; + protected Player target; + protected final float range; + protected final TargetingConditions targetPredicate; + + public MonolithAggroGoal(MonolithEntity mobEntity, float f) { + this.mob = mobEntity; + this.range = f; + this.setFlags(EnumSet.of(Goal.Flag.LOOK)); + this.targetPredicate = (TargetingConditions.forCombat()).range(this.range).selector(EntitySelector.NO_CREATIVE_OR_SPECTATOR::test); + } + + private Player getTarget() { + Player playerEntity = this.mob.level.getNearestPlayer(this.targetPredicate, this.mob, this.mob.getX(), this.mob.getEyeY(), this.mob.getZ()); + return playerEntity != null && this.mob.hasLineOfSight(playerEntity) && playerEntity.distanceTo(this.mob) < 50 ? playerEntity : null; + } + + public boolean canStart() { + return (this.target = this.getTarget()) != null && this.target.distanceTo(this.mob) <= 50; + } + + public boolean shouldContinue() { + return (this.target = this.getTarget()) != null && this.target.distanceTo(this.mob) <= 50; + } + + public void start() { + } + + public void stop() { + this.target = null; + this.mob.setAggro(0); + } + + public void tick() { + if (this.target != null && this.target.distanceTo(this.mob) > 70) { + this.stop(); + return; + } + + if (this.target != null && (this.target.getInventory().armor.get(0).getItem() == ModItems.WORLD_THREAD_HELMET && this.target.getInventory().armor.get(1).getItem() == ModItems.WORLD_THREAD_CHESTPLATE && this.target.getInventory().armor.get(2).getItem() == ModItems.WORLD_THREAD_LEGGINGS && this.target.getInventory().armor.get(3).getItem() == ModItems.WORLD_THREAD_BOOTS)) { + RandomSource random = RandomSource.create(); + int i = random.nextInt(64); + if (this.target instanceof ServerPlayer) { + if (i < 4) { + this.target.getInventory().armor.get(0).hurt(i, random, (ServerPlayer) this.target); + this.target.getInventory().armor.get(1).hurt(i, random, (ServerPlayer) this.target); + this.target.getInventory().armor.get(2).hurt(i, random, (ServerPlayer) this.target); + this.target.getInventory().armor.get(3).hurt(i, random, (ServerPlayer) this.target); + } + } + return; + } + + boolean visibility = this.target != null; + this.mob.updateAggroLevel(this.target, visibility); + + // Change orientation and face a player if one is in range + if (this.target != null) { + this.mob.facePlayer(this.target); + if (this.mob.isDangerous()) { + // Play sounds on the server side, if the player isn't in Limbo. + // Limbo is excluded to avoid drowning out its background music. + // Also, since it's a large open area with many Monoliths, some + // of the sounds that would usually play for a moment would + // keep playing constantly and would get very annoying. + this.mob.playSounds(this.target.position()); + ServerPacketHandler.get((ServerPlayer) this.target).sendPacket(new MonolithAggroParticlesPacket(this.mob.getAggro())); + } + + // Teleport the target player if various conditions are met + if (this.mob.getAggro() >= MAX_AGGRO && DimensionalDoors.getConfig().getMonolithsConfig().monolithTeleportation && !this.target.isCreative() && this.mob.isDangerous()) { + this.mob.setAggro(0); + this.target.teleportTo(this.target.getX(), this.target.getY() + 256, this.target.getZ()); + this.target.level.playSound(null, new BlockPos(new Vec3i((int) this.target.position().x, (int) this.target.position().y, (int) this.target.position().z)), ModSoundEvents.CRACK.get(), SoundSource.HOSTILE, 13, 1); + this.target.awardStat(ModStats.TIMES_TELEPORTED_BY_MONOLITH); + ServerPacketHandler.get((ServerPlayer) this.target).sendPacket(new MonolithTeleportParticlesPacket()); + } + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboEntranceSource.java b/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboEntranceSource.java new file mode 100644 index 00000000..0fa78ffd --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboEntranceSource.java @@ -0,0 +1,33 @@ +package org.dimdev.dimdoors.entity.limbo; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.contents.TranslatableContents; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.player.Player; + +public abstract class LimboEntranceSource { + public abstract Component getMessage(Player player); + + public void broadcast(Player player, MinecraftServer server) { + server.getPlayerList().broadcastSystemMessage(this.getMessage(player), false); + } + + public static LimboDeathEntranceSource ofDamageSource(DamageSource source) { + return new LimboDeathEntranceSource(source); + } + + public static class LimboDeathEntranceSource extends LimboEntranceSource { + private final DamageSource damageSource; + + private LimboDeathEntranceSource(DamageSource damageSource) { + this.damageSource = damageSource; + } + + @Override + public Component getMessage(Player player) { + TranslatableContents message = (TranslatableContents) this.damageSource.getLocalizedDeathMessage(player).getContents(); + return Component.translatable("limbo." + message.getKey(), message.getArgs()); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboExitReason.java b/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboExitReason.java new file mode 100644 index 00000000..8470f85c --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/limbo/LimboExitReason.java @@ -0,0 +1,23 @@ +package org.dimdev.dimdoors.entity.limbo; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.StringIdentifiable; + +public enum LimboExitReason implements StringIdentifiable { + ETERNAL_FLUID, + GENERIC, + RIFT; + + @Override + public String asString() { + return "limbo.exit." + this.name().toLowerCase(); + } + + public void broadcast(PlayerEntity player) { + //noinspection ConstantConditions + player.getServer().getPlayerManager().broadcast(Text.translatable(asString(), player.getGameProfile().getName()), false); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/entity/stat/ModStats.java b/common/src/main/java/org/dimdev/dimdoors/entity/stat/ModStats.java new file mode 100644 index 00000000..fb1b6ead --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/entity/stat/ModStats.java @@ -0,0 +1,28 @@ +package org.dimdev.dimdoors.entity.stat; + +import dev.architectury.registry.registries.DeferredRegister; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.stats.StatFormatter; +import net.minecraft.stats.Stats; +import org.dimdev.dimdoors.DimensionalDoors; + +public class ModStats { + public static final DeferredRegister STATS = DeferredRegister.create(DimensionalDoors.MOD_ID, Registries.CUSTOM_STAT); + + public static final ResourceLocation DEATHS_IN_POCKETS = register("deaths_in_pocket", StatFormatter.DEFAULT); + public static final ResourceLocation TIMES_SENT_TO_LIMBO = register("times_sent_to_limbo", StatFormatter.DEFAULT); + public static final ResourceLocation TIMES_TELEPORTED_BY_MONOLITH = register("times_teleported_by_monolith", StatFormatter.DEFAULT); + public static final ResourceLocation TIMES_BEEN_TO_DUNGEON = register("times_been_to_dungeon", StatFormatter.DEFAULT); + + private static ResourceLocation register(String string, StatFormatter statFormatter) { + ResourceLocation resourceLocation = DimensionalDoors.id(string); + STATS.register(string, () -> resourceLocation); + Stats.CUSTOM.get(resourceLocation, statFormatter); + return resourceLocation; + } + + public static void init() { + // just loads the class + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/item/ModItems.java b/common/src/main/java/org/dimdev/dimdoors/item/ModItems.java index 13063f1e..ae67a1a7 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/ModItems.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/ModItems.java @@ -1,39 +1,12 @@ package org.dimdev.dimdoors.item; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Supplier; - import dev.architectury.core.item.ArchitecturyBucketItem; import dev.architectury.registry.CreativeTabRegistry; import dev.architectury.registry.registries.DeferredRegister; import dev.architectury.registry.registries.RegistrySupplier; import net.minecraft.core.registries.Registries; import net.minecraft.world.item.*; -import net.minecraft.world.level.block.Block; import org.dimdev.dimdoors.DimensionalDoors; -import org.dimdev.matrix.Matrix; -import org.dimdev.matrix.Registrar; -import org.dimdev.matrix.RegistryEntry; - -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.item.ArmorItem; -import net.minecraft.item.BlockItem; -import net.minecraft.item.BucketItem; -import net.minecraft.item.Item; -import net.minecraft.item.ItemGroup; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.item.SpawnEggItem; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; - -import net.fabricmc.fabric.api.event.registry.RegistryEntryAddedCallback; -import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; - import org.dimdev.dimdoors.block.ModBlocks; import org.dimdev.dimdoors.entity.ModEntityTypes; import org.dimdev.dimdoors.fluid.ModFluids; @@ -41,15 +14,21 @@ import org.dimdev.dimdoors.item.door.DimensionalTrapdoorItem; import org.dimdev.dimdoors.rift.targets.RandomTarget; import org.dimdev.dimdoors.sound.ModSoundEvents; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + import static org.dimdev.dimdoors.DimensionalDoors.id; public final class ModItems { // DO NOT REMOVE!!! public static final DeferredRegister REGISTRY = DeferredRegister.create(DimensionalDoors.MOD_ID, Registries.ITEM); - public static final RegistrySupplier OAK_DIMENSIONAL_TRAPDOOR = register("wood_dimensional_trapdoor", () -> new DimensionalTrapdoorItem( + public static final RegistrySupplier OAK_DIMENSIONAL_TRAPDOOR = register("wood_dimensional_trapdoor", properties -> new DimensionalTrapdoorItem( ModBlocks.OAK_DIMENSIONAL_TRAPDOOR.get(), - new Item.Properties().stacksTo(1), + properties.stacksTo(1), rift -> rift.setDestination( RandomTarget.builder() .acceptedGroups(Collections.singleton(0)) @@ -62,73 +41,73 @@ public final class ModItems { .build()) )); - public static final RegistrySupplier WORLD_THREAD = register("world_thread", () -> new Item(new Item.Properties())); + public static final RegistrySupplier WORLD_THREAD = register("world_thread", Item::new); - public static final RegistrySupplier INFRANGIBLE_FIBER = register("infrangible_fiber", () -> new Item(new Item.Properties())); + public static final RegistrySupplier INFRANGIBLE_FIBER = register("infrangible_fiber", properties -> new Item(properties.fireResistant())); - public static final RegistrySupplier FRAYED_FILAMENTS = register("frayed_filament", () -> new Item(new Item.Properties())); + public static final RegistrySupplier FRAYED_FILAMENTS = register("frayed_filament", Item::new); public static final RegistrySupplier RIFT_CONFIGURATION_TOOL = register("rift_configuration_tool", RiftConfigurationToolItem::new); - public static final RegistrySupplier RIFT_BLADE = register("rift_blade", () -> new RiftBladeItem(new Item.Properties().durability(100))); + public static final RegistrySupplier RIFT_BLADE = register("rift_blade", properties -> new RiftBladeItem(properties.durability(100))); - public static final RegistrySupplier RIFT_REMOVER = register("rift_remover", () -> new RiftRemoverItem(new Item.Properties().stacksTo(1).durability(100))); + public static final RegistrySupplier RIFT_REMOVER = register("rift_remover", properties -> new RiftRemoverItem(properties.stacksTo(1).durability(100))); - public static final RegistrySupplier RIFT_SIGNATURE = register("rift_signature", () -> new RiftSignatureItem(new Item.Properties().stacksTo(1).durability(1))); + public static final RegistrySupplier RIFT_SIGNATURE = register("rift_signature", properties -> new RiftSignatureItem(properties.stacksTo(1).durability(1))); - public static final RegistrySupplier STABILIZED_RIFT_SIGNATURE = register("stabilized_rift_signature", () -> new StabilizedRiftSignatureItem(new Item.Properties().stacksTo(1).durability(20))); + public static final RegistrySupplier STABILIZED_RIFT_SIGNATURE = register("stabilized_rift_signature", properties -> new StabilizedRiftSignatureItem(properties.stacksTo(1).durability(20))); - public static final RegistrySupplier RIFT_STABILIZER = register("rift_stabilizer", () -> new RiftStabilizerItem(new Item.Properties().stacksTo(1).durability(6))); + public static final RegistrySupplier RIFT_STABILIZER = register("rift_stabilizer", properties -> new RiftStabilizerItem(properties.stacksTo(1).durability(6))); - public static final RegistrySupplier RIFT_KEY = register("rift_key", () -> new RiftKeyItem(new Item.Properties().fireResistant().stacksTo(1))); + public static final RegistrySupplier RIFT_KEY = register("rift_key", properties -> new RiftKeyItem(properties.fireResistant().stacksTo(1))); - public static final RegistrySupplier DIMENSIONAL_ERASER = register("dimensional_eraser", () -> new DimensionalEraserItem(new Item.Properties().durability(100))); + public static final RegistrySupplier DIMENSIONAL_ERASER = register("dimensional_eraser", properties -> new DimensionalEraserItem(properties.durability(100))); - public static final RegistrySupplier MONOLITH_SPAWNER = register("monolith_spawner", () -> new SpawnEggItem(ModEntityTypes.MONOLITH, 0xffffff, 0xffffff, new Item.Properties()); + public static final RegistrySupplier MONOLITH_SPAWNER = register("monolith_spawner", properties -> new SpawnEggItem(ModEntityTypes.MONOLITH.get(), 0xffffff, 0xffffff, properties)); - public static final RegistrySupplier WORLD_THREAD_HELMET = register("world_thread_helmet", () -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.HELMET, new Item.Properties())); + public static final RegistrySupplier WORLD_THREAD_HELMET = register("world_thread_helmet", properties -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.HELMET, properties)); - public static final RegistrySupplier WORLD_THREAD_CHESTPLATE = register("world_thread_chestplate", () -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.CHESTPLATE, new Item.Properties())); + public static final RegistrySupplier WORLD_THREAD_CHESTPLATE = register("world_thread_chestplate", properties -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.CHESTPLATE, properties)); - public static final RegistrySupplier WORLD_THREAD_LEGGINGS = register("world_thread_leggings", () -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.LEGGINGS, new Item.Properties())); + public static final RegistrySupplier WORLD_THREAD_LEGGINGS = register("world_thread_leggings", properties -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.LEGGINGS, properties)); - public static final RegistrySupplier WORLD_THREAD_BOOTS = register("world_thread_boots", () -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.BOOTS, new Item.Properties())); + public static final RegistrySupplier WORLD_THREAD_BOOTS = register("world_thread_boots", properties -> new ArmorItem(ModArmorMaterials.WORLD_THREAD, ArmorItem.Type.BOOTS, properties)); - public static final RegistrySupplier MASK_WAND = register("mask_wand", () -> new MaskWandItem(new Item.Properties().stacksTo(100)/**/)); + public static final RegistrySupplier MASK_WAND = register("mask_wand", properties -> new MaskWandItem(properties.stacksTo(100))); - public static final RegistrySupplier STABLE_FABRIC = register("stable_fabric", () -> new Item(new Item.Properties())); + public static final RegistrySupplier STABLE_FABRIC = register("stable_fabric", Item::new); - public static final RegistrySupplier CREEPY_RECORD = register("creepy_record", () -> new RecordItem(10, ModSoundEvents.CREEPY.get(), new Item.Properties(), 317)); + public static final RegistrySupplier CREEPY_RECORD = register("creepy_record", properties -> new RecordItem(10, ModSoundEvents.CREEPY.get(), properties, 317)); - public static final RegistrySupplier WHITE_VOID_RECORD = register("white_void_record", () -> new RecordItem(10, ModSoundEvents.WHITE_VOID.get(), new Item.Properties(), 225)); + public static final RegistrySupplier WHITE_VOID_RECORD = register("white_void_record", properties -> new RecordItem(10, ModSoundEvents.WHITE_VOID.get(), properties, 225)); - public static final Item ETERNAL_FLUID_BUCKET = register("eternal_fluid_bucket", () -> new ArchitecturyBucketItem(ModFluids.ETERNAL_FLUID, new Item.Properties().craftRemainder(Items.BUCKET).stacksTo(1))); + public static final RegistrySupplier ETERNAL_FLUID_BUCKET = register("eternal_fluid_bucket", properties -> new ArchitecturyBucketItem(ModFluids.ETERNAL_FLUID, properties.craftRemainder(Items.BUCKET).stacksTo(1))); - public static final RegistrySupplier MASK_SHARD = register("mask_shard", () -> new Item(new Item.Properties())); + public static final RegistrySupplier MASK_SHARD = register("mask_shard", Item::new); - public static final RegistrySupplier FUZZY_FIREBALL = register("fuzzy_fireball", () -> new Item(new Item.Properties())); + public static final RegistrySupplier FUZZY_FIREBALL = register("fuzzy_fireball", Item::new); - public static final RegistrySupplier FABRIC_OF_FINALITY = register("fabric_of_finality", () -> new Item(new Item.Properties())); + public static final RegistrySupplier FABRIC_OF_FINALITY = register("fabric_of_finality", Item::new); - public static final RegistrySupplier LIMINAL_LINT = register("liminal_lint", () -> new Item(new Item.Properties())); + public static final RegistrySupplier LIMINAL_LINT = register("liminal_lint", Item::new); - public static final RegistrySupplier ENDURING_FIBERS = register("enduring_fibers", () -> new Item(new Item.Properties())); + public static final RegistrySupplier ENDURING_FIBERS = register("enduring_fibers", Item::new); - public static final RegistrySupplier RIFT_PEARL = register("rift_pearl", () -> new Item(new Item.Properties())); + public static final RegistrySupplier RIFT_PEARL = register("rift_pearl", Item::new); - public static final RegistrySupplier FABRIC_OF_REALITY = register("fabric_of_reality", () -> new Item(new Item.Properties())); + public static final RegistrySupplier FABRIC_OF_REALITY = register("fabric_of_reality", Item::new); - public static final RegistrySupplier AMALGAM_LUMP = register("amalgam_lump", () -> new Item(new Item.Properties())); + public static final RegistrySupplier AMALGAM_LUMP = register("amalgam_lump", Item::new); - public static final RegistrySupplier CLOD = register("clod", () -> new Item(new Item.Properties())); + public static final RegistrySupplier CLOD = register("clod", Item::new); - public static final RegistrySupplier GARMENT_OF_REALITY_HELMET = register("garment_of_reality_helmet", () -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.HELMET, new Item.Properties())); + public static final RegistrySupplier GARMENT_OF_REALITY_HELMET = register("garment_of_reality_helmet", properties -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.HELMET, properties)); - public static final RegistrySupplier GARMENT_OF_REALITY_CHESTPLATE = register("garment_of_reality_chestplate", () -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.CHESTPLATE, new Item.Properties())); + public static final RegistrySupplier GARMENT_OF_REALITY_CHESTPLATE = register("garment_of_reality_chestplate", properties -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.CHESTPLATE, properties)); - public static final RegistrySupplier GARMENT_OF_REALITY_LEGGINGS = register("garment_of_reality_leggings", () -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.LEGGINGS, new Item.Properties())); + public static final RegistrySupplier GARMENT_OF_REALITY_LEGGINGS = register("garment_of_reality_leggings", properties -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.LEGGINGS, properties)); - public static final RegistrySupplier GARMENT_OF_REALITY_BOOTS = register("garment_of_reality_boots", () -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.BOOTS, new Item.Properties())); + public static final RegistrySupplier GARMENT_OF_REALITY_BOOTS = register("garment_of_reality_boots", properties -> new ArmorItem(ModArmorMaterials.GARMENT_OF_REALITY, ArmorItem.Type.BOOTS, properties)); public static final Set DOOR_ITEMS = new HashSet<>(); diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RaycastHelper.java b/common/src/main/java/org/dimdev/dimdoors/item/RaycastHelper.java index 2a59ac20..07cd6eff 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RaycastHelper.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RaycastHelper.java @@ -1,28 +1,26 @@ package org.dimdev.dimdoors.item; -import java.util.function.Predicate; - -import net.minecraft.entity.Entity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.projectile.ProjectileUtil; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.EntityHitResult; -import net.minecraft.util.hit.HitResult; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.BlockView; - +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.projectile.ProjectileUtil; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.EntityHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import java.util.function.Predicate; + public final class RaycastHelper { public static final int REACH_DISTANCE = 5; - public static boolean hitsDetachedRift(HitResult hit, BlockView world) { + public static boolean hitsDetachedRift(HitResult hit, BlockGetter world) { return hit != null && hit.getType() == HitResult.Type.BLOCK && world.getBlockEntity(((BlockHitResult) hit).getBlockPos()) instanceof DetachedRiftBlockEntity; } - public static boolean hitsRift(HitResult hit, BlockView world) { + public static boolean hitsRift(HitResult hit, BlockGetter world) { return hit != null && hit.getType() == HitResult.Type.BLOCK && world.getBlockEntity(((BlockHitResult) hit).getBlockPos()) instanceof RiftBlockEntity; } @@ -31,10 +29,10 @@ public final class RaycastHelper { } public static HitResult raycast(Entity entity, double maxDistance, float tickDelta, Predicate predicate) { - Vec3d vec3d = entity.getCameraPosVec(tickDelta); - Vec3d vec3d2 = entity.getRotationVec(tickDelta); - Vec3d vec3d3 = vec3d.add(vec3d2.x * maxDistance, vec3d2.y * maxDistance, vec3d2.z * maxDistance); - Box box = entity.getBoundingBox().stretch(vec3d2.multiply(maxDistance)).expand(1.0D, 1.0D, 1.0D); - return ProjectileUtil.raycast(entity, vec3d, vec3d3, box, predicate, maxDistance); + Vec3 vec3d = entity.getEyePosition(tickDelta); + Vec3 vec3d2 = entity.getViewVector(tickDelta); + Vec3 vec3d3 = vec3d.add(vec3d2.x * maxDistance, vec3d2.y * maxDistance, vec3d2.z * maxDistance); + var box = entity.getBoundingBox().expandTowards(vec3d2.scale(maxDistance)).inflate(1.0D, 1.0D, 1.0D); + return ProjectileUtil.getEntityHitResult(entity, vec3d, vec3d3, box, predicate, maxDistance); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RiftBladeItem.java b/common/src/main/java/org/dimdev/dimdoors/item/RiftBladeItem.java index dac95df4..db4d154e 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RiftBladeItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RiftBladeItem.java @@ -1,40 +1,30 @@ package org.dimdev.dimdoors.item; -import java.util.List; -import java.util.Objects; - -import net.minecraft.client.item.TooltipContext; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.item.SwordItem; -import net.minecraft.item.ToolMaterials; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableTextContent; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.HitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; -import net.minecraft.world.World; - import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; - -import net.minecraft.world.item.Item; -import net.minecraft.world.item.SwordItem; -import net.minecraft.world.item.Tier; -import net.minecraft.world.item.Tiers; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.network.chat.Component; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.*; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import org.dimdev.dimdoors.DimensionalDoors; import org.dimdev.dimdoors.block.DimensionalPortalBlock; import org.dimdev.dimdoors.block.ModBlocks; import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; import org.dimdev.dimdoors.block.entity.RiftBlockEntity; import org.dimdev.dimdoors.client.ToolTipHelper; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Objects; public class RiftBladeItem extends SwordItem { public static final String ID = "rift_blade"; @@ -45,23 +35,23 @@ public class RiftBladeItem extends SwordItem { @Environment(EnvType.CLIENT) @Override - public void appendTooltip(ItemStack itemStack, World world, List list, TooltipContext tooltipContext) { - ToolTipHelper.processTranslation(list, this.getTranslationKey() + ".info"); + public void appendHoverText(ItemStack itemStack, @Nullable Level level, List list, TooltipFlag tooltipFlag) { + ToolTipHelper.processTranslation(list, this.getDescriptionId() + ".info"); } @Override - public boolean hasGlint(ItemStack itemStack) { + public boolean isFoil(ItemStack itemStack) { return true; } @Override - public boolean canRepair(ItemStack item, ItemStack repairingItem) { - return Objects.equals(ModItems.STABLE_FABRIC, repairingItem.getItem()); + public boolean isValidRepairItem(ItemStack item, ItemStack repairingItem) { + return Objects.equals(ModItems.STABLE_FABRIC.get(), repairingItem.getItem()); } @Override - public TypedActionResult use(World world, PlayerEntity player, Hand hand) { - ItemStack stack = player.getStackInHand(hand); + public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); HitResult hit = RaycastHelper.raycast(player, 16, 0.0F, LivingEntity.class::isInstance); if (hit == null) { @@ -69,54 +59,54 @@ public class RiftBladeItem extends SwordItem { } if (hit == null) { - hit = player.raycast(16, 1.0F, false); //TODO: make the range of the Rift Blade configurable + hit = player.pick(16, 1.0F, false); //TODO: make the range of the Rift Blade configurable } if (hit == null) { - hit = player.raycast(16, 0, false); + hit = player.pick(16, 0, false); } - if (world.isClient) { + if (world.isClientSide) { if (RaycastHelper.hitsLivingEntity(hit) || RaycastHelper.hitsRift(hit, world)) { - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } else { - player.sendMessage(Text.translatable(this.getTranslationKey() + ".rift_miss"), true); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".rift_miss"), true); RiftBlockEntity.showRiftCoreUntil = System.currentTimeMillis() + DimensionalDoors.getConfig().getGraphicsConfig().highlightRiftCoreFor; - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } } if (RaycastHelper.hitsLivingEntity(hit)) { - double damageMultiplier = (double) stack.getDamage() / (double) stack.getMaxDamage(); + double damageMultiplier = (double) stack.getDamageValue() / (double) stack.getMaxDamage(); // TODO: gaussian, instead or random double offsetDistance = Math.random() * damageMultiplier * 7 + 2; //TODO: make these offset distances configurable double offsetRotationYaw = (Math.random() - 0.5) * damageMultiplier * 360; - Vec3d playerVec = player.getPos(); - Vec3d entityVec = hit.getPos(); - Vec3d offsetDirection = playerVec.subtract(entityVec).normalize(); - offsetDirection = offsetDirection.rotateY((float) (offsetRotationYaw * Math.PI) / 180); + var playerVec = player.position(); + var entityVec = hit.getLocation(); + var offsetDirection = playerVec.subtract(entityVec).normalize(); + offsetDirection = offsetDirection.yRot((float) (offsetRotationYaw * Math.PI) / 180); - Vec3d added = entityVec.add(offsetDirection.multiply(offsetDistance)); + Vec3 added = entityVec.add(offsetDirection.scale(offsetDistance)); BlockPos teleportPosition = new BlockPos(new Vec3i((int) added.x, (int) added. y, (int) added.z)); - while (world.getBlockState(teleportPosition).getMaterial().blocksMovement()) - teleportPosition = teleportPosition.up(); - player.teleport(teleportPosition.getX(), teleportPosition.getY(), teleportPosition.getZ()); - player.setYaw((float) (Math.random() * 2 * Math.PI)); + while (world.getBlockState(teleportPosition).getMaterial().blocksMotion()) + teleportPosition = teleportPosition.above(); + player.teleportTo(teleportPosition.getX(), teleportPosition.getY(), teleportPosition.getZ()); + player.setYRot((float) (Math.random() * 2 * Math.PI)); - stack.damage(1, player, a -> a.sendToolBreakStatus(hand)); - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + stack.hurtAndBreak(1, player, a -> a.broadcastBreakEvent(hand)); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } else if (RaycastHelper.hitsDetachedRift(hit, world)) { BlockHitResult blockHitResult = (BlockHitResult) hit; BlockPos pos = blockHitResult.getBlockPos(); RiftBlockEntity rift = (RiftBlockEntity) world.getBlockEntity(blockHitResult.getBlockPos()); - world.setBlockState(pos, ModBlocks.DIMENSIONAL_PORTAL.getDefaultState().with(DimensionalPortalBlock.FACING, blockHitResult.getSide())); + world.setBlockAndUpdate(pos, ModBlocks.DIMENSIONAL_PORTAL.get().defaultBlockState().setValue(DimensionalPortalBlock.FACING, blockHitResult.getDirection())); ((EntranceRiftBlockEntity) world.getBlockEntity(pos)).setData(rift.getData()); - stack.damage(1, player, a -> a.sendToolBreakStatus(hand)); - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + stack.hurtAndBreak(1, player, a -> a.broadcastBreakEvent(hand)); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RiftKeyItem.java b/common/src/main/java/org/dimdev/dimdoors/item/RiftKeyItem.java index be630600..7e8eb303 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RiftKeyItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RiftKeyItem.java @@ -57,11 +57,6 @@ public class RiftKeyItem extends Item { return !isEmpty(stack); } - @Override - public boolean isNbtSynced() { - return super.isNbtSynced(); - } - @Override public int getMaxUseTime(ItemStack stack) { return 30; diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RiftRemoverItem.java b/common/src/main/java/org/dimdev/dimdoors/item/RiftRemoverItem.java index 42ec095b..d308e560 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RiftRemoverItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RiftRemoverItem.java @@ -11,8 +11,12 @@ import net.minecraft.item.ItemStack; import net.minecraft.loot.context.LootContext; import net.minecraft.loot.context.LootContextParameters; import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; +import net.minecraft.sounds.SoundSource; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; @@ -24,21 +28,36 @@ import net.minecraft.util.TypedActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.World; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraft.world.phys.Vec3; import org.dimdev.dimdoors.DimensionalDoors; import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; import org.dimdev.dimdoors.block.entity.RiftBlockEntity; import org.dimdev.dimdoors.client.ToolTipHelper; import org.dimdev.dimdoors.sound.ModSoundEvents; +import org.jetbrains.annotations.Nullable; public class RiftRemoverItem extends Item { public static final String ID = "rift_remover"; - public static final Identifier REMOVED_RIFT_LOOT_TABLE = DimensionalDoors.id("removed_rift"); + public static final ResourceLocation REMOVED_RIFT_LOOT_TABLE = DimensionalDoors.id("removed_rift"); public RiftRemoverItem(Item.Properties settings) { super(settings); @@ -46,21 +65,21 @@ public class RiftRemoverItem extends Item { @Environment(EnvType.CLIENT) @Override - public void appendTooltip(ItemStack itemStack, World world, List list, TooltipContext tooltipContext) { - ToolTipHelper.processTranslation(list, this.getTranslationKey() + ".info"); + public void appendHoverText(ItemStack itemStack, @Nullable Level level, List list, TooltipFlag tooltipFlag) { + ToolTipHelper.processTranslation(list, this.getDescription() + ".info"); } @Override - public TypedActionResult use(World world, PlayerEntity player, Hand hand) { - ItemStack stack = player.getStackInHand(hand); - HitResult hit = player.raycast(RaycastHelper.REACH_DISTANCE, 0, false); + public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + HitResult hit = player.pick(RaycastHelper.REACH_DISTANCE, 0, false); - if (world.isClient) { + if (world.isClientSide) { if (!RaycastHelper.hitsDetachedRift(hit, world)) { - player.sendMessage(Text.translatable("tools.rift_miss"), true); + player.displayClientMessage(Component.translatable("tools.rift_miss"), true); RiftBlockEntity.showRiftCoreUntil = System.currentTimeMillis() + DimensionalDoors.getConfig().getGraphicsConfig().highlightRiftCoreFor; } - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } if (RaycastHelper.hitsDetachedRift(hit, world)) { @@ -68,19 +87,19 @@ public class RiftRemoverItem extends Item { DetachedRiftBlockEntity rift = (DetachedRiftBlockEntity) world.getBlockEntity(((BlockHitResult) hit).getBlockPos()); if (!Objects.requireNonNull(rift).closing) { rift.setClosing(true); - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_CLOSE, SoundCategory.BLOCKS, 0.6f, 1); - stack.damage(10, player, a -> a.sendToolBreakStatus(hand)); - LootContext ctx = new LootContext.Builder((ServerWorld) world).random(world.random).parameter(LootContextParameters.ORIGIN, Vec3d.ofCenter(((BlockHitResult) hit).getBlockPos())).optionalParameter(LootContextParameters.THIS_ENTITY, player).build(LootContextTypes.GENERIC); - ((ServerWorld) world).getServer().getLootManager().getTable(REMOVED_RIFT_LOOT_TABLE).generateLoot(ctx).forEach(stack1 -> { - ItemScatterer.spawn(world, ((BlockHitResult) hit).getBlockPos().getX(), ((BlockHitResult) hit).getBlockPos().getY(), ((BlockHitResult) hit).getBlockPos().getZ(), stack1); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_CLOSE.get(), SoundSource.BLOCKS, 0.6f, 1); + stack.hurtAndBreak(10, player, a -> a.broadcastBreakEvent(hand)); + LootContext ctx = new LootContext.Builder((ServerLevel) world).withRandom(world.random).withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(((BlockHitResult) hit).getBlockPos())).withOptionalParameter(LootContextParams.THIS_ENTITY, player).create(LootContextParamSets.ALL_PARAMS); + ((ServerLevel) world).getServer().getLootTables().get(REMOVED_RIFT_LOOT_TABLE).getRandomItems(ctx).forEach(stack1 -> { + Containers.dropItemStack(world, ((BlockHitResult) hit).getBlockPos().getX(), ((BlockHitResult) hit).getBlockPos().getY(), ((BlockHitResult) hit).getBlockPos().getZ(), stack1); }); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".closing"), true); - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".closing"), true); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } else { - player.sendMessage(Text.translatable(this.getTranslationKey() + ".already_closing"), true); + player.displayClientMessage(Component.translatable(this.getDescription() + ".already_closing"), true); } } - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RiftSignatureItem.java b/common/src/main/java/org/dimdev/dimdoors/item/RiftSignatureItem.java index 59c6309a..b08491d8 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RiftSignatureItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RiftSignatureItem.java @@ -1,32 +1,21 @@ package org.dimdev.dimdoors.item; -import java.util.List; - +import net.minecraft.ChatFormatting; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; -import org.jetbrains.annotations.NotNull; - -import net.minecraft.client.item.TooltipContext; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUsageContext; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.sound.SoundCategory; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.text.TranslatableTextContent; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Formatting; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.World; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; import org.dimdev.dimdoors.DimensionalDoors; import org.dimdev.dimdoors.api.util.Location; import org.dimdev.dimdoors.api.util.RotatedLocation; @@ -37,6 +26,8 @@ import org.dimdev.dimdoors.rift.targets.RiftReference; import org.dimdev.dimdoors.sound.ModSoundEvents; import org.dimdev.dimdoors.world.ModDimensions; +import java.util.List; + public class RiftSignatureItem extends Item { public static final String ID = "rift_signature"; @@ -45,105 +36,103 @@ public class RiftSignatureItem extends Item { } @Override - public boolean hasGlint(ItemStack stack) { - return stack.getNbt() != null && stack.getNbt().contains("destination"); + public boolean isFoil(ItemStack stack) { + return stack.getTag() != null && stack.getTag().contains("destination"); } @Override - public ActionResult useOnBlock(@NotNull ItemUsageContext itemUsageContext) { - PlayerEntity player = itemUsageContext.getPlayer(); - World world = itemUsageContext.getWorld(); - BlockPos pos = itemUsageContext.getBlockPos(); - Hand hand = itemUsageContext.getHand(); - Direction side = itemUsageContext.getSide(); + public InteractionResult useOn(UseOnContext itemUsageContext) { + Player player = itemUsageContext.getPlayer(); + Level world = itemUsageContext.getLevel(); + BlockPos pos = itemUsageContext.getClickedPos(); + InteractionHand hand = itemUsageContext.getHand(); + Direction side = itemUsageContext.getClickedFace(); - ItemPlacementContext placementContext = new ItemPlacementContext(itemUsageContext); + BlockPlaceContext placementContext = new BlockPlaceContext(itemUsageContext); - ItemStack stack = player.getStackInHand(hand); - pos = world.getBlockState(pos).getBlock().canReplace(world.getBlockState(pos), placementContext) ? pos : pos.offset(side); + ItemStack stack = player.getItemInHand(hand); + pos = world.getBlockState(pos).getBlock().canBeReplaced(world.getBlockState(pos), placementContext) ? pos : pos.relative(side); // Fail if the player can't place a block there - if (!player.canPlaceOn(pos, side.getOpposite(), stack)) { - return ActionResult.FAIL; + if (!player.mayUseItemAt(pos, side.getOpposite(), stack)) { + return InteractionResult.FAIL; } - if (world.isClient) { - return ActionResult.SUCCESS; + if (world.isClientSide) { + return InteractionResult.SUCCESS; } if(ModDimensions.isPrivatePocketDimension(world) && !DimensionalDoors.getConfig().getPocketsConfig().canUseRiftSignatureInPrivatePockets) { - player.sendMessage(Text.translatable("tools.signature_blocked").formatted(Formatting.BLACK), true); - return ActionResult.FAIL; + player.displayClientMessage(Component.translatable("tools.signature_blocked").withStyle(ChatFormatting.BLACK), true); + return InteractionResult.FAIL; } RotatedLocation target = getSource(stack); if (target == null) { // The link signature has not been used. Store its current target as the first location. - setSource(stack, new RotatedLocation(world.getRegistryKey(), pos, player.getYaw(), 0)); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".stored"), true); - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_START, SoundCategory.BLOCKS, 0.6f, 1); + setSource(stack, new RotatedLocation(world.dimension(), pos, player.getYRot(), 0)); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".stored"), true); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_START.get(), SoundSource.BLOCKS, 0.6f, 1); } else { // Place a rift at the saved point if (target.getBlockState().getBlock() != ModBlocks.DETACHED_RIFT) { - if (!target.getBlockState().getBlock().canMobSpawnInside()) { - player.sendMessage(Text.translatable("tools.target_became_block"), true); + if (!target.getBlockState().getBlock().isPossibleToRespawnInThis()) { + player.displayClientMessage(Component.translatable("tools.target_became_block"), true); clearSource(stack); // TODO: But is this fair? It's a rather hidden way of unbinding your signature! - return ActionResult.FAIL; + return InteractionResult.FAIL; } - World sourceWorld = DimensionalDoors.getWorld(target.world); - sourceWorld.setBlockState(target.getBlockPos(), ModBlocks.DETACHED_RIFT.getDefaultState()); + Level sourceWorld = DimensionalDoors.getWorld(target.world); + sourceWorld.setBlockAndUpdate(target.getBlockPos(), ModBlocks.DETACHED_RIFT.get().defaultBlockState()); DetachedRiftBlockEntity rift1 = (DetachedRiftBlockEntity) target.getBlockEntity(); - rift1.setDestination(RiftReference.tryMakeRelative(target, new Location((ServerWorld) world, pos))); + rift1.setDestination(RiftReference.tryMakeRelative(target, new Location((ServerLevel) world, pos))); rift1.register(); } // Place a rift at the target point - world.setBlockState(pos, ModBlocks.DETACHED_RIFT.getDefaultState()); + world.setBlockAndUpdate(pos, ModBlocks.DETACHED_RIFT.get().defaultBlockState()); DetachedRiftBlockEntity rift2 = (DetachedRiftBlockEntity) world.getBlockEntity(pos); - rift2.setDestination(RiftReference.tryMakeRelative(new Location((ServerWorld) world, pos), target)); + rift2.setDestination(RiftReference.tryMakeRelative(new Location((ServerLevel) world, pos), target)); rift2.register(); - stack.damage(1, player, a -> { - }); // TODO: calculate damage based on position? + stack.hurtAndBreak(1, player, a -> {}); // TODO: calculate damage based on position? clearSource(stack); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".created"), true); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".created"), true); // null = send sound to the player too, we have to do this because this code is not run client-side - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_END, SoundCategory.BLOCKS, 0.6f, 1); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_END.get(), SoundSource.BLOCKS, 0.6f, 1); } - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } public static void setSource(ItemStack itemStack, RotatedLocation destination) { - if (!itemStack.hasNbt()) itemStack.setNbt(new NbtCompound()); - itemStack.getNbt().put("destination", RotatedLocation.serialize(destination)); + if (!itemStack.hasTag()) itemStack.setTag(new CompoundTag()); + itemStack.getTag().put("destination", RotatedLocation.serialize(destination)); } public static void clearSource(ItemStack itemStack) { - if (itemStack.hasNbt()) { - itemStack.getNbt().remove("destination"); + if (itemStack.hasTag()) { + itemStack.getTag().remove("destination"); } } public static RotatedLocation getSource(ItemStack itemStack) { - if (itemStack.hasNbt() && itemStack.getNbt().contains("destination")) { - return RotatedLocation.deserialize(itemStack.getNbt().getCompound("destination")); + if (itemStack.hasTag() && itemStack.getTag().contains("destination")) { + return RotatedLocation.deserialize(itemStack.getTag().getCompound("destination")); } else { return null; } } @Override - @Environment(EnvType.CLIENT) - public void appendTooltip(ItemStack itemStack, World world, List list, TooltipContext tooltipContext) { + public void appendHoverText(ItemStack itemStack, Level world, List list, TooltipFlag tooltipContext) { RotatedLocation transform = getSource(itemStack); if (transform != null) { - list.add(Text.translatable(this.getTranslationKey() + ".bound.info0", transform.getX(), transform.getY(), transform.getZ(), transform.getWorldId().getValue())); - list.add(Text.translatable(this.getTranslationKey() + ".bound.info1", transform.getWorldId().getValue())); + list.add(Component.translatable(this.getDescriptionId() + ".bound.info0", transform.getX(), transform.getY(), transform.getZ(), transform.getWorldId().getValue())); + list.add(Component.translatable(this.getDescriptionId() + ".bound.info1", transform.getWorldId().location())); } else { - ToolTipHelper.processTranslation(list, this.getTranslationKey() + ".unbound.info"); + ToolTipHelper.processTranslation(list, this.getDescriptionId() + ".unbound.info"); } } } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/RiftStabilizerItem.java b/common/src/main/java/org/dimdev/dimdoors/item/RiftStabilizerItem.java index 17565cc2..92f888cc 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/RiftStabilizerItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/RiftStabilizerItem.java @@ -3,10 +3,14 @@ package org.dimdev.dimdoors.item; import java.util.List; import net.minecraft.client.item.TooltipContext; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; +import net.minecraft.network.chat.Component; import net.minecraft.sound.SoundCategory; +import net.minecraft.sounds.SoundSource; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.text.TranslatableTextContent; @@ -16,57 +20,67 @@ import net.minecraft.util.TypedActionResult; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3i; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.InteractionResultHolder; import net.minecraft.world.World; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.phys.HitResult; import org.dimdev.dimdoors.DimensionalDoors; import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; import org.dimdev.dimdoors.block.entity.RiftBlockEntity; import org.dimdev.dimdoors.sound.ModSoundEvents; +import org.jetbrains.annotations.Nullable; + +import static com.sun.org.apache.xalan.internal.xsltc.compiler.util.Type.Text; public class RiftStabilizerItem extends Item { public RiftStabilizerItem(Item.Properties settings) { super(settings); } - - @Override - public TypedActionResult use(World world, PlayerEntity player, Hand hand) { - ItemStack stack = player.getStackInHand(hand); - HitResult hit = player.raycast(RaycastHelper.REACH_DISTANCE, 0, false); - if (world.isClient) { + @Override + public InteractionResultHolder use(Level world, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + HitResult hit = player.pick(RaycastHelper.REACH_DISTANCE, 0, false); + + if (world.isClientSide) { if (RaycastHelper.hitsDetachedRift(hit, world)) { // TODO: not necessarily success, fix this and all other similar cases to make arm swing correct - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } else { - player.sendMessage(Text.translatable("tools.rift_miss"), true); + player.displayClientMessage(Component.translatable("tools.rift_miss"), true); RiftBlockEntity.showRiftCoreUntil = System.currentTimeMillis() + DimensionalDoors.getConfig().getGraphicsConfig().highlightRiftCoreFor; - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } } if (RaycastHelper.hitsDetachedRift(hit, world)) { - DetachedRiftBlockEntity rift = (DetachedRiftBlockEntity) world.getBlockEntity(new BlockPos(new Vec3i((int) hit.getPos().x, (int) hit.getPos().y, (int) hit.getPos().z))); + DetachedRiftBlockEntity rift = (DetachedRiftBlockEntity) world.getBlockEntity(new BlockPos(new Vec3i((int) hit.getLocation().x, (int) hit.getLocation().y, (int) hit.getLocation().z))); if (!rift.stabilized && !rift.closing) { rift.setStabilized(true); - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_CLOSE, SoundCategory.BLOCKS, 0.6f, 1); // TODO: different sound - stack.damage(1, player, a -> { - }); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".stabilized"), true); - return new TypedActionResult<>(ActionResult.SUCCESS, stack); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_CLOSE.get(), SoundSource.BLOCKS, 0.6f, 1); // TODO: different sound + stack.hurtAndBreak(1, player, a -> {}); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".stabilized"), true); + return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack); } else { - player.sendMessage(Text.translatable(this.getTranslationKey() + ".already_stabilized"), true); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".already_stabilized"), true); } } - return new TypedActionResult<>(ActionResult.FAIL, stack); + return new InteractionResultHolder<>(InteractionResult.FAIL, stack); } - @Environment(EnvType.CLIENT) @Override - public void appendTooltip(ItemStack itemStack, World world, List list, TooltipContext tooltipContext) { - list.add(Text.translatable(this.getTranslationKey() + ".info")); + public void appendHoverText(ItemStack itemStack, @Nullable Level level, List list, TooltipFlag tooltipFlag) { + list.add(Component.translatable(this.getDescriptionId() + ".info")); } } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/StabilizedRiftSignatureItem.java b/common/src/main/java/org/dimdev/dimdoors/item/StabilizedRiftSignatureItem.java index a039c705..62eb0286 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/StabilizedRiftSignatureItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/StabilizedRiftSignatureItem.java @@ -1,23 +1,26 @@ package org.dimdev.dimdoors.item; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.item.ItemPlacementContext; -import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUsageContext; +import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtCompound; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; -import net.minecraft.text.MutableText; +import net.minecraft.sounds.SoundSource; import net.minecraft.text.Text; -import net.minecraft.text.TranslatableTextContent; import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.World; - +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; import org.dimdev.dimdoors.DimensionalDoors; import org.dimdev.dimdoors.api.util.Location; import org.dimdev.dimdoors.api.util.RotatedLocation; @@ -34,82 +37,81 @@ public class StabilizedRiftSignatureItem extends Item { // TODO: common supercla } @Override - public boolean hasGlint(ItemStack stack) { - return stack.getNbt() != null && stack.getNbt().contains("destination"); + public boolean isFoil(ItemStack stack) { + return stack.getTag() != null && stack.getTag().contains("destination"); } @Override - public ActionResult useOnBlock(ItemUsageContext itemUsageContext) { - PlayerEntity player = itemUsageContext.getPlayer(); - World world = itemUsageContext.getWorld(); - BlockPos pos = itemUsageContext.getBlockPos(); - Hand hand = itemUsageContext.getHand(); - Direction side = itemUsageContext.getSide(); + public InteractionResult useOn(UseOnContext itemUsageContext) { + Player player = itemUsageContext.getPlayer(); + Level world = itemUsageContext.getLevel(); + BlockPos pos = itemUsageContext.getClickedPos()(); + InteractionHand hand = itemUsageContext.getHand(); + Direction side = itemUsageContext.getClickedFace(); - ItemPlacementContext itemPlacementContext = new ItemPlacementContext(itemUsageContext); + BlockPlaceContext itemPlacementContext = new BlockPlaceContext(itemUsageContext); - ItemStack stack = player.getStackInHand(hand); - pos = world.getBlockState(pos).getBlock().canReplace(world.getBlockState(pos), new ItemPlacementContext(itemUsageContext)) ? pos : pos.offset(side); + ItemStack stack = player.getItemInHand(hand); + pos = world.getBlockState(pos).getBlock().canBeReplaced(world.getBlockState(pos), new BlockPlaceContext(itemUsageContext)) ? pos : pos.relative(side); // Fail if the player can't place a block there - if (!player.canPlaceOn(pos, side.getOpposite(), stack)) { - return ActionResult.FAIL; + if (!player.mayUseItemAt(pos, side.getOpposite(), stack)) { + return InteractionResult.FAIL; } - if (world.isClient) { - return ActionResult.SUCCESS; + if (world.isClientSide) { + return InteractionResult.SUCCESS; } RotatedLocation target = getTarget(stack); if (target == null) { // The link signature has not been used. Store its current target as the first location. - setSource(stack, new RotatedLocation(world.getRegistryKey(), pos, player.getYaw(), 0)); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".stored"), true); - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_START, SoundCategory.BLOCKS, 0.6f, 1); + setSource(stack, new RotatedLocation(world.dimension(), pos, player.getYRot(), 0)); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".stored"), true); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_START.get(), SoundSource.BLOCKS, 0.6f, 1); } else { // Place a rift at the target point if (target.getBlockState().getBlock() != ModBlocks.DETACHED_RIFT) { - if (!target.getBlockState().getBlock().canReplace(world.getBlockState(target.getBlockPos()), itemPlacementContext)) { - player.sendMessage(Text.translatable("tools.target_became_block"), true); + if (!target.getBlockState().getBlock().canBeReplaced(world.getBlockState(target.getBlockPos()), itemPlacementContext)) { + player.displayClientMessage(Component.translatable("tools.target_became_block"), true); // Don't clear source, stabilized signatures always stay bound - return ActionResult.FAIL; + return InteractionResult.FAIL; } - World targetWorld = DimensionalDoors.getWorld(target.world); - targetWorld.setBlockState(target.getBlockPos(), ModBlocks.DETACHED_RIFT.getDefaultState()); + Level targetWorld = DimensionalDoors.getWorld(target.world); + targetWorld.setBlockAndUpdate(target.getBlockPos(), ModBlocks.DETACHED_RIFT.get().defaultBlockState()); DetachedRiftBlockEntity rift1 = (DetachedRiftBlockEntity) target.getBlockEntity(); rift1.register(); } // Place a rift at the source point - world.setBlockState(pos, ModBlocks.DETACHED_RIFT.getDefaultState()); + world.setBlockAndUpdate(pos, ModBlocks.DETACHED_RIFT.get().defaultBlockState()); DetachedRiftBlockEntity rift2 = (DetachedRiftBlockEntity) world.getBlockEntity(pos); - rift2.setDestination(RiftReference.tryMakeRelative(new Location((ServerWorld) world, pos), target)); + rift2.setDestination(RiftReference.tryMakeRelative(new Location((ServerLevel) world, pos), target)); rift2.register(); - stack.damage(1, player, playerEntity -> { - }); + stack.hurtAndBreak(1, player, playerEntity -> {}); - player.sendMessage(Text.translatable(this.getTranslationKey() + ".created"), true); - world.playSound(null, player.getBlockPos(), ModSoundEvents.RIFT_END, SoundCategory.BLOCKS, 0.6f, 1); + player.displayClientMessage(Component.translatable(this.getDescriptionId() + ".created"), true); + world.playSound(null, player.blockPosition(), ModSoundEvents.RIFT_END.get(), SoundSource.BLOCKS, 0.6f, 1); } - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } public static void setSource(ItemStack itemStack, RotatedLocation destination) { - if (!itemStack.hasNbt()) itemStack.setNbt(new NbtCompound()); - itemStack.getNbt().put("destination", RotatedLocation.serialize(destination)); + if (!itemStack.hasTag()) itemStack.setTag(new CompoundTag()); + itemStack.getTag().put("destination", RotatedLocation.serialize(destination)); } public static void clearSource(ItemStack itemStack) { - if (itemStack.hasNbt()) { - itemStack.getNbt().remove("destination"); + if (itemStack.hasTag()) { + itemStack.getTag().remove("destination"); } } public static RotatedLocation getTarget(ItemStack itemStack) { - if (itemStack.hasNbt() && itemStack.getNbt().contains("destination")) { - return RotatedLocation.deserialize(itemStack.getNbt().getCompound("destination")); + if (itemStack.hasTag() && itemStack.getTag().contains("destination")) { + return RotatedLocation.deserialize(itemStack.getTag().getCompound("destination")); } else { return null; } diff --git a/common/src/main/java/org/dimdev/dimdoors/item/door/DimensionalTrapdoorItem.java b/common/src/main/java/org/dimdev/dimdoors/item/door/DimensionalTrapdoorItem.java index b037e9ca..edffe96a 100644 --- a/common/src/main/java/org/dimdev/dimdoors/item/door/DimensionalTrapdoorItem.java +++ b/common/src/main/java/org/dimdev/dimdoors/item/door/DimensionalTrapdoorItem.java @@ -1,21 +1,18 @@ package org.dimdev.dimdoors.item.door; -import java.util.function.Consumer; - -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.item.BlockItem; -import net.minecraft.item.ItemPlacementContext; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; import org.dimdev.dimdoors.block.RiftProvider; import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import java.util.function.Consumer; + public class DimensionalTrapdoorItem extends BlockItem { private final Consumer setupFunction; @@ -25,20 +22,20 @@ public class DimensionalTrapdoorItem extends BlockItem { } @Override - public ActionResult place(ItemPlacementContext context) { - World world = context.getWorld(); - BlockPos pos = context.getBlockPos(); + public InteractionResult place(BlockPlaceContext context) { + Level world = context.getLevel(); + BlockPos pos = context.getClickedPos(); - if (world.isClient) { + if (world.isClientSide) { return super.place(context); } - boolean replaceable = world.getBlockState(pos).canReplace(context); // Check this before calling super, since that changes the block - ActionResult result = super.place(context); + boolean replaceable = world.getBlockState(pos).canBeReplaced(context); // Check this before calling super, since that changes the block + InteractionResult result = super.place(context); - if (result == ActionResult.SUCCESS) { + if (result == InteractionResult.SUCCESS) { if (!replaceable) { - pos = pos.offset(context.getPlayerLookDirection()); + pos = pos.relative(context.getNearestLookingDirection()); } BlockState state = world.getBlockState(pos); @@ -49,7 +46,7 @@ public class DimensionalTrapdoorItem extends BlockItem { this.setupRift(entranceRift); // Register the rift in the registry - entranceRift.markDirty(); + entranceRift.setChanged(); entranceRift.register(); } diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ArgumentTypesMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ArgumentTypesMixin.java new file mode 100644 index 00000000..35348d03 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ArgumentTypesMixin.java @@ -0,0 +1,28 @@ +package org.dimdev.dimdoors.mixin; + +import com.mojang.brigadier.arguments.ArgumentType; +import net.minecraft.commands.synchronization.ArgumentTypeInfo; +import net.minecraft.commands.synchronization.ArgumentTypeInfos; +import net.minecraft.commands.synchronization.SingletonArgumentInfo; +import net.minecraft.core.Registry; +import org.dimdev.dimdoors.command.arguments.BlockPlacementTypeArgumentType; +import org.dimdev.dimdoors.command.arguments.PocketTemplateArgumentType; +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; + +@Mixin(ArgumentTypeInfos.class) +public abstract class ArgumentTypesMixin { + @Shadow + private static , T extends ArgumentTypeInfo.Template> ArgumentTypeInfo register(Registry> registry, String string, Class clazz, ArgumentTypeInfo argumentSerializer) { + throw new AssertionError("Nope."); + } + + @Inject(method = "bootstrap", at = @At("RETURN")) + private static void register(Registry> registry, CallbackInfoReturnable> ci) { + register(registry, "pocket", PocketTemplateArgumentType.class, SingletonArgumentInfo.contextFree(PocketTemplateArgumentType::new)); + register(registry, "block_placement_type", BlockPlacementTypeArgumentType.class, SingletonArgumentInfo.contextFree(BlockPlacementTypeArgumentType::blockPlacementType)); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/BlockMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/BlockMixin.java new file mode 100644 index 00000000..5390939a --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/BlockMixin.java @@ -0,0 +1,23 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.dimdev.dimdoors.criteria.ModCriteria; +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(Block.class) +public class BlockMixin { + @Inject(method = "playerWillDestroy", at = @At(value = "INVOKE", shift = At.Shift.AFTER, target = "Lnet/minecraft/world/level/block/Block;spawnDestroyParticles(Lnet/minecraft/world/level/Level;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)V")) + public void triggerTagBlockBreak(Level world, BlockPos pos, BlockState state, Player player, CallbackInfo ci) { + if (!world.isClientSide()) { + ModCriteria.TAG_BLOCK_BREAK.trigger((ServerPlayer) player, state); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/DoorBlockMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/DoorBlockMixin.java new file mode 100644 index 00000000..fb1322dd --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/DoorBlockMixin.java @@ -0,0 +1,31 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.level.block.state.properties.BlockSetType; +import org.dimdev.dimdoors.block.DoorSoundProvider; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(DoorSoundProvider.class) +public abstract class DoorBlockMixin implements DoorSoundProvider { + + @Shadow + @Final + private BlockSetType blockSetType; + + @Override + public SoundEvent getOpenSound() { + return this.blockSetType.doorOpen(); + } + + @Override + public SoundEvent getCloseSound() { + return this.blockSetType.doorClose(); + } + + @Override + public BlockSetType getSetType() { + return this.blockSetType; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java new file mode 100644 index 00000000..0a974538 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/EntityMixin.java @@ -0,0 +1,24 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.phys.Vec3; +import org.dimdev.dimdoors.api.entity.LastPositionProvider; +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(Entity.class) +public abstract class EntityMixin implements LastPositionProvider { + private Vec3 lastPos; + + @Inject(method = "checkInsideBlocks()V", at = @At("TAIL")) + public void checkBlockCollisionSaveLastPos(CallbackInfo ci) { + lastPos = ((Entity) (Object) this).position(); + } + + public Vec3 getLastPos() { + return lastPos == null ? ((Entity) (Object) this).position() : lastPos; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java new file mode 100644 index 00000000..4c51ea45 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ExplosionMixin.java @@ -0,0 +1,44 @@ +package org.dimdev.dimdoors.mixin; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.dimdev.dimdoors.api.block.ExplosionConvertibleBlock; +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.stream.Collectors; + +@Mixin(Explosion.class) +public class ExplosionMixin { + @Mutable + @Shadow + @Final + private ObjectArrayList toBlow; + + @Shadow + @Final + private Level level; + + @Inject(method = "finalizeExplosion", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;shuffle(Lit/unimi/dsi/fastutil/objects/ObjectArrayList;Lnet/minecraft/util/RandomSource;)V", ordinal = 0, shift = At.Shift.AFTER)) + private void handleExplosionConvertibleBlocks(boolean b1, CallbackInfo ci) { + this.toBlow = this.toBlow.stream().filter(blockPos -> { + BlockState state = this.level.getBlockState(blockPos); + Block block = state.getBlock(); + if (!(block instanceof ExplosionConvertibleBlock)) { + return true; + } + InteractionResult result = ((ExplosionConvertibleBlock) block).explode(this.level, blockPos, state, state.hasBlockEntity() ? this.level.getBlockEntity(blockPos) : null); + return result == InteractionResult.PASS; + }).collect(Collectors.toCollection(ObjectArrayList::new)); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ExtendedServerPlayNetworkhandlerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ExtendedServerPlayNetworkhandlerMixin.java new file mode 100644 index 00000000..10cd1cec --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ExtendedServerPlayNetworkhandlerMixin.java @@ -0,0 +1,26 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.dimdev.dimdoors.network.ExtendedServerPlayNetworkHandler; +import org.dimdev.dimdoors.network.ServerPacketHandler; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerGamePacketListenerImpl.class) +public class ExtendedServerPlayNetworkhandlerMixin implements ExtendedServerPlayNetworkHandler { + @Final @Shadow + private MinecraftServer server; + private final ServerPacketHandler dimdoorsServerPacketHandler = new ServerPacketHandler((ServerGamePacketListenerImpl) (Object) this); + + @Override + public ServerPacketHandler getDimDoorsPacketHandler() { + return dimdoorsServerPacketHandler; + } + + @Override + public MinecraftServer dimdoorsGetServer() { + return server; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ItemMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ItemMixin.java new file mode 100644 index 00000000..8e1f1871 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ItemMixin.java @@ -0,0 +1,37 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.world.item.Item; +import org.dimdev.dimdoors.item.ItemExtensions; +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.CallbackInfo; + +@Mixin(Item.class) +public class ItemMixin implements ItemExtensions { + @Unique + private Item.Properties settings; + + @Inject(method = "", at = @At("TAIL")) + public void cacheSettings(Item.Properties settings, CallbackInfo ci) { + this.settings = settings; + } + + @Override + public Item.Properties dimdoors_getSettings() { + return settings; + } + + @Mixin(Item.Properties.class) + public static class SettingsMixin implements SettingsExtensions { + @Override + public Item.Properties clone() { + try { + return (Item.Properties) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(e); // Cant happen, we are Cloneable + } + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/PlayerEntityMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/PlayerEntityMixin.java new file mode 100644 index 00000000..45625126 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/PlayerEntityMixin.java @@ -0,0 +1,52 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.mixin.accessor.EntityAccessor; +import org.dimdev.dimdoors.world.ModDimensions; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(value = Player.class, priority = 900) +public abstract class PlayerEntityMixin extends LivingEntity { + + @Shadow + public abstract void awardStat(ResourceLocation stat); + + public PlayerEntityMixin(EntityType entityType, Level world) { + super(entityType, world); + } + + + @Inject(method = "causeFallDamage", at = @At("HEAD"), cancellable = true) + public void handleLimboFallDamage(float fallDistance, float damageMultiplier, DamageSource damageSource, CallbackInfoReturnable cir) { + if (ModDimensions.isLimboDimension(level)) { + cir.setReturnValue(false); + } + } + + @Inject(method = "die", at = @At("HEAD"), cancellable = true) + public void checkDeath(DamageSource source, CallbackInfo ci) { + this.doOnDeathStuff(source, ci); + } + + @Unique + protected void doOnDeathStuff(DamageSource source, CallbackInfo ci) { + if (ModDimensions.isPocketDimension(this.level) || DimensionalDoors.getConfig().getLimboConfig().universalLimbo) { + ((EntityAccessor) this).setRemovalReason(null); + this.dead = false; + this.setHealth(this.getMaxHealth()); + ci.cancel(); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..484fc91b --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayNetworkHandlerMixin.java @@ -0,0 +1,68 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import org.dimdev.dimdoors.api.block.AfterMoveCollidableBlock; +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.CallbackInfo; + +@Mixin(ServerGamePacketListenerImpl.class) +public class ServerPlayNetworkHandlerMixin { + @Shadow + public ServerPlayer player; + @Shadow + private double firstGoodX; + @Shadow + private double firstGoodY; + @Shadow + private double firstGoodZ; + + @Shadow private double lastGoodX; + + @Shadow private double lastGoodY; + + @Shadow private double lastGoodZ; + + @Inject(method = "handleMovePlayer", at = @At("TAIL")) + protected void checkBlockCollision(ServerboundMovePlayerPacket packet, CallbackInfo ci) { + // stolen from Entity#checkBlockCollision + AABB box = player.getBoundingBox(); + BlockPos blockPos = new BlockPos((int) (box.minX + 0.001D), (int) (box.minY + 0.001D), (int) (box.minZ + 0.001D)); + BlockPos blockPos2 = new BlockPos((int) (box.maxX - 0.001D), (int) (box.maxY - 0.001D), (int) (box.maxZ - 0.001D)); + if (player.level.hasChunksAt(blockPos, blockPos2)) { + BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); + + boolean done = false; + for(int i = blockPos.getX(); i <= blockPos2.getX(); ++i) { + for(int j = blockPos.getY(); j <= blockPos2.getY(); ++j) { + for(int k = blockPos.getZ(); k <= blockPos2.getZ(); ++k) { + mutable.set(i, j, k); + BlockState blockState = player.level.getBlockState(mutable); + Block block = blockState.getBlock(); + if (block instanceof AfterMoveCollidableBlock && ((AfterMoveCollidableBlock) block).onAfterMovePlayerCollision(blockState, player.getLevel(), mutable, player, player.position().subtract(lastGoodX, lastGoodY, lastGoodZ)).consumesAction()) { + done = true; + } + if (done) { + break; + } + } + if (done) { + break; + } + } + if (done) { + break; + } + } + } + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerEntityMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerEntityMixin.java new file mode 100644 index 00000000..67e61610 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerEntityMixin.java @@ -0,0 +1,155 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.ServerRecipeBook; +import net.minecraft.stats.Stat; +import net.minecraft.util.RandomSource; +import net.minecraft.world.damagesource.DamageSource; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; +import org.dimdev.dimdoors.api.util.TeleportUtil; +import org.dimdev.dimdoors.block.UnravelledFabricBlock; +import org.dimdev.dimdoors.criteria.ModCriteria; +import org.dimdev.dimdoors.entity.limbo.LimboEntranceSource; +import org.dimdev.dimdoors.entity.stat.ModStats; +import org.dimdev.dimdoors.world.ModDimensions; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +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.CallbackInfo; + +@Mixin(value = ServerPlayer.class, priority = 900) +public abstract class ServerPlayerEntityMixin extends PlayerEntityMixin { + @Shadow + @Final + private ServerRecipeBook recipeBook; + + @Shadow + public abstract void readAdditionalSaveData(CompoundTag nbt); + + @Shadow public abstract void awardStat(Stat arg, int i); + + private static final float RANDOM_ACTION_CHANCE = 0.1F; + private static final float CHANCE_TO_MAKE_LIMBO_LIKE_OTHER_DIMENSIONS = 0.1F; + private static final int CHUNK_SIZES = 25; + private static final int POSITION_AWAY = 50; + private static final float RANDOM_LIQUID_CHANCE = 0.7F; + @Unique + RandomSource dimdoors_random = RandomSource.create(); + + public ServerPlayerEntityMixin(EntityType entityType, Level world) { + super(entityType, world); + } + + @Inject(method = "tick", at = @At("HEAD")) + public void playerTickMixin(CallbackInfo ci) { + if (dimdoors_random.nextFloat() <= RANDOM_ACTION_CHANCE) { + if(ModDimensions.isLimboDimension(((Player)(Object)(this)).getLevel())) { + tryMakingLimboLikeOtherDimensions((Player)(Object)this); + } + } + + } + private boolean isValidBlockToReplace(Level world, BlockPos pos) { + return world.getBlockState(pos.above()).isAir() && world.getBlockState(pos).getBlock() instanceof UnravelledFabricBlock; + } + private void makeLimboLikeOverworld(Player player) { + /* + World world = player.getEntityWorld(); + BlockPos pos = player.getBlockPos().add(random.nextInt(random.nextInt(POSITION_AWAY)), 0, random.nextInt(POSITION_AWAY)); + if(random.nextFloat() <= RANDOM_LIQUID_CHANCE) { + makeSpotOfLiquid(world, pos, Blocks.WATER.getDefaultState(), 3); + } + BlockPos.iterateOutwards(pos, CHUNK_SIZES, 15, CHUNK_SIZES).forEach( (blockPos -> { + if(isValidBlockToReplace(world, blockPos)) { + world.setBlockState(blockPos, Blocks.GRASS_BLOCK.getDefaultState()); + } + + })); + + */ + + } + private void makeLimboLikeEnd(Player player) { + /* + World world = player.getEntityWorld(); + BlockPos pos = player.getBlockPos().add(random.nextInt(POSITION_AWAY), 0, random.nextInt(POSITION_AWAY)); + BlockPos.iterateOutwards(pos, CHUNK_SIZES, 15, CHUNK_SIZES).forEach( (blockPos -> { + if(isValidBlockToReplace(world, blockPos)) { + world.setBlockState(blockPos, Blocks.END_STONE.getDefaultState()); + } + + })); + + */ + } + + private void makeSpotOfLiquid(Level world, BlockPos pos, BlockState state, int range) { + + BlockPos.withinManhattan(pos, dimdoors_random.nextInt(range), dimdoors_random.nextInt(range), dimdoors_random.nextInt(range)).forEach( (blockPos -> { + if(isValidBlockToReplace(world, blockPos)) { + world.setBlockAndUpdate(blockPos, state); + } + })); + + } + + private void makeLimboLikeNether(Player player) { + /* + World world = player.getEntityWorld(); + BlockPos pos = player.getBlockPos().add(random.nextInt(POSITION_AWAY), 0, random.nextInt(POSITION_AWAY)); + if(random.nextFloat() <= RANDOM_LIQUID_CHANCE) { + makeSpotOfLiquid(world, pos, Blocks.LAVA.getDefaultState(), 10); + } + BlockPos.iterateOutwards(pos, CHUNK_SIZES, 15, CHUNK_SIZES).forEach( (blockPos -> { + if(isValidBlockToReplace(world, blockPos)) { + world.setBlockState(blockPos, Blocks.NETHERRACK.getDefaultState()); + } + + })); + + */ + } + private void tryMakingLimboLikeOtherDimensions(Player player) { + if(dimdoors_random.nextFloat() > CHANCE_TO_MAKE_LIMBO_LIKE_OTHER_DIMENSIONS) { + return; + } + switch (dimdoors_random.nextInt(3)) { + case 0 -> makeLimboLikeOverworld(player); + case 1 -> makeLimboLikeNether(player); + case 2 -> makeLimboLikeEnd(player); + } + } + + @Inject(method = "die", at = @At("HEAD"), cancellable = true) + public void checkDeathServer(DamageSource source, CallbackInfo ci) { + this.doOnDeathStuff(source, ci); + if (ci.isCancelled()) { + if (ModDimensions.isPocketDimension(this.level)) { + this.awardStat(ModStats.DEATHS_IN_POCKETS); + } + this.awardStat(ModStats.TIMES_SENT_TO_LIMBO); + TeleportUtil.teleportRandom(this, ModDimensions.LIMBO_DIMENSION, 512); + //noinspection ConstantConditions + LimboEntranceSource.ofDamageSource(source).broadcast((Player) (Object) this, this.getServer()); + } + } + + @Inject(method = "setRespawnPosition", at = @At("TAIL")) + public void onSpawnPointSet(ResourceKey dimension, BlockPos pos, float angle, boolean spawnPointSet, boolean bl, CallbackInfo ci) { + if (ModDimensions.isPocketDimension(dimension)) { + ModCriteria.POCKET_SPAWN_POINT_SET.trigger((ServerPlayer) (Object) this); + } + } + + +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerInteractionManagerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..66908d17 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerPlayerInteractionManagerMixin.java @@ -0,0 +1,27 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.level.ServerPlayerGameMode; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.phys.BlockHitResult; +import org.dimdev.dimdoors.api.event.UseItemOnBlockCallback; +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(ServerPlayerGameMode.class) +public class ServerPlayerInteractionManagerMixin { + + @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/level/ServerPlayerGameMode;isCreative()Z", ordinal = 0), method = "useItemOn", cancellable = true) + public void useItemOnBlock(ServerPlayer serverPlayer, Level level, ItemStack itemStack, InteractionHand interactionHand, BlockHitResult blockHitResult, CallbackInfoReturnable cir) { + InteractionResult result = UseItemOnBlockCallback.EVENT.invoker().useItemOnBlock(serverPlayer, level, interactionHand, blockHitResult); + if (result != InteractionResult.PASS) { + cir.setReturnValue(result); + cir.cancel(); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/ServerWorldMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerWorldMixin.java new file mode 100644 index 00000000..2ea093f1 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/ServerWorldMixin.java @@ -0,0 +1,19 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.server.level.ServerLevel; +import org.dimdev.dimdoors.world.decay.LimboDecay; +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.function.BooleanSupplier; + +@Mixin(ServerLevel.class) +public abstract class ServerWorldMixin { + + @Inject(method = "tick(Ljava/util/function/BooleanSupplier;)V", at = @At(target = "Lnet/minecraft/server/level/ServerLevel;fluidTicks:Lnet/minecraft/world/ticks/LevelTicks;", value = "FIELD", ordinal = 0, shift = At.Shift.AFTER)) + public void afterScheduledTick(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { + LimboDecay.tick((ServerLevel) (Object) this); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/TrapDoorMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/TrapDoorMixin.java new file mode 100644 index 00000000..98897a40 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/TrapDoorMixin.java @@ -0,0 +1,32 @@ +package org.dimdev.dimdoors.mixin; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.level.block.TrapDoorBlock; +import net.minecraft.world.level.block.state.properties.BlockSetType; +import org.dimdev.dimdoors.block.DoorSoundProvider; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(TrapDoorBlock.class) +public class TrapDoorMixin implements DoorSoundProvider { + + @Shadow + @Final + private BlockSetType type; + + @Override + public SoundEvent getOpenSound() { + return this.type.doorOpen(); + } + + @Override + public SoundEvent getCloseSound() { + return this.type.doorClose(); + } + + @Override + public BlockSetType getSetType() { + return this.type; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java new file mode 100644 index 00000000..2251fa71 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/WorldMixin.java @@ -0,0 +1,86 @@ +package org.dimdev.dimdoors.mixin; + +import com.mojang.datafixers.util.Pair; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import org.dimdev.dimdoors.api.block.CustomBreakBlock; +import org.jetbrains.annotations.Nullable; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.util.function.Consumer; + +@Mixin(Level.class) +public abstract class WorldMixin { + + /* + I thought about redirecting the entire break method to be handled by the block itself, + but I am not quite sure what that would mean for compatibility with other mixins, + since then a large part of the method would need to be canceled. This is rather hacky, but it should fulfill the purpose best + ~CreepyCre + */ + @Redirect(method = "destroyBlock", + at = @At(value = "INVOKE_ASSIGN", + target = "Lnet/minecraft/world/level/Level;getFluidState(Lnet/minecraft/core/BlockPos;)Lnet/minecraft/world/level/material/FluidState;", + ordinal = 0)) + private FluidState replaceFluidStateWithCustomHackyFluidState(FluidState original, BlockPos pos, boolean drop, @Nullable Entity breakingEntity, int maxUpdateDepth) { //TODO: Fix + Level world = (Level) (Object) this; + BlockState blockState = world.getBlockState(pos); + Block block = blockState.getBlock(); + if (!(block instanceof CustomBreakBlock)) { + return original; + } + InteractionResultHolder>> result = ((CustomBreakBlock) block).customBreakBlock(world, pos, blockState, breakingEntity); + if (!result.getResult().consumesAction()) { + return original; + } + Pair> pair = result.getObject(); + return new CustomBreakBlock.HackyFluidState(pair.getFirst(), pair.getSecond()); + } + + @Inject(method = "destroyBlock", + locals = LocalCapture.CAPTURE_FAILHARD, + at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/Level;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;II)Z", + ordinal = 0, + shift = At.Shift.AFTER)) + private void applyBlockEntityModification(BlockPos pos, boolean drop, Entity breakingEntity, int maxUpdateDepth, CallbackInfoReturnable cir, BlockState blockState, FluidState fluidState) { + if (!(fluidState instanceof CustomBreakBlock.HackyFluidState)) { + return; + } + Consumer blockEntityConsumer = ((CustomBreakBlock.HackyFluidState) fluidState).getBlockEntityConsumer(); + if (blockEntityConsumer == null) { + return; + } + BlockEntity blockEntity = ((Level) (Object) this).getBlockEntity(pos); + if (blockEntity != null) { + blockEntityConsumer.accept(blockEntity); + } + } + + /* + This is where I'd inject if it turns out the method used above does actually have an issue + */ +// @Inject(method = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;I)Z", +// cancellable = true, +// at = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/world/WorldAccess;syncWorldEvent(ILnet/minecraft/util/math/BlockPos;I)V", +// ordinal = 0, +// shift = At.Shift.BY, +// by = 2 +// ) +// ) + + +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ChunkGeneratorAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ChunkGeneratorAccessor.java new file mode 100644 index 00000000..68a453d3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ChunkGeneratorAccessor.java @@ -0,0 +1,8 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.world.level.chunk.ChunkGenerator; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ChunkGenerator.class) +public interface ChunkGeneratorAccessor { +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/CraftingInventoryAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/CraftingInventoryAccessor.java new file mode 100644 index 00000000..000becfb --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/CraftingInventoryAccessor.java @@ -0,0 +1,15 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.core.NonNullList; +import net.minecraft.world.inventory.CraftingContainer; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(CraftingContainer.class) +public interface CraftingInventoryAccessor { + @Mutable + @Accessor("items") + void setInventory(NonNullList inventory); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DefaultParticleTypeAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DefaultParticleTypeAccessor.java new file mode 100644 index 00000000..99d13926 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DefaultParticleTypeAccessor.java @@ -0,0 +1,13 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.core.particles.SimpleParticleType; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(SimpleParticleType.class) +public interface DefaultParticleTypeAccessor { + @Invoker("") + static SimpleParticleType createDefaultParticleType(boolean alwaysShow) { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DirectionAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DirectionAccessor.java new file mode 100644 index 00000000..7a065a96 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/DirectionAccessor.java @@ -0,0 +1,13 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.core.Direction; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Direction.class) +public interface DirectionAccessor { + @Accessor("BY_2D_DATA") + static Direction[] getHorizontal() { + throw new AssertionError(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/EntityAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/EntityAccessor.java new file mode 100644 index 00000000..ab8f23ef --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/EntityAccessor.java @@ -0,0 +1,11 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Entity.class) +public interface EntityAccessor { + @Accessor + void setRemovalReason(Entity.RemovalReason removalReason); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/GenerationSettingsAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/GenerationSettingsAccessor.java new file mode 100644 index 00000000..1d630f5d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/GenerationSettingsAccessor.java @@ -0,0 +1,17 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.core.HolderSet; +import net.minecraft.world.level.biome.BiomeGenerationSettings; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(BiomeGenerationSettings.class) +public interface GenerationSettingsAccessor { + @Mutable + @Accessor + void setFeatures(List> features); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ListTagAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ListTagAccessor.java new file mode 100644 index 00000000..7eb35de9 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/ListTagAccessor.java @@ -0,0 +1,16 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; + +@Mixin(ListTag.class) +public interface ListTagAccessor { + @Invoker("") + static ListTag createListTag(List list, byte type) { + throw new UnsupportedOperationException(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RecipesProviderAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RecipesProviderAccessor.java new file mode 100644 index 00000000..579c4eb6 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RecipesProviderAccessor.java @@ -0,0 +1,23 @@ +package org.dimdev.dimdoors.mixin.accessor;//TODO: Disabled unsure if needed. +//package org.dimdev.dimdoors.mixin.accessor; +// +//import com.google.gson.JsonObject; +//import net.minecraft.data.DataWriter; +//import net.minecraft.data.server.recipe.RecipeProvider; +//import org.spongepowered.asm.mixin.Mixin; +//import org.spongepowered.asm.mixin.gen.Invoker; +// +//import java.nio.file.Path; +// +//@Mixin(RecipeProvider.class) +//public interface RecipesProviderAccessor { +// @Invoker +// static void callSaveRecipe(DataWriter cache, JsonObject json, Path path) { +// throw new UnsupportedOperationException(); +// } +// +// @Invoker +// static void callSaveRecipeAdvancement(DataWriter cache, JsonObject json, Path path) { +// throw new UnsupportedOperationException(); +// } +//} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RedstoneWireBlockAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RedstoneWireBlockAccessor.java new file mode 100644 index 00000000..6c922fbc --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/accessor/RedstoneWireBlockAccessor.java @@ -0,0 +1,15 @@ +package org.dimdev.dimdoors.mixin.accessor; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.RedStoneWireBlock; +import net.minecraft.world.level.block.state.properties.RedstoneSide; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(RedStoneWireBlock.class) +public interface RedstoneWireBlockAccessor { + @Invoker + RedstoneSide invokeGetConnectingSide(BlockGetter blockGetter, BlockPos blockPos, Direction direction); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/BackgroundRendererMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/BackgroundRendererMixin.java new file mode 100644 index 00000000..1267eca7 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/BackgroundRendererMixin.java @@ -0,0 +1,22 @@ +package org.dimdev.dimdoors.mixin.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.renderer.FogRenderer; +import org.spongepowered.asm.mixin.Mixin; + +@Environment(EnvType.CLIENT) +@Mixin(FogRenderer.class) +public class BackgroundRendererMixin { +// @ModifyVariable( +// method = "render", +// at = @At(value = "STORE", ordinal = 0), +// ordinal = 0 +// ) +// private static double modifyVoidColor(double scale) { +// if(ModDimensions.isPrivatePocketDimension(MinecraftClient.getInstance().world)) { +// scale = 1.0; +// } +// return scale; +// } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayNetworkHandlerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..fbc46387 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayNetworkHandlerMixin.java @@ -0,0 +1,20 @@ +package org.dimdev.dimdoors.mixin.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import org.dimdev.dimdoors.network.client.ClientPacketListener; +import org.spongepowered.asm.mixin.Mixin; + +@Environment(EnvType.CLIENT) +@Mixin(ClientPacketListener.class) +public class ClientPlayNetworkHandlerMixin { +// @Shadow +// private ClientWorld world; +// +// @Inject(method = "onPlayerPositionLook", at = @At("RETURN"), locals = LocalCapture.CAPTURE_FAILHARD) +// public void onPlayerLookPositionPacket(PlayerPositionLookS2CPacket packet, CallbackInfo ci, PlayerEntity playerEntity, Vec3d vec3d, boolean bl, boolean bl2, boolean bl3, double f, double g, double j, double k, double n, double o, float p, float q) { +// if (ModDimensions.isLimboDimension(this.world)) { +// this.world.addParticle(ModParticleTypes.MONOLITH, playerEntity.getX(), playerEntity.getY(), playerEntity.getZ(), 0.0D, 0.0D, 0.0D); +// } +// } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayerInteractionManagerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..905e318d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,42 @@ +package org.dimdev.dimdoors.mixin.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.MultiPlayerGameMode; +import net.minecraft.client.multiplayer.prediction.PredictiveAction; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.phys.BlockHitResult; +import org.dimdev.dimdoors.api.event.UseItemOnBlockCallback; +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; + +@Mixin(MultiPlayerGameMode.class) +public abstract class ClientPlayerInteractionManagerMixin { + + @Shadow + @Final + private Minecraft minecraft; + + @Shadow + protected abstract void startPrediction(ClientLevel world, PredictiveAction packetCreator); + + @Inject(method = "useItemOn", cancellable = true, at = @At(value = "NEW", target = "org/apache/commons/lang3/mutable/MutableObject", remap = false)) + public void useItemOnBlock(LocalPlayer player, InteractionHand hand, BlockHitResult hitResult, CallbackInfoReturnable info) { + InteractionResult result = UseItemOnBlockCallback.EVENT.invoker().useItemOnBlock(player, minecraft.level, hand, hitResult); + if (result == InteractionResult.PASS) { + return; + } + info.setReturnValue(result); + info.cancel(); + if (result == InteractionResult.SUCCESS) { + this.startPrediction(this.minecraft.level, sequence -> new ServerboundUseItemOnPacket(hand, hitResult, sequence)); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/ExtendedClientPlayNetworkHandlerMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ExtendedClientPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..4dda0bf1 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/ExtendedClientPlayNetworkHandlerMixin.java @@ -0,0 +1,30 @@ +package org.dimdev.dimdoors.mixin.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import org.dimdev.dimdoors.network.client.ClientPacketHandler; +import org.dimdev.dimdoors.network.client.ExtendedClientPlayNetworkHandler; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Environment(EnvType.CLIENT) +@Mixin(ClientPacketListener.class) +public class ExtendedClientPlayNetworkHandlerMixin implements ExtendedClientPlayNetworkHandler { + @Final @Shadow + private Minecraft minecraft; + private final ClientPacketHandler dimdoors_PacketHandler = new ClientPacketHandler((ClientPacketListener) (Object) this); + + @Unique + public ClientPacketHandler getDimDoorsPacketHandler() { + return dimdoors_PacketHandler; + } + + @Unique + public Minecraft dimdoorsGetClient() { + return minecraft; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/GameRendererMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/GameRendererMixin.java new file mode 100644 index 00000000..7b6c7a15 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/GameRendererMixin.java @@ -0,0 +1,36 @@ +package org.dimdev.dimdoors.mixin.client; + +import java.io.IOException; +import java.util.List; +import java.util.function.Consumer; + +import com.mojang.datafixers.util.Pair; +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.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import net.minecraft.client.gl.ShaderProgram; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.resource.ResourceFactory; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.client.ModShaders; + +@Environment(EnvType.CLIENT) +@Mixin(GameRenderer.class) +public abstract class GameRendererMixin { + @Shadow + protected abstract ShaderProgram preloadProgram(ResourceFactory arg, String string, VertexFormat vertexFormat) throws IOException; + + @Inject(method = "loadPrograms", at = @At(value = "INVOKE", shift = At.Shift.AFTER, ordinal = 1, target = "java/util/List.add(Ljava/lang/Object;)Z"), locals = LocalCapture.CAPTURE_FAILSOFT) + public void onReload(ResourceFactory manager, CallbackInfo ci, List list, List list2) throws IOException { + list2.add(Pair.of(new ShaderProgram(manager, "dimensional_portal", VertexFormats.POSITION), (Consumer) ModShaders::setDimensionalPortal)); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/InGameHudMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/InGameHudMixin.java new file mode 100644 index 00000000..74afe702 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/InGameHudMixin.java @@ -0,0 +1,36 @@ +package org.dimdev.dimdoors.mixin.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.gui.Gui; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Environment(EnvType.CLIENT) +@Mixin(Gui.class) +public abstract class InGameHudMixin{ + private int frame = 0; + private static final float OVERLAY_OPACITY_ADJUSTEMENT = 1.5F; + @Shadow + private int screenHeight; + @Shadow + private int screenWidth; + + @Shadow + protected abstract Player getCameraPlayer(); + + // @Inject(at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;setShader(Ljava/util/function/Supplier;)V"), method = "renderVignetteOverlay(Lnet/minecraft/entity/Entity;)V") +// public void renderVignetteOverlay(Entity entity, CallbackInfo info) { +// if (ModDimensions.isLimboDimension(entity.world)) { +// RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); +// } +// } + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + public void renderOverlayMixin(PoseStack matrices, float tickDelta, CallbackInfo ci) { + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/PostProcessShaderMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/PostProcessShaderMixin.java new file mode 100644 index 00000000..d0f74b33 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/PostProcessShaderMixin.java @@ -0,0 +1,28 @@ +package org.dimdev.dimdoors.mixin.client; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EffectInstance; +import net.minecraft.client.renderer.PostPass; +import net.minecraft.world.entity.player.Player; +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.CallbackInfo; + +@Mixin(PostPass.class) +public class PostProcessShaderMixin { + + @Shadow @Final private EffectInstance effect; + + @Inject(method = "process(F)V", at = @At("HEAD"), cancellable = true) + public void render(float time, CallbackInfo cir) { + effect.safeGetUniform("GameTime").set(RenderSystem.getShaderGameTime()); + } + + private Player getCameraPlayer() { + return !(Minecraft.getInstance().getCameraEntity() instanceof Player player) ? null : player; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/WorldRendererMixin.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/WorldRendererMixin.java new file mode 100644 index 00000000..c0a72249 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/WorldRendererMixin.java @@ -0,0 +1,88 @@ +package org.dimdev.dimdoors.mixin.client; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.SheetedDecalTextureGenerator; +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Camera; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.*; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.core.BlockPos; +import net.minecraft.world.phys.Vec3; +import org.dimdev.dimdoors.client.CustomBreakBlockHandler; +import org.joml.Matrix4f; +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.CallbackInfo; + +import java.util.Map; + +@Mixin(LevelRenderer.class) +@Environment(EnvType.CLIENT) +public abstract class WorldRendererMixin { + + @Shadow + private ClientLevel level; + + @Shadow + private Minecraft minecraft; + + @Shadow + @Final + private RenderBuffers renderBuffers; + + @Shadow + private int ticks; + + @Inject(method = "renderLevel", + at = @At(value = "FIELD", target = "Lnet/minecraft/client/renderer/LevelRenderer;destructionProgress:Lit/unimi/dsi/fastutil/longs/Long2ObjectMap;", ordinal = 1)) // bytecode order is flipped from java code order, notice the ordinal + public void renderCustomBreakBlockAnimation(PoseStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightTexture lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) { + Vec3 vec3d = camera.getPosition(); + double d = vec3d.x(); + double e = vec3d.y(); + double f = vec3d.z(); + + Map breakBlocks = CustomBreakBlockHandler.getCustomBreakBlockMap(this.ticks); + + // stolen from WorldRenderer#render + for (Map.Entry entry : breakBlocks.entrySet()) { + BlockPos pos = entry.getKey(); + double h = (double) pos.getX() - d; + double x = (double) pos.getY() - e; + double y = (double) pos.getZ() - f; + if (!(h * h + x * x + y * y > 1024.0D)) { + int stage = entry.getValue().getStage(); + matrices.pushPose(); + matrices.translate((double) pos.getX() - d, (double) pos.getY() - e, (double) pos.getZ() - f); + PoseStack.Pose entry3 = matrices.last(); + VertexConsumer vertexConsumer2 = new SheetedDecalTextureGenerator(this.renderBuffers.crumblingBufferSource().getBuffer((RenderType) ModelBakery.DESTROY_TYPES.get(stage)), entry3.pose(), entry3.normal(), 1.0F); + this.minecraft.getBlockRenderer().renderBreakingTexture(this.level.getBlockState(pos), pos, this.level, matrices, vertexConsumer2); + matrices.popPose(); + } + } + } + +// TODO:Fix or find out alternative. +// @ModifyConstant( +// method = "renderSky(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/util/math/Matrix4f;FLjava/lang/Runnable;)V", +// constant = @Constant(doubleValue = 0.0, ordinal = 0), +// slice = @Slice( +// from = @At( +// value = "INVOKE", +// target = "Lnet/minecraft/client/world/ClientWorld$Properties;getSkyDarknessHeight(Lnet/minecraft/world/HeightLimitView;)D" +// ) +// ) +// ) +// private double modifyVoidBackgroundCondition(double zero) { +// if(ModDimensions.isPrivatePocketDimension(world)) { +// zero = Double.NEGATIVE_INFINITY; +// } +// return zero; +// } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/mixin/client/accessor/WorldRendererAccessor.java b/common/src/main/java/org/dimdev/dimdoors/mixin/client/accessor/WorldRendererAccessor.java new file mode 100644 index 00000000..39cf2234 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/mixin/client/accessor/WorldRendererAccessor.java @@ -0,0 +1,11 @@ +package org.dimdev.dimdoors.mixin.client.accessor; + +import net.minecraft.client.renderer.LevelRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(LevelRenderer.class) +public interface WorldRendererAccessor { + @Accessor + int getTicks(); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/ExtendedServerPlayNetworkHandler.java b/common/src/main/java/org/dimdev/dimdoors/network/ExtendedServerPlayNetworkHandler.java new file mode 100644 index 00000000..544396e3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/ExtendedServerPlayNetworkHandler.java @@ -0,0 +1,14 @@ +package org.dimdev.dimdoors.network; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayNetworkHandler; + +public interface ExtendedServerPlayNetworkHandler { + static ExtendedServerPlayNetworkHandler get(ServerPlayNetworkHandler networkHandler) { + return (ExtendedServerPlayNetworkHandler) networkHandler; + } + + ServerPacketHandler getDimDoorsPacketHandler(); + + MinecraftServer dimdoorsGetServer(); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketHandler.java b/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketHandler.java new file mode 100644 index 00000000..88158d4e --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketHandler.java @@ -0,0 +1,166 @@ +package org.dimdev.dimdoors.network; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Supplier; + +import dev.architectury.networking.NetworkManager; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import net.minecraft.world.level.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.registry.RegistryKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +import org.dimdev.dimdoors.api.item.ExtendedItem; +import org.dimdev.dimdoors.network.packet.c2s.HitBlockWithItemC2SPacket; +import org.dimdev.dimdoors.network.packet.c2s.NetworkHandlerInitializedC2SPacket; +import org.dimdev.dimdoors.network.packet.s2c.PlayerInventorySlotUpdateS2CPacket; +import org.dimdev.dimdoors.network.packet.s2c.SyncPocketAddonsS2CPacket; +import org.dimdev.dimdoors.world.ModDimensions; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; +import org.dimdev.dimdoors.world.pocket.PocketDirectory; +import org.dimdev.dimdoors.world.pocket.type.Pocket; +import org.dimdev.dimdoors.world.pocket.type.addon.AutoSyncedAddon; + +// each client has their own corresponding ServerPacketHandler, so feel free to add client specific data in here +public class ServerPacketHandler implements ServerPacketListener { + private static final Logger LOGGER = LogManager.getLogger(); + + private final net.minecraft.network.protocol.game.ServerPacketListener networkHandler; + private final Set registeredChannels = new HashSet<>(); + private boolean initialized = false; + + private ResourceKey lastSyncedPocketWorld; + private int lastSyncedPocketId = Integer.MIN_VALUE; + private boolean pocketSyncDirty = true; + + public void init() { + if (initialized) throw new RuntimeException("ServerPacketHandler has already been initialized."); + initialized = true; + registerReceiver(NetworkHandlerInitializedC2SPacket.ID, NetworkHandlerInitializedC2SPacket::new); + registerReceiver(HitBlockWithItemC2SPacket.ID, HitBlockWithItemC2SPacket::new); + } + + public static ServerPacketHandler get(ServerPlayer player) { + return get(player.connection); + } + + public static ServerPacketHandler get(ServerGamePacketListenerImpl networkHandler) { + return ((ExtendedServerPlayNetworkHandler) networkHandler).getDimDoorsPacketHandler(); + } + + public static boolean sendPacket(ServerPlayer player, SimplePacket packet) { + try { + NetworkManager.sendToPlayer(player, packet.channelId(), packet.write(PacketByteBufs.create())); + return true; + } catch (IOException e) { + LOGGER.error(e); + return false; + } + } + + public boolean sendPacket(SimplePacket packet) { + return sendPacket(getPlayer(), packet); + } + + public ServerPacketHandler(net.minecraft.network.protocol.game.ServerPacketListener networkHandler) { + this.networkHandler = networkHandler; + } + + private void registerReceiver(ResourceLocation channelName, Supplier> supplier) { + NetworkManager.registerReceiver(NetworkManager.Side.S2C, channelName, new NetworkManager.NetworkReceiver() { + @Override + public void receive(FriendlyByteBuf buf, NetworkManager.PacketContext context) { + context.getPlayer() + } + }); + + ServerPlayNetworking.registerReceiver(networkHandler, channelName, + (MinecraftServer server, ServerPlayerEntity player, ServerPlayNetworkHandler handler, PacketByteBuf buf, PacketSender responseSender) -> { + try { + supplier.get().read(buf).apply(this); + } catch (IOException e) { + LOGGER.error(e); + } + }); + registeredChannels.add(channelName); + } + + private void unregisterReceiver(Identifier channelName) { + ServerPlayNetworking.unregisterReceiver(networkHandler, channelName); + registeredChannels.remove(channelName); + } + + public void unregister() { + new HashSet<>(registeredChannels).forEach(this::unregisterReceiver); + } + + public MinecraftServer getServer() { + return ((ExtendedServerPlayNetworkHandler) networkHandler).dimdoorsGetServer(); + } + + public ServerPlayerEntity getPlayer() { + return networkHandler.player; + } + + // TODO: attach this to some event to detect other kinds teleportation + public void syncPocketAddonsIfNeeded(World world, BlockPos pos) { + if (!ModDimensions.isPocketDimension(world)) return; + PocketDirectory directory = DimensionalRegistry.getPocketDirectory(world.getRegistryKey()); + Pocket pocket = directory.getPocketAt(pos); + if (pocket == null) return; + if ((pocketSyncDirty || pocket.getId() != lastSyncedPocketId || !world.getRegistryKey().getValue().equals(lastSyncedPocketWorld.getValue()))) { + pocketSyncDirty = false; + lastSyncedPocketId = pocket.getId(); + lastSyncedPocketWorld = world.getRegistryKey(); + sendPacket(getPlayer(), new SyncPocketAddonsS2CPacket(world.getRegistryKey(), directory.getGridSize(), pocket.getId(), pocket.getRange(), pocket.getAddonsInstanceOf(AutoSyncedAddon.class))); + } + } + + public void markPocketSyncDirty(int id) { + if (lastSyncedPocketId == id) pocketSyncDirty = true; + } + + public void sync(ItemStack stack, Hand hand) { + if (hand == Hand.OFF_HAND) { + sendPacket(new PlayerInventorySlotUpdateS2CPacket(45, stack)); + } else { + sendPacket(new PlayerInventorySlotUpdateS2CPacket(getPlayer().getInventory().selectedSlot, stack)); + } + } + + @Override + public void onAttackBlock(HitBlockWithItemC2SPacket packet) { + getServer().execute(() -> { + Item item = getPlayer().getStackInHand(packet.getHand()).getItem(); + if (item instanceof ExtendedItem) { + ((ExtendedItem) item).onAttackBlock(getPlayer().world, getPlayer(), packet.getHand(), packet.getPos(), packet.getDirection()); + } + }); + } + + @Override + public void onNetworkHandlerInitialized(NetworkHandlerInitializedC2SPacket packet) { + syncPocketAddonsIfNeeded(getPlayer().world, getPlayer().getBlockPos()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketListener.java b/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketListener.java new file mode 100644 index 00000000..49646f65 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/ServerPacketListener.java @@ -0,0 +1,10 @@ +package org.dimdev.dimdoors.network; + +import org.dimdev.dimdoors.network.packet.c2s.HitBlockWithItemC2SPacket; +import org.dimdev.dimdoors.network.packet.c2s.NetworkHandlerInitializedC2SPacket; + +public interface ServerPacketListener { + void onAttackBlock(HitBlockWithItemC2SPacket packet); + + void onNetworkHandlerInitialized(NetworkHandlerInitializedC2SPacket packet); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/SimplePacket.java b/common/src/main/java/org/dimdev/dimdoors/network/SimplePacket.java new file mode 100644 index 00000000..d20dd032 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/SimplePacket.java @@ -0,0 +1,16 @@ +package org.dimdev.dimdoors.network; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; + +import java.io.IOException; + +public interface SimplePacket { + SimplePacket read(FriendlyByteBuf buf) throws IOException; + + FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException; + + void apply(T listener); + + ResourceLocation channelId(); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketHandler.java b/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketHandler.java new file mode 100644 index 00000000..5810fc86 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketHandler.java @@ -0,0 +1,156 @@ +package org.dimdev.dimdoors.network.client; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import dev.architectury.networking.NetworkManager; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.registry.RegistryKey; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; + +import org.dimdev.dimdoors.client.CustomBreakBlockHandler; +import org.dimdev.dimdoors.entity.MonolithEntity; +import org.dimdev.dimdoors.mixin.client.accessor.WorldRendererAccessor; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.packet.c2s.NetworkHandlerInitializedC2SPacket; +import org.dimdev.dimdoors.network.packet.s2c.MonolithAggroParticlesPacket; +import org.dimdev.dimdoors.network.packet.s2c.MonolithTeleportParticlesPacket; +import org.dimdev.dimdoors.network.packet.s2c.PlayerInventorySlotUpdateS2CPacket; +import org.dimdev.dimdoors.network.packet.s2c.RenderBreakBlockS2CPacket; +import org.dimdev.dimdoors.network.packet.s2c.SyncPocketAddonsS2CPacket; +import org.dimdev.dimdoors.particle.client.MonolithParticle; +import org.dimdev.dimdoors.world.pocket.type.addon.AutoSyncedAddon; + +@Environment(EnvType.CLIENT) +public class ClientPacketHandler implements ClientPacketListener { + private static final Logger LOGGER = LogManager.getLogger(); + + private final net.minecraft.client.multiplayer.ClientPacketListener networkHandler; + private boolean initialized = false; + + private final Set registeredChannels = new HashSet<>(); + + private ResourceKey pocketWorld; + private int gridSize = 1; + private int pocketId = Integer.MIN_VALUE; + private int pocketRange = 1; + private List addons = new ArrayList<>(); + + public void init() { + if (initialized) throw new RuntimeException("ClientPacketHandler has already been initialized."); + initialized = true; + registerReceiver(PlayerInventorySlotUpdateS2CPacket.ID, PlayerInventorySlotUpdateS2CPacket::new); + registerReceiver(SyncPocketAddonsS2CPacket.ID, SyncPocketAddonsS2CPacket::new); + registerReceiver(MonolithAggroParticlesPacket.ID, MonolithAggroParticlesPacket::new); + registerReceiver(MonolithTeleportParticlesPacket.ID, MonolithTeleportParticlesPacket::new); + registerReceiver(RenderBreakBlockS2CPacket.ID, RenderBreakBlockS2CPacket::new); + + sendPacket(new NetworkHandlerInitializedC2SPacket()); + } + + public static boolean sendPacket(SimplePacket packet) { + try { + NetworkManager.send.send(packet.channelId(), packet.write(PacketByteBufs.create())); + return true; + } catch (IOException e) { + LOGGER.error(e); + return false; + } + } + + public ClientPacketHandler(net.minecraft.client.multiplayer.ClientPacketListener networkHandler) { + this.networkHandler = networkHandler; + } + + private void registerReceiver(Identifier channelName, Supplier> supplier) { + ClientPlayNetworking.registerReceiver(channelName, (client, handler, buf, responseSender) -> { + try { + supplier.get().read(buf).apply(this); + } catch (IOException e) { + LOGGER.error(e); + } + }); + registeredChannels.add(channelName); + } + + private void unregisterReceiver(Identifier channelName) { + ClientPlayNetworking.unregisterReceiver(channelName); + registeredChannels.remove(channelName); + } + + public void unregister() { + new HashSet<>(registeredChannels).forEach(this::unregisterReceiver); + } + + public RegistryKey getPocketWorld() { + return pocketWorld; + } + + public int getGridSize() { + return gridSize; + } + + public int getPocketId() { + return pocketId; + } + + public int getPocketRange() { + return pocketRange; + } + + public List getAddons() { + return addons; + } + + @Override + public void onPlayerInventorySlotUpdate(PlayerInventorySlotUpdateS2CPacket packet) { + MinecraftClient.getInstance().execute(() -> { + if (MinecraftClient.getInstance().player != null) { + MinecraftClient.getInstance().player.getInventory().setStack(packet.getSlot(), packet.getStack()); + } + }); + } + + @Override + public void onSyncPocketAddons(SyncPocketAddonsS2CPacket packet) { + this.pocketWorld = packet.getWorld(); + this.gridSize = packet.getGridSize(); + this.pocketId = packet.getPocketId(); + this.pocketRange = packet.getPocketRange(); + this.addons = packet.getAddons(); + } + + @Override + public void onMonolithAggroParticles(MonolithAggroParticlesPacket packet) { + MinecraftClient.getInstance().execute(() -> MonolithEntity.spawnParticles(packet.getAggro())); + } + + @Override + public void onMonolithTeleportParticles(MonolithTeleportParticlesPacket packet) { + MinecraftClient client = MinecraftClient.getInstance(); + //noinspection ConstantConditions + client.execute(() -> client.particleManager.addParticle(new MonolithParticle(client.world, client.player.getX(), client.player.getY(), client.player.getZ()))); + } + + @Override + public void onRenderBreakBlock(RenderBreakBlockS2CPacket packet) { + CustomBreakBlockHandler.customBreakBlock(packet.getPos(), packet.getStage(), ((WorldRendererAccessor) MinecraftClient.getInstance().worldRenderer).getTicks()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketListener.java b/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketListener.java new file mode 100644 index 00000000..84b665fd --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/client/ClientPacketListener.java @@ -0,0 +1,19 @@ +package org.dimdev.dimdoors.network.client; + +import org.dimdev.dimdoors.network.packet.s2c.MonolithAggroParticlesPacket; +import org.dimdev.dimdoors.network.packet.s2c.MonolithTeleportParticlesPacket; +import org.dimdev.dimdoors.network.packet.s2c.PlayerInventorySlotUpdateS2CPacket; +import org.dimdev.dimdoors.network.packet.s2c.RenderBreakBlockS2CPacket; +import org.dimdev.dimdoors.network.packet.s2c.SyncPocketAddonsS2CPacket; + +public interface ClientPacketListener { + void onPlayerInventorySlotUpdate(PlayerInventorySlotUpdateS2CPacket packet); + + void onSyncPocketAddons(SyncPocketAddonsS2CPacket packet); + + void onMonolithAggroParticles(MonolithAggroParticlesPacket packet); + + void onMonolithTeleportParticles(MonolithTeleportParticlesPacket packet); + + void onRenderBreakBlock(RenderBreakBlockS2CPacket packet); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/client/ExtendedClientPlayNetworkHandler.java b/common/src/main/java/org/dimdev/dimdoors/network/client/ExtendedClientPlayNetworkHandler.java new file mode 100644 index 00000000..114c7049 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/client/ExtendedClientPlayNetworkHandler.java @@ -0,0 +1,12 @@ +package org.dimdev.dimdoors.network.client; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; + +@Environment(EnvType.CLIENT) +public interface ExtendedClientPlayNetworkHandler { + ClientPacketHandler getDimDoorsPacketHandler(); + + Minecraft dimdoorsGetClient(); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/HitBlockWithItemC2SPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/HitBlockWithItemC2SPacket.java new file mode 100644 index 00000000..94b4ebf2 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/HitBlockWithItemC2SPacket.java @@ -0,0 +1,74 @@ +package org.dimdev.dimdoors.network.packet.c2s; + +import java.io.IOException; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Hand; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.ServerPacketListener; +import org.dimdev.dimdoors.network.SimplePacket; + +public class HitBlockWithItemC2SPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("hit_block_with_item"); + + private Hand hand; + private BlockPos pos; + private Direction direction; + + public HitBlockWithItemC2SPacket() { + } + + @Environment(EnvType.CLIENT) + public HitBlockWithItemC2SPacket(Hand hand, BlockPos pos, Direction direction) { + this.hand = hand; + this.pos = pos; + this.direction = direction; + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + hand = buf.readEnumConstant(Hand.class); + pos = buf.readBlockPos(); + direction = buf.readEnumConstant(Direction.class); + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + buf.writeEnumConstant(hand); + buf.writeBlockPos(pos); + buf.writeEnumConstant(direction); + return buf; + } + + @Override + public void apply(ServerPacketListener listener) { + listener.onAttackBlock(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } + + public BlockPos getPos() { + return pos; + } + + public Direction getDirection() { + return direction; + } + + public Hand getHand() { + return hand; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/NetworkHandlerInitializedC2SPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/NetworkHandlerInitializedC2SPacket.java new file mode 100644 index 00000000..334c8742 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/c2s/NetworkHandlerInitializedC2SPacket.java @@ -0,0 +1,33 @@ +package org.dimdev.dimdoors.network.packet.c2s; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.ServerPacketListener; +import org.dimdev.dimdoors.network.SimplePacket; + +import java.io.IOException; + +public class NetworkHandlerInitializedC2SPacket implements SimplePacket { + public static final ResourceLocation ID = DimensionalDoors.id("network_handler_initialized"); + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + return buf; + } + + @Override + public void apply(ServerPacketListener listener) { + listener.onNetworkHandlerInitialized(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithAggroParticlesPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithAggroParticlesPacket.java new file mode 100644 index 00000000..f6fea457 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithAggroParticlesPacket.java @@ -0,0 +1,54 @@ +package org.dimdev.dimdoors.network.packet.s2c; + +import java.io.IOException; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Identifier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.client.ClientPacketListener; + +public class MonolithAggroParticlesPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("monolith_aggro_particles"); + + private int aggro; + + @Environment(EnvType.CLIENT) + public MonolithAggroParticlesPacket() { + } + + public MonolithAggroParticlesPacket(int aggro) { + this.aggro = aggro; + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + return new MonolithAggroParticlesPacket(buf.readVarInt()); + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + buf.writeVarInt(aggro); + return buf; + } + + @Override + public void apply(ClientPacketListener listener) { + listener.onMonolithAggroParticles(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } + + public int getAggro() { + return aggro; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithTeleportParticlesPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithTeleportParticlesPacket.java new file mode 100644 index 00000000..d82f32b9 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/MonolithTeleportParticlesPacket.java @@ -0,0 +1,39 @@ +package org.dimdev.dimdoors.network.packet.s2c; + +import java.io.IOException; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Identifier; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.client.ClientPacketListener; + +public class MonolithTeleportParticlesPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("monolith_tp_particles"); + + public MonolithTeleportParticlesPacket() { + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + return buf; + } + + @Override + public void apply(ClientPacketListener listener) { + listener.onMonolithTeleportParticles(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/PlayerInventorySlotUpdateS2CPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/PlayerInventorySlotUpdateS2CPacket.java new file mode 100644 index 00000000..240bd916 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/PlayerInventorySlotUpdateS2CPacket.java @@ -0,0 +1,65 @@ +package org.dimdev.dimdoors.network.packet.s2c; + +import java.io.IOException; + +import net.minecraft.item.ItemStack; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Identifier; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.client.ClientPacketListener; + +public class PlayerInventorySlotUpdateS2CPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("player_inventory_slot_update"); + + private int slot; + private ItemStack stack; + + @Environment(EnvType.CLIENT) + public PlayerInventorySlotUpdateS2CPacket() { + this.stack = ItemStack.EMPTY; + } + + public PlayerInventorySlotUpdateS2CPacket(int slot, ItemStack stack) { + this.slot = slot; + this.stack = stack; + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + slot = buf.readInt(); + stack = buf.readItemStack(); + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + buf.writeInt(slot); + buf.writeItemStack(stack); + return buf; + } + + @Override + public void apply(ClientPacketListener listener) { + listener.onPlayerInventorySlotUpdate(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } + + public int getSlot() { + return slot; + } + + public ItemStack getStack() { + return stack; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/RenderBreakBlockS2CPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/RenderBreakBlockS2CPacket.java new file mode 100644 index 00000000..2cc77f6d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/RenderBreakBlockS2CPacket.java @@ -0,0 +1,65 @@ +package org.dimdev.dimdoors.network.packet.s2c; + +import java.io.IOException; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.client.ClientPacketListener; + +public class RenderBreakBlockS2CPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("render_break_block"); + + private BlockPos pos; + private int stage; + + @Environment(EnvType.CLIENT) + public RenderBreakBlockS2CPacket() { + + } + + public RenderBreakBlockS2CPacket(BlockPos pos, int stage) { + this.pos = pos; + this.stage = stage; + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + pos = buf.readBlockPos(); + stage = buf.readInt(); + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + buf.writeBlockPos(pos); + buf.writeInt(stage); + return buf; + } + + @Override + public void apply(ClientPacketListener listener) { + listener.onRenderBreakBlock(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } + + public BlockPos getPos() { + return pos; + } + + public int getStage() { + return stage; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/SyncPocketAddonsS2CPacket.java b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/SyncPocketAddonsS2CPacket.java new file mode 100644 index 00000000..71920f6b --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/network/packet/s2c/SyncPocketAddonsS2CPacket.java @@ -0,0 +1,92 @@ +package org.dimdev.dimdoors.network.packet.s2c; + +import java.io.IOException; +import java.util.List; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.network.SimplePacket; +import org.dimdev.dimdoors.network.client.ClientPacketListener; +import org.dimdev.dimdoors.world.pocket.type.addon.AutoSyncedAddon; + +public class SyncPocketAddonsS2CPacket implements SimplePacket { + public static final Identifier ID = DimensionalDoors.id("sync_pocket_addons"); + + private RegistryKey world; + private int gridSize; + private int pocketId; + private int pocketRange; + private List addons; + + @Environment(EnvType.CLIENT) + public SyncPocketAddonsS2CPacket() { + } + + public SyncPocketAddonsS2CPacket(RegistryKey world, int gridSize, int pocketId, int pocketRange, List addons) { + this.world = world; + this.gridSize = gridSize; + this.pocketId = pocketId; + this.pocketRange = pocketRange; + this.addons = addons; + } + + @Override + public SimplePacket read(FriendlyByteBuf buf) throws IOException { + this.world = RegistryKey.of(RegistryKeys.WORLD, buf.readIdentifier()); + this.gridSize = buf.readInt(); + this.pocketId = buf.readInt(); + this.pocketRange = buf.readInt(); + this.addons = AutoSyncedAddon.readAutoSyncedAddonList(buf); + return this; + } + + @Override + public FriendlyByteBuf write(FriendlyByteBuf buf) throws IOException { + buf.writeIdentifier(world.getValue()); + buf.writeInt(gridSize); + buf.writeInt(pocketId); + buf.writeInt(pocketRange); + AutoSyncedAddon.writeAutoSyncedAddonList(buf, addons); + return buf; + } + + @Override + public void apply(ClientPacketListener listener) { + listener.onSyncPocketAddons(this); + } + + @Override + public ResourceLocation channelId() { + return ID; + } + + public int getGridSize() { + return gridSize; + } + + public int getPocketId() { + return pocketId; + } + + public int getPocketRange() { + return pocketRange; + } + + public List getAddons() { + return addons; + } + + public RegistryKey getWorld() { + return world; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/DefaultDungeonDestinations.java b/common/src/main/java/org/dimdev/dimdoors/pockets/DefaultDungeonDestinations.java new file mode 100644 index 00000000..ff27c23c --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/DefaultDungeonDestinations.java @@ -0,0 +1,84 @@ +package org.dimdev.dimdoors.pockets; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; + +import org.dimdev.dimdoors.rift.registry.LinkProperties; +import org.dimdev.dimdoors.rift.targets.PocketEntranceMarker; +import org.dimdev.dimdoors.rift.targets.RandomTarget; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; + +public interface DefaultDungeonDestinations { // TODO: lower weights? + LinkProperties POCKET_LINK_PROPERTIES = LinkProperties.builder() + .groups(new HashSet<>(Arrays.asList(0, 1))) + .linksRemaining(1) + .build(); + + LinkProperties OVERWORLD_LINK_PROPERTIES = LinkProperties.builder() + .groups(new HashSet<>(Arrays.asList(0, 1))) + .entranceWeight(50) + .linksRemaining(1) + .build(); + + static VirtualTarget getDeeperDungeonDestination() { + return RandomTarget.builder() + .acceptedGroups(Collections.singleton(0)) + .coordFactor(1) + .negativeDepthFactor(10000) + .positiveDepthFactor(80) + .weightMaximum(100) + .noLink(false) + .noLinkBack(false) + .newRiftWeight(1) + .build(); + } + + static VirtualTarget getShallowerDungeonDestination() { + return RandomTarget.builder() + .acceptedGroups(Collections.singleton(0)) + .coordFactor(1) + .negativeDepthFactor(160) + .positiveDepthFactor(10000) + .weightMaximum(100) + .newRiftWeight(1) + .build(); + } + + static VirtualTarget getOverworldDestination() { + return RandomTarget.builder() + .acceptedGroups(Collections.singleton(0)) + .coordFactor(1) + .negativeDepthFactor(0.00000000001) // The division result is cast to an int, so Double.MIN_VALUE would cause an overflow + .positiveDepthFactor(Double.POSITIVE_INFINITY) + .weightMaximum(100) + .newRiftWeight(1) + .build(); + } + + static VirtualTarget getTwoWayPocketEntrance() { + return PocketEntranceMarker.builder() + .weight(1) + .ifDestination(new PocketEntranceMarker()) + .otherwiseDestination(RandomTarget.builder() + .acceptedGroups(Collections.singleton(0)) + .coordFactor(1) + .negativeDepthFactor(80) + .positiveDepthFactor(10000) + .weightMaximum(100) + .newRiftWeight(1) + .build()) + .build(); + } + + static VirtualTarget getGateway() { + return RandomTarget.builder() + .acceptedGroups(Collections.singleton(0)) + .coordFactor(1) + .negativeDepthFactor(Double.POSITIVE_INFINITY) + .positiveDepthFactor(160) + .weightMaximum(300) // Link further away + .newRiftWeight(1) + .build(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerationContext.java b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerationContext.java new file mode 100644 index 00000000..994ab411 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerationContext.java @@ -0,0 +1,19 @@ +package org.dimdev.dimdoors.pockets; + +import java.util.Map; + +import net.minecraft.server.world.ServerWorld; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.rift.registry.LinkProperties; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; +import org.dimdev.dimdoors.world.pocket.VirtualLocation; + +public record PocketGenerationContext(ServerWorld world, VirtualLocation sourceVirtualLocation, VirtualTarget linkTo, LinkProperties linkProperties) { + public Map toVariableMap(Map stringDoubleMap) { + stringDoubleMap.put("depth", (double) this.sourceVirtualLocation.getDepth()); + stringDoubleMap.put("public_size", (double) DimensionalDoors.getConfig().getPocketsConfig().publicPocketSize); + stringDoubleMap.put("private_size", (double) DimensionalDoors.getConfig().getPocketsConfig().privatePocketSize); + return stringDoubleMap; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerator.java new file mode 100644 index 00000000..a37046af --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketGenerator.java @@ -0,0 +1,84 @@ +package org.dimdev.dimdoors.pockets; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.rift.registry.LinkProperties; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; +import org.dimdev.dimdoors.world.ModDimensions; +import org.dimdev.dimdoors.world.pocket.VirtualLocation; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public final class PocketGenerator { + private static final Logger LOGGER = LogManager.getLogger(); + + public static final Identifier ALL_DUNGEONS = DimensionalDoors.id("dungeon"); + public static final Identifier NETHER_DUNGEONS = DimensionalDoors.id("nether"); + public static final Identifier RUINS_DUNGEONS = DimensionalDoors.id("ruins"); + public static final Identifier ATLANTIS_DUNGEONS = DimensionalDoors.id("atlantis"); + public static final Identifier JUNGLE_DUNGEONS = DimensionalDoors.id("jungle"); + public static final Identifier SNOW_DUNGEONS = DimensionalDoors.id("snow"); + public static final Identifier PYRAMID_DUNGEONS = DimensionalDoors.id("pyramid"); + public static final Identifier END_DUNGEONS = DimensionalDoors.id("end"); + + /* + private static Pocket prepareAndPlacePocket(ServerWorld world, PocketTemplate pocketTemplate, VirtualLocation virtualLocation, boolean setup) { + LOGGER.info("Generating pocket from template " + pocketTemplate.getId() + " at virtual location " + virtualLocation); + + Pocket pocket = DimensionalRegistry.getPocketDirectory(world.getRegistryKey()).newPocket(Pocket.builder().expand(new Vec3i(1, 1, 1))); + pocketTemplate.place(pocket, setup); + pocket.virtualLocation = virtualLocation; + return pocket; + } + */ + + + public static Pocket generatePrivatePocketV2(VirtualLocation virtualLocation) { + return generateFromPocketGroupV2(DimensionalDoors.getWorld(ModDimensions.PERSONAL), DimensionalDoors.id("private"), virtualLocation, null, null); + } + + public static Pocket generatePublicPocketV2(VirtualLocation virtualLocation, VirtualTarget linkTo, LinkProperties linkProperties) { + return generateFromPocketGroupV2(DimensionalDoors.getWorld(ModDimensions.PUBLIC), DimensionalDoors.id("public"), virtualLocation, linkTo, linkProperties); + } + + public static Pocket generateFromPocketGroupV2(ServerWorld world, Identifier group, VirtualLocation virtualLocation, VirtualTarget linkTo, LinkProperties linkProperties) { + PocketGenerationContext context = new PocketGenerationContext(world, virtualLocation, linkTo, linkProperties); + return generatePocketV2(PocketLoader.getInstance().getGroup(group).getNextPocketGeneratorReference(context), context); + } + + public static Pocket generatePocketV2(PocketGeneratorReference pocketGeneratorReference, PocketGenerationContext context) { + return pocketGeneratorReference.prepareAndPlacePocket(context); + } + + public static Pocket generateDungeonPocketV2(VirtualLocation virtualLocation, VirtualTarget linkTo, LinkProperties linkProperties) { + return generateFromPocketGroupV2(DimensionalDoors.getWorld(ModDimensions.DUNGEON), DimensionalDoors.id("dungeon"), virtualLocation, linkTo, linkProperties); + } + + public static Pocket generateDungeonPocketV2(VirtualLocation virtualLocation, VirtualTarget linkTo, LinkProperties linkProperties, Identifier group) { + return generateFromPocketGroupV2(DimensionalDoors.getWorld(ModDimensions.DUNGEON), group, virtualLocation, linkTo, linkProperties); + } + + /* + /** + * Create a dungeon pockets at a certain depth. + * + * @param virtualLocation The virtual location of the pockets + * @return The newly-generated dungeon pockets + */ + /* + public static Pocket generateDungeonPocket(VirtualLocation virtualLocation, VirtualTarget linkTo, LinkProperties linkProperties) { + int depth = virtualLocation.getDepth(); + float netherProbability = DimensionalDoorsInitializer.getWorld(virtualLocation.getWorld()).getDimension().isUltrawarm() ? 1 : (float) depth / 200; // TODO: improve nether probability + Random random = Random.create(); + String group = random.nextFloat() < netherProbability ? "nether" : "ruins"; + PocketTemplate pocketTemplate = SchematicHandler.INSTANCE.getRandomTemplate(group, depth, DimensionalDoorsInitializer.getConfig().getPocketsConfig().maxPocketSize, false); + + return generatePocketFromTemplate(DimensionalDoorsInitializer.getWorld(ModDimensions.DUNGEON), pocketTemplate, virtualLocation, linkTo, linkProperties); + } + */ +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/PocketLoader.java b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketLoader.java new file mode 100644 index 00000000..fd5fae93 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketLoader.java @@ -0,0 +1,147 @@ +package org.dimdev.dimdoors.pockets; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.NbtUtil; +import org.dimdev.dimdoors.api.util.Path; +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.api.util.SimpleTree; +import org.dimdev.dimdoors.api.util.WeightedList; +import org.dimdev.dimdoors.pockets.generator.PocketGenerator; +import org.dimdev.dimdoors.pockets.virtual.VirtualPocket; +import org.dimdev.dimdoors.util.schematic.Schematic; + +public class PocketLoader implements SimpleSynchronousResourceReloadListener { + private static final Logger LOGGER = LogManager.getLogger(); + private static final PocketLoader INSTANCE = new PocketLoader(); + private SimpleTree pocketGenerators = new SimpleTree<>(String.class); + private SimpleTree pocketGroups = new SimpleTree<>(String.class); + private SimpleTree virtualPockets = new SimpleTree<>(String.class); + private SimpleTree templates = new SimpleTree<>(String.class); + private SimpleTree dataTree = new SimpleTree<>(String.class); + + private PocketLoader() { + } + + public void dump() { + virtualPockets.forEach((path, pocketGenerator) -> LOGGER.info("Virtual Pocket: " + path + " -> " + pocketGenerator.toString())); + pocketGroups.forEach((path, pocketGenerator) -> LOGGER.info("Pocket Group: " + path + " -> " + pocketGenerator.toString())); + } + + @Override + public void reload(ResourceManager manager) { + pocketGenerators.clear(); + pocketGroups.clear(); + virtualPockets.clear(); + templates.clear(); + dataTree.clear(); + + dataTree = ResourceUtil.loadResourcePathToMap(manager, "pockets/json", ".json", new SimpleTree<>(String.class), ResourceUtil.NBT_READER.composeIdentity(), ResourceUtil.PATH_KEY_PROVIDER).join(); + + CompletableFuture> futurePocketGeneratorMap = ResourceUtil.loadResourcePathToMap(manager, "pockets/generators", ".json", new SimpleTree<>(String.class), ResourceUtil.NBT_READER.andThenReader(pocketGeneratorLoader(manager)), ResourceUtil.PATH_KEY_PROVIDER); + CompletableFuture> futurePocketGroups = ResourceUtil.loadResourcePathToMap(manager, "pockets/groups", ".json", new SimpleTree<>(String.class), ResourceUtil.NBT_READER.andThenReader(virtualPocketLoader(manager)), ResourceUtil.PATH_KEY_PROVIDER); + CompletableFuture> futureVirtualPockets = ResourceUtil.loadResourcePathToMap(manager, "pockets/virtual", ".json", new SimpleTree<>(String.class), ResourceUtil.NBT_READER.andThenReader(virtualPocketLoader(manager)), ResourceUtil.PATH_KEY_PROVIDER); + CompletableFuture> futureTemplates = ResourceUtil.loadResourcePathToMap(manager, "pockets/schematic", ".schem", new SimpleTree<>(String.class), ResourceUtil.COMPRESSED_NBT_READER.andThenReader(this::loadPocketTemplate), ResourceUtil.PATH_KEY_PROVIDER); + + + pocketGenerators = futurePocketGeneratorMap.join(); + pocketGroups = futurePocketGroups.join(); + virtualPockets = futureVirtualPockets.join(); + templates = futureTemplates.join(); + + pocketGroups.values().forEach(VirtualPocket::init); + virtualPockets.values().forEach(VirtualPocket::init); + } + +// public void load() { +// long startTime = System.currentTimeMillis(); +// +// try { +// Path path = Paths.get(SchematicV2Handler.class.getResource("/data/dimdoors/pockets/generators").toURI()); +// loadJson(path, new String[0], this::loadPocketGenerator); +// LOGGER.info("Loaded pockets in {} seconds", System.currentTimeMillis() - startTime); +// } catch (URISyntaxException e) { +// LOGGER.error(e); +// } +// +// startTime = System.currentTimeMillis(); +// try { +// Path path = Paths.get(SchematicV2Handler.class.getResource("/data/dimdoors/pockets/groups").toURI()); +// loadJson(path, new String[0], this::loadPocketGroup); +// LOGGER.info("Loaded pocket groups in {} seconds", System.currentTimeMillis() - startTime); +// } catch (URISyntaxException e) { +// LOGGER.error(e); +// } +// } + + public NbtElement getDataNbt(String id) { + return this.dataTree.get(Path.stringPath(id)); + } + + public NbtCompound getDataNbtCompound(String id) { + return NbtUtil.asNbtCompound(getDataNbt(id), "Could not convert NbtElement \"" + id + "\" to NbtCompound!"); + } + + private BiFunction, VirtualPocket> virtualPocketLoader(ResourceManager manager) { + return (nbt, ignore) -> VirtualPocket.deserialize(nbt, manager); + } + + private BiFunction, PocketGenerator> pocketGeneratorLoader(ResourceManager manager) { + return (nbt, ignore) -> PocketGenerator.deserialize(NbtUtil.asNbtCompound(nbt, "Could not load PocketGenerator since its json does not represent an NbtCompound!"), manager); + } + + private PocketTemplate loadPocketTemplate(NbtCompound nbt, Path id) { + try { + return new PocketTemplate(Schematic.fromNbt(nbt), new Identifier(id.reduce(String::concat).orElseThrow())); + } catch (Exception e) { + throw new RuntimeException("Error loading " + nbt.toString(), e); + } + } + + public WeightedList getPocketsMatchingTags(List required, List blackList, boolean exact) { + return new WeightedList<>(pocketGenerators.values().stream().filter(pocketGenerator -> pocketGenerator.checkTags(required, blackList, exact)).collect(Collectors.toList())); + } + + public VirtualPocket getGroup(Identifier group) { + return pocketGroups.get(Path.stringPath(group)); + } + + public static PocketLoader getInstance() { + return INSTANCE; + } + + public SimpleTree getTemplates() { + return this.templates; + } + + public SimpleTree getPocketGroups() { + return this.pocketGroups; + } + + public SimpleTree getVirtualPockets() { + return this.virtualPockets; + } + + public PocketGenerator getGenerator(Identifier id) { + return pocketGenerators.get(Path.stringPath(id)); + } + + @Override + public Identifier getFabricId() { + return DimensionalDoors.id("schematics_v2"); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/PocketTemplate.java b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketTemplate.java new file mode 100644 index 00000000..257dc0f6 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/PocketTemplate.java @@ -0,0 +1,98 @@ +package org.dimdev.dimdoors.pockets; + +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.BlockPlacementType; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.util.schematic.Schematic; +import org.dimdev.dimdoors.util.schematic.SchematicPlacer; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class PocketTemplate { + private static final Logger LOGGER = LogManager.getLogger(); + private static final boolean replacingPlaceholders = false; + private final Schematic schematic; + private final Identifier id; + + public PocketTemplate(Schematic schematic, Identifier id) { + this.schematic = schematic; + this.id = id; + } + + /* + public void setup(Pocket pocket, VirtualTarget linkTo, LinkProperties linkProperties) { + ServerWorld world = DimensionalDoorsInitializer.getWorld(pocket.world); + + List rifts = new ArrayList<>(); + BlockPos origin = pocket.getOrigin(); + for (CompoundTag blockEntityTag : this.schematic.getBlockEntities()) { + int[] pos = blockEntityTag.getIntArray("Pos"); + + BlockPos actualBlock = origin.add(pos[0], pos[1], pos[2]); + + BlockEntity tile = world.getBlockEntity(actualBlock); + + if (tile instanceof RiftBlockEntity) { + LOGGER.debug("Rift found in schematic at " + actualBlock); + RiftBlockEntity rift = (RiftBlockEntity) tile; + rift.getDestination().setLocation(new Location((ServerWorld) Objects.requireNonNull(rift.getWorld()), rift.getPos())); + rifts.add(rift); + } else if (tile instanceof Inventory) { + Inventory inventory = (Inventory) tile; + if (inventory.isEmpty()) { + if (tile instanceof ChestBlockEntity || tile instanceof DispenserBlockEntity) { + TemplateUtils.setupLootTable(world, tile, inventory, LOGGER); + if (inventory.isEmpty()) { + LOGGER.error(", however Inventory is: empty!"); + } + } + } + } + } + + TemplateUtils.registerRifts(rifts, linkTo, linkProperties, pocket); + } + */ + + public void place(Pocket pocket, BlockPlacementType placementType) { + pocket.setSize(schematic.getWidth(), schematic.getHeight(), schematic.getLength()); + ServerWorld world = DimensionalDoors.getWorld(pocket.getWorld()); + BlockPos origin = pocket.getOrigin(); + SchematicPlacer.place(this.schematic, world, origin, placementType); + } + + public Map getAbsoluteRifts(Pocket pocket) { + pocket.setSize(schematic.getWidth(), schematic.getHeight(), schematic.getLength()); + Map absoluteRifts = SchematicPlacer.getAbsoluteRifts(this.schematic, pocket.getOrigin()); + World world = DimensionalDoors.getWorld(pocket.getWorld()); + absoluteRifts.values().forEach(rift -> rift.setWorld(world)); + return absoluteRifts; + } + + public void place(LazyGenerationPocket pocket, Chunk chunk, BlockPos originalOrigin, BlockPlacementType placementType) { + SchematicPlacer.place(this.schematic, DimensionalDoors.getWorld(pocket.getWorld()), chunk, originalOrigin, placementType); + } + + public static boolean isReplacingPlaceholders() { + return replacingPlaceholders; + } + + public Schematic getSchematic() { + return this.schematic; + } + + public Identifier getId() { + return id; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/TemplateUtils.java b/common/src/main/java/org/dimdev/dimdoors/pockets/TemplateUtils.java new file mode 100644 index 00000000..e98ad788 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/TemplateUtils.java @@ -0,0 +1,177 @@ +package org.dimdev.dimdoors.pockets; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.loot.LootTable; +import net.minecraft.loot.context.LootContext; +import net.minecraft.loot.context.LootContextParameters; +import net.minecraft.loot.context.LootContextTypes; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.StructureWorldAccess; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.Location; +import org.dimdev.dimdoors.api.util.math.MathUtil; +import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.entity.ModEntityTypes; +import org.dimdev.dimdoors.entity.MonolithEntity; +import org.dimdev.dimdoors.rift.registry.LinkProperties; +import org.dimdev.dimdoors.rift.targets.PocketEntranceMarker; +import org.dimdev.dimdoors.rift.targets.PocketExitMarker; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; +import org.dimdev.dimdoors.util.schematic.Schematic; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class TemplateUtils { + static void setupEntityPlaceholders(List entities, NbtCompound entityTag) { + if (entityTag.contains("placeholder")) { + double x = entityTag.getDouble("x"); + double y = entityTag.getDouble("y"); + double z = entityTag.getDouble("z"); + float yaw = entityTag.getFloat("yaw"); + float pitch = entityTag.getFloat("pitch"); + + NbtCompound newTag; + if ("monolith".equals(entityTag.getString("placeholder"))) { + MonolithEntity monolith = Objects.requireNonNull(ModEntityTypes.MONOLITH.create(null)); + monolith.setPos(x, y, z); + monolith.setYaw(yaw); + monolith.setPitch(pitch); + newTag = monolith.writeNbt(new NbtCompound()); + } else { + throw new RuntimeException("Unknown entity placeholder: " + entityTag.getString("placeholder")); + } + entities.add(newTag); + } else { + entities.add(entityTag); + } + } + + public static void setupLootTable(ServerWorld world, BlockEntity tile, Inventory inventory, Logger logger) { + LootTable table; + if (tile instanceof ChestBlockEntity) { + logger.debug("Now populating chest."); + table = world.getServer().getLootManager().getTable(DimensionalDoors.id("dungeon_chest")); + } else { + logger.debug("Now populating dispenser."); + table = world.getServer().getLootManager().getTable(DimensionalDoors.id("dispenser_projectiles")); + } + LootContext ctx = new LootContext.Builder(world).random(world.random).parameter(LootContextParameters.ORIGIN, Vec3d.of(tile.getPos())).build(LootContextTypes.CHEST); + table.supplyInventory(inventory, ctx); + if (inventory.isEmpty()) { + logger.error(", however Inventory is: empty!"); + } + } + + static public void registerRifts(List rifts, VirtualTarget linkTo, LinkProperties linkProperties, Pocket pocket) { + ServerWorld world = DimensionalDoors.getWorld(pocket.getWorld()); + HashMap entranceWeights = new HashMap<>(); + + for (RiftBlockEntity rift : rifts) { // Find an entrance + if (rift.getDestination() instanceof PocketEntranceMarker) { + entranceWeights.put(rift, ((PocketEntranceMarker) rift.getDestination()).getWeight()); + } + } + + if (entranceWeights.size() == 0) { + return; + } + + RiftBlockEntity selectedEntrance = MathUtil.weightedRandom(entranceWeights); + + // Replace entrances with appropriate destinations + for (RiftBlockEntity rift : rifts) { + VirtualTarget dest = rift.getDestination(); + if (dest instanceof PocketEntranceMarker) { + if (rift == selectedEntrance) { + rift.setDestination(((PocketEntranceMarker) dest).getIfDestination()); + rift.register(); + DimensionalRegistry.getRiftRegistry().addPocketEntrance(pocket, new Location((ServerWorld) rift.getWorld(), rift.getPos())); + } else { + rift.setDestination(((PocketEntranceMarker) dest).getOtherwiseDestination()); + } + } + } + + for (RiftBlockEntity rift : rifts) { + VirtualTarget dest = rift.getDestination(); + if (dest instanceof PocketExitMarker) { + if (linkProperties != null) rift.setProperties(linkProperties); + rift.setDestination(rift.getProperties() == null || !rift.getProperties().isOneWay() ? linkTo : null); + } + } + + for (RiftBlockEntity rift : rifts) { + rift.register(); + rift.markDirty(); + } + } + + public static void replacePlaceholders(Schematic schematic, StructureWorldAccess world) { + // Replace placeholders (some schematics will contain them) + List blockEntities = new ArrayList<>(); + for (NbtCompound blockEntityTag : schematic.getBlockEntities()) { + if (blockEntityTag.contains("placeholder")) { + int x = blockEntityTag.getInt("x"); + int y = blockEntityTag.getInt("y"); + int z = blockEntityTag.getInt("z"); + BlockPos pos = new BlockPos(x, y, z); + + NbtCompound newTag = new NbtCompound(); + EntranceRiftBlockEntity rift = new EntranceRiftBlockEntity(pos, Schematic.getBlockSample(schematic).getBlockState(pos)); + switch (blockEntityTag.getString("placeholder")) { + case "deeper_depth_door" -> { + rift.setProperties(DefaultDungeonDestinations.POCKET_LINK_PROPERTIES); + rift.setDestination(DefaultDungeonDestinations.getDeeperDungeonDestination()); + rift.writeNbt(newTag); + } + case "less_deep_depth_door" -> { + rift.setProperties(DefaultDungeonDestinations.POCKET_LINK_PROPERTIES); + rift.setDestination(DefaultDungeonDestinations.getShallowerDungeonDestination()); + rift.writeNbt(newTag); + } + case "overworld_door" -> { + rift.setProperties(DefaultDungeonDestinations.POCKET_LINK_PROPERTIES); + rift.setDestination(DefaultDungeonDestinations.getOverworldDestination()); + rift.writeNbt(newTag); + } + case "entrance_door" -> { + rift.setProperties(DefaultDungeonDestinations.POCKET_LINK_PROPERTIES); + rift.setDestination(DefaultDungeonDestinations.getTwoWayPocketEntrance()); + rift.writeNbt(newTag); + } + case "gateway_portal" -> { + rift.setProperties(DefaultDungeonDestinations.OVERWORLD_LINK_PROPERTIES); + rift.setDestination(DefaultDungeonDestinations.getGateway()); + rift.writeNbt(newTag); + } + default -> throw new RuntimeException("Unknown block entity placeholder: " + blockEntityTag.getString("placeholder")); + } + rift.setWorld(world.toServerWorld()); + blockEntities.add(newTag); + } else { + blockEntities.add(blockEntityTag); + } + } + schematic.setBlockEntities(blockEntities); + + List entities = new ArrayList<>(); + for (NbtCompound entityTag : schematic.getEntities()) { + TemplateUtils.setupEntityPlaceholders(entities, entityTag); + } + schematic.setEntities(entities); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/generator/ChunkGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/ChunkGenerator.java new file mode 100644 index 00000000..71e19bbf --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/ChunkGenerator.java @@ -0,0 +1,245 @@ +package org.dimdev.dimdoors.pockets.generator; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.fluid.FluidState; +import net.minecraft.fluid.Fluids; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerLightingProvider; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.HeightLimitView; +import net.minecraft.world.Heightmap; +import net.minecraft.world.biome.Biome; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.ChunkStatus; +import net.minecraft.world.chunk.ProtoChunk; +import net.minecraft.world.chunk.UpgradeData; +import net.minecraft.world.gen.GenerationStep; +import net.minecraft.world.gen.chunk.Blender; +import net.minecraft.world.gen.chunk.ChunkGeneratorSettings; +import net.minecraft.world.gen.noise.NoiseConfig; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.block.ModBlocks; +import org.dimdev.dimdoors.block.entity.DetachedRiftBlockEntity; +import org.dimdev.dimdoors.block.entity.ModBlockEntityTypes; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.rift.targets.PocketEntranceMarker; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; +import org.dimdev.dimdoors.world.pocket.VirtualLocation; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class ChunkGenerator extends PocketGenerator { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "chunk"; + + private Identifier dimensionID; + private Vec3i size; // TODO: equation-ify + private int virtualYOffset; // TODO: equation-ify + + public ChunkGenerator() { + } + + @Override + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + this.dimensionID = new Identifier(nbt.getString("dimension_id")); + + int[] temp = nbt.getIntArray("size"); + this.size = new Vec3i(temp[0], temp[1], temp[2]); + + this.virtualYOffset = nbt.contains("virtual_y_offset") ? nbt.getInt("virtual_y_offset") : 0; + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("dimension_id", dimensionID.toString()); + nbt.putIntArray("size", new int[]{this.size.getX(), this.size.getY(), this.size.getZ()}); + nbt.putInt("virtual_y_offset", this.virtualYOffset); + return nbt; + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + ServerWorld world = parameters.world(); + VirtualLocation sourceVirtualLocation = parameters.sourceVirtualLocation(); + + int chunkSizeX = ((this.size.getX() >> 4) + (this.size.getX() % 16 == 0 ? 0 : 1)); + int chunkSizeZ = ((this.size.getZ() >> 4) + (this.size.getZ() % 16 == 0 ? 0 : 1)); + + Pocket pocket = DimensionalRegistry.getPocketDirectory(world.getRegistryKey()).newPocket(builder); + + LOGGER.info("Generating chunk pocket at location " + pocket.getOrigin()); + + ServerWorld genWorld = DimensionalDoors.getWorld(RegistryKey.of(RegistryKeys.WORLD, dimensionID)); + net.minecraft.world.gen.chunk.ChunkGenerator genWorldChunkGenerator = genWorld.getChunkManager().getChunkGenerator(); + + NoiseConfig config = NoiseConfig.create(ChunkGeneratorSettings.createMissingSettings(), world.getRegistryManager().get(RegistryKeys.NOISE_PARAMETERS).getReadOnlyWrapper(), world.getSeed()); + + ArrayList protoChunks = new ArrayList<>(); + for (int z = 0; z < chunkSizeZ; z++) { + for (int x = 0; x < chunkSizeX; x++) { + ProtoChunk protoChunk = new ProtoChunk(new ChunkPos(pocket.getOrigin().add(x * 16, 0, z * 16)), UpgradeData.NO_UPGRADE_DATA, world, genWorld.getRegistryManager().get(RegistryKeys.BIOME), null); + protoChunk.setLightingProvider(genWorld.getLightingProvider()); + protoChunks.add(protoChunk); + } + } + ChunkRegion protoRegion = new ChunkRegionHack(genWorld, protoChunks);//TODO Redo? + for (Chunk protoChunk : protoChunks) { // TODO: check wether structures are even activated + genWorldChunkGenerator.setStructureStarts(genWorld.getRegistryManager(), genWorld.getChunkManager().getStructurePlacementCalculator(), genWorld.getStructureAccessor(), protoChunk, genWorld.getStructureTemplateManager()); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.STRUCTURE_STARTS); + } + for (Chunk protoChunk : protoChunks) { + genWorldChunkGenerator.addStructureReferences(protoRegion, genWorld.getStructureAccessor().forRegion(protoRegion), protoChunk); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.STRUCTURE_REFERENCES); + } + for (Chunk protoChunk : protoChunks) { + genWorldChunkGenerator.populateBiomes(Util.getMainWorkerExecutor(), config, Blender.getNoBlending(), genWorld.getStructureAccessor(), protoChunk); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.BIOMES); + } + for (Chunk protoChunk : protoChunks) { + try { + genWorldChunkGenerator.populateNoise(net.minecraft.util.Util.getMainWorkerExecutor(), Blender.getNoBlending(), config, genWorld.getStructureAccessor().forRegion(protoRegion), protoChunk).get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.NOISE); + } + for (Chunk protoChunk : protoChunks) { + genWorldChunkGenerator.buildSurface(protoRegion, genWorld.getStructureAccessor(), config, protoChunk); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.SURFACE); + } + for (GenerationStep.Carver carver : GenerationStep.Carver.values()) { + for (Chunk protoChunk : protoChunks) { + genWorldChunkGenerator.carve(protoRegion, genWorld.getSeed(), config, genWorld.getBiomeAccess(), genWorld.getStructureAccessor(), protoChunk, carver); + ProtoChunk pChunk = ((ProtoChunk) protoChunk); + if (pChunk.getStatus() == ChunkStatus.SURFACE) pChunk.setStatus(ChunkStatus.CARVERS); + else pChunk.setStatus(ChunkStatus.LIQUID_CARVERS); + } + } + for (Chunk protoChunk : protoChunks) { + ChunkRegion tempRegion = new ChunkRegionHack(genWorld, ChunkPos.stream(protoChunk.getPos(), 10).map(chunkPos -> protoRegion.getChunk(chunkPos.x, chunkPos.z)).collect(Collectors.toList())); + genWorldChunkGenerator.generateFeatures(tempRegion, protoChunk, genWorld.getStructureAccessor().forRegion(tempRegion)); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.FEATURES); + } + for (Chunk protoChunk : protoChunks) { // likely only necessary for spawn step since we copy over anyways + ((ServerLightingProvider) genWorld.getLightingProvider()).light(protoChunk, false); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.LIGHT); + } + for (Chunk protoChunk : protoChunks) { // TODO: does this even work? + ChunkRegion tempRegion = new ChunkRegionHack(genWorld, ChunkPos.stream(protoChunk.getPos(), 5).map(chunkPos -> protoRegion.getChunk(chunkPos.x, chunkPos.z)).collect(Collectors.toList())); + genWorldChunkGenerator.populateEntities(tempRegion); + ((ProtoChunk) protoChunk).setStatus(ChunkStatus.SPAWN); + } + + + BlockPos firstCorner = pocket.getOrigin(); + BlockPos secondCorner = new BlockPos(firstCorner.getX() + size.getX() - 1, Math.min(firstCorner.getY() + size.getY() - 1, world.getHeight() - virtualYOffset - 1), firstCorner.getZ() + size.getZ() - 1); // subtracting 1 here since it should be 0 inclusive and size exclusive + + BlockPos pocketOriginChunkOffset = new ChunkPos(pocket.getOrigin()).getStartPos().subtract(firstCorner); + for (BlockPos blockPos : BlockPos.iterate(firstCorner, secondCorner)) { + BlockPos sourcePos = blockPos.add(pocketOriginChunkOffset).add(0, virtualYOffset, 0); + BlockState blockState = protoRegion.getBlockState(sourcePos); + if (!blockState.isAir()) { + world.setBlockState(blockPos, protoRegion.getBlockState(blockPos.add(pocketOriginChunkOffset).add(0, virtualYOffset, 0))); + } + } + Box realBox = new Box(firstCorner, secondCorner); + for (Chunk protoChunk : protoChunks) { + for(BlockPos virtualBlockPos : protoChunk.getBlockEntityPositions()) { + BlockPos realBlockPos = virtualBlockPos.subtract(pocketOriginChunkOffset).add(0, -virtualYOffset, 0); + if (realBox.contains(realBlockPos.getX(), realBlockPos.getY(), realBlockPos.getZ())) { + world.addBlockEntity(protoChunk.getBlockEntity(virtualBlockPos)); // TODO: ensure this works, likely bugged + } + } + } + Box virtualBox = realBox.offset(pocketOriginChunkOffset.add(0, virtualYOffset, 0)); + /* + for (Entity entity : protoRegion.getOtherEntities(null, virtualBox)) { // TODO: does this even work? + TeleportUtil.teleport(entity, world, entity.getPos().add(-pocketOriginChunkOffset.getX(), -pocketOriginChunkOffset.getY() - virtualYOffset, -pocketOriginChunkOffset.getZ()), entity.yaw); + } // TODO: Entities?/ Biomes/ Structure Data + */ + world.setBlockState(world.getTopPosition(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, pocket.getOrigin()), ModBlocks.DETACHED_RIFT.getDefaultState()); + + DetachedRiftBlockEntity rift = ModBlockEntityTypes.DETACHED_RIFT.instantiate(world.getTopPosition(Heightmap.Type.MOTION_BLOCKING_NO_LEAVES, pocket.getOrigin()), ModBlocks.DETACHED_RIFT.getDefaultState()); + world.addBlockEntity(rift); + + rift.setDestination(new PocketEntranceMarker()); + pocket.virtualLocation = sourceVirtualLocation; + + return pocket; + } + + @Override + public PocketGeneratorType getType() { + return PocketGeneratorType.CHUNK; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public Vec3i getSize(PocketGenerationContext parameters) { + return size; + } + + private static class ChunkRegionHack extends ChunkRegion { // Please someone tell me if there is a better way + ChunkRegionHack(ServerWorld world, List chunks) { + super(world, chunks, ChunkStatus.EMPTY, 0); + } + + @Override + public Chunk getChunk(int chunkX, int chunkZ, ChunkStatus leastStatus, boolean create) { + Chunk chunk = super.getChunk(chunkX, chunkZ, leastStatus, false); + return chunk == null ? new ProtoChunkHack(new ChunkPos(chunkX, chunkZ), UpgradeData.NO_UPGRADE_DATA, this, this.getRegistryManager().get(RegistryKeys.BIOME)) : chunk; + } + + // TODO: Override getSeed() + } + + private static class ProtoChunkHack extends ProtoChunk { // exists solely to make some calls in the non utilized chunks faster + public ProtoChunkHack(ChunkPos pos, UpgradeData upgradeData, HeightLimitView world, Registry biomeRegistry) { + super(pos, upgradeData, world, biomeRegistry, null); + } + + @Override + public BlockState setBlockState(BlockPos pos, BlockState state, boolean moved) { + return Blocks.VOID_AIR.getDefaultState(); + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return Blocks.VOID_AIR.getDefaultState(); + } + + @Override + public FluidState getFluidState(BlockPos pos) { + return Fluids.EMPTY.getDefaultState(); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/generator/LazyPocketGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/LazyPocketGenerator.java new file mode 100644 index 00000000..914ef0b3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/LazyPocketGenerator.java @@ -0,0 +1,149 @@ +package org.dimdev.dimdoors.pockets.generator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.block.entity.DispenserBlockEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerTask; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.Chunk; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.pockets.TemplateUtils; +import org.dimdev.dimdoors.pockets.modifier.LazyModifier; +import org.dimdev.dimdoors.pockets.modifier.Modifier; +import org.dimdev.dimdoors.pockets.modifier.RiftManager; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public abstract class LazyPocketGenerator extends PocketGenerator { + private static final Logger LOGGER = LogManager.getLogger(); + + public static boolean currentlyGenerating = false; + public static Queue generationQueue = new LinkedList<>(); + + + protected List lazyModifierList = new ArrayList<>(); + + public void generateChunk(LazyGenerationPocket pocket, Chunk chunk) { + lazyModifierList.forEach(modifier -> modifier.applyToChunk(pocket, chunk)); + } + + // LazyPocketGenerator handles attaching itself so that it can drop itself if it has already generated everything necessary. + public void attachToPocket(LazyGenerationPocket pocket) { + // We assume that this LazyPocketGenerator has not been cloned yet if the modifier list has any entries since it should be empty at this stage + if (this.modifierList.size() > 0) throw new UnsupportedOperationException("Cannot attach LazyPocketGenerator that has not been cloned yet to pocket"); + pocket.attachGenerator(this); + } + + @Override + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + if (nbt.contains("lazy_modifiers")) { + NbtList modifiersNbt = nbt.getList("lazy_modifiers", 10); + for (int i = 0; i < modifiersNbt.size(); i++) { + // TODO: skip deserialization of single Modifiers on Exception. + // TODO: Modifier via ResourceManager + lazyModifierList.add((LazyModifier) Modifier.deserialize(modifiersNbt.getCompound(i), manager)); + } + } + + if (nbt.contains("lazy_modifier_references")) { + NbtList modifiersNbt = nbt.getList("lazy_modifier_references", NbtType.STRING); + for (NbtElement nbtElement : modifiersNbt) { + // TODO: skip deserialization of single Modifiers on Exception. + // TODO: Modifier via ResourceManager + lazyModifierList.add((LazyModifier) Modifier.deserialize(nbtElement, manager)); + } + } + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + if (lazyModifierList.size() > 0) { + List lazyModNbts = lazyModifierList.stream().map(lazyModifier -> lazyModifier.toNbt(new NbtCompound(), allowReference)).collect(Collectors.toList()); + + NbtList lazyModifiersNbt = new NbtList(); + lazyModifiersNbt.addAll(lazyModNbts.stream().filter(NbtCompound.class::isInstance).collect(Collectors.toList())); + nbt.put("lazy_modifiers", lazyModifiersNbt); + + NbtList lazyModifierReferences = new NbtList(); + lazyModifiersNbt.addAll(lazyModNbts.stream().filter(NbtString.class::isInstance).collect(Collectors.toList())); + nbt.put("lazy_modifier_references", lazyModifierReferences); + } + + return nbt; + } + + @Override + public RiftManager getRiftManager(Pocket pocket) { + if (pocket instanceof LazyGenerationPocket) { + return new RiftManager(pocket, true); + } else { + return new RiftManager(pocket, false); + } + } + + public void attachLazyModifiers(Collection lazyModifiers) { + this.lazyModifierList.addAll(lazyModifiers); + } + + public LazyPocketGenerator cloneWithLazyModifiers(BlockPos originalOrigin) { + LazyPocketGenerator clone = cloneWithEmptyModifiers(originalOrigin); + clone.attachLazyModifiers(this.modifierList.stream().filter(LazyModifier.class::isInstance).map(LazyModifier.class::cast).collect(Collectors.toList())); + return clone; + } + + public LazyPocketGenerator cloneWithEmptyModifiers(BlockPos originalOrigin) { + LazyPocketGenerator generator = getNewInstance(); + + // Builder/ weight related stuff seems irrelevant here + generator.setupLoot = this.setupLoot; + + return generator; + } + + public void setSetupLoot(Boolean setupLoot) { + this.setupLoot = setupLoot; + } + + abstract public LazyPocketGenerator getNewInstance(); + + public void setupChunk(Pocket pocket, Chunk chunk, boolean setupLootTables) { + MinecraftServer server = DimensionalDoors.getServer(); + chunk.getBlockEntityPositions().stream().map(chunk::getBlockEntity).forEach(blockEntity -> { // RiftBlockEntities should already be initialized here + if (setupLootTables && blockEntity instanceof Inventory) { + Inventory inventory = (Inventory) blockEntity; + server.send(new ServerTask(server.getTicks(), () -> { + if (inventory.isEmpty()) { + if (blockEntity instanceof ChestBlockEntity || blockEntity instanceof DispenserBlockEntity) { + TemplateUtils.setupLootTable(DimensionalDoors.getWorld(pocket.getWorld()), blockEntity, inventory, LOGGER); + } + } + })); + } + }); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/generator/PocketGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/PocketGenerator.java new file mode 100644 index 00000000..51888b99 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/PocketGenerator.java @@ -0,0 +1,333 @@ +package org.dimdev.dimdoors.pockets.generator; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.function.Supplier; + +import com.google.common.collect.Multimap; +import com.mojang.serialization.Lifecycle; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.block.entity.DispenserBlockEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3i; + +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.Location; +import org.dimdev.dimdoors.api.util.ReferenceSerializable; +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.api.util.Weighted; +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.api.util.math.Equation.EquationParseException; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.TemplateUtils; +import org.dimdev.dimdoors.pockets.modifier.Modifier; +import org.dimdev.dimdoors.pockets.modifier.RiftManager; +import org.dimdev.dimdoors.world.pocket.type.AbstractPocket; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public abstract class PocketGenerator implements Weighted, ReferenceSerializable { + private static final Logger LOGGER = LogManager.getLogger(); + public static final Registry> REGISTRY = FabricRegistryBuilder.from(new SimpleRegistry>(RegistryKey.ofRegistry(DimensionalDoors.id("pocket_generator_type")), Lifecycle.stable(), false)).buildAndRegister(); + public static final String RESOURCE_STARTING_PATH = "pockets/generator"; //TODO: might want to restructure data packs + + private static final String defaultWeightEquation = "5"; // TODO: make config + private static final int fallbackWeight = 5; // TODO: make config + protected final List modifierList = new ArrayList<>(); + + private String resourceKey = null; + + private NbtCompound builderNbt; + protected String weight = defaultWeightEquation; + protected Equation weightEquation; + protected Boolean setupLoot; + + private final List tags = new ArrayList<>(); + + public PocketGenerator() { } + + public PocketGenerator(String weight) { + this.weight = weight; + parseWeight(); + } + + public static PocketGenerator deserialize(NbtElement nbt, ResourceManager manager) { + switch (nbt.getType()) { + case NbtType.COMPOUND: // It's a serialized Modifier + return PocketGenerator.deserialize((NbtCompound) nbt, manager); + case NbtType.STRING: // It's a reference to a resource location + // TODO: throw if manager is null + return ResourceUtil.loadReferencedResource(manager, RESOURCE_STARTING_PATH, nbt.asString(), ResourceUtil.NBT_READER.andThenComposable(nbtElement -> deserialize(nbtElement, manager))); + default: + throw new RuntimeException(String.format("Unexpected NbtType %d!", nbt.getType())); + } + } + + public static PocketGenerator deserialize(NbtElement nbt) { + return deserialize(nbt, null); + } + + public static PocketGenerator deserialize(NbtCompound nbt, ResourceManager manager) { + Identifier id = Identifier.tryParse(nbt.getString("type")); // TODO: return some NONE PocketGenerator if type cannot be found or deserialization fails. + PocketGeneratorType type = REGISTRY.get(id); + if (type == null) { + LOGGER.error("Could not deserialize PocketGenerator: " + nbt.toString()); + return null; + } + return type.fromNbt(nbt, manager); + } + + public static PocketGenerator deserialize(NbtCompound nbt) { + return deserialize(nbt, null); + } + + public static NbtElement serialize(PocketGenerator pocketGenerator, boolean allowReference) { + return pocketGenerator.toNbt(new NbtCompound(), allowReference); + } + + public static NbtElement serialize(PocketGenerator pocketGenerator) { + return serialize(pocketGenerator, false); + } + + private void parseWeight() { + try { + this.weightEquation = Equation.parse(weight); + } catch (EquationParseException e) { + LOGGER.error("Could not parse weight equation \"" + weight + "\", defaulting to default weight equation \"" + defaultWeightEquation + "\"", e); + try { + // FIXME: do we actually want to have it serialize to the broken String equation we input? + this.weightEquation = Equation.newEquation(Equation.parse(defaultWeightEquation)::apply, stringBuilder -> stringBuilder.append(weight)); + } catch (EquationParseException equationParseException) { + LOGGER.error("Could not parse default weight equation \"" + defaultWeightEquation + "\", defaulting to fallback weight \"" + fallbackWeight + "\"", equationParseException); + // FIXME: do we actually want to have it serialize to the broken String equation we input? + this.weightEquation = Equation.newEquation(stringDoubleMap -> (double) fallbackWeight, stringBuilder -> stringBuilder.append(weight)); + } + } + } + + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + if (nbt.contains("builder", NbtType.COMPOUND)) builderNbt = nbt.getCompound("builder"); + + this.weight = nbt.contains("weight") ? nbt.getString("weight") : defaultWeightEquation; + parseWeight(); + + if (nbt.contains("setup_loot")) setupLoot = nbt.getBoolean("setup_loot"); + + if (nbt.contains("modifiers")) { + NbtList modifiersNbt = nbt.getList("modifiers", 10); + for (int i = 0; i < modifiersNbt.size(); i++) { + modifierList.add(Modifier.deserialize(modifiersNbt.getCompound(i), manager)); + } + } + + if (nbt.contains("modifier_references")) { + NbtList modifiersNbt = nbt.getList("modifier_references", NbtType.STRING); + for (NbtElement nbtElement : modifiersNbt) { + modifierList.add(Modifier.deserialize(nbtElement, manager)); + } + } + + if (nbt.contains("tags")) { + NbtList nbtList = nbt.getList("tags", NbtType.STRING); + for (int i = 0; i < nbtList.size(); i++) { + tags.add(nbtList.getString(i)); + } + } + return this; + } + + public PocketGenerator fromNbt(NbtCompound nbt) { + return fromNbt(nbt, null); + } + + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.resourceKey != null) { + return NbtString.of(this.resourceKey); + } + return toNbtInternal(nbt, allowReference); + } + + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + this.getType().toNbt(nbt); + + if (builderNbt != null) nbt.put("builder", builderNbt); + + if (!weight.equals(defaultWeightEquation)) nbt.putString("weight", weight); + + if (setupLoot != null) nbt.putBoolean("setup_loot", setupLoot); + + NbtList modifiersNbt = new NbtList(); + NbtList modifierReferences = new NbtList(); + for (Modifier modifier : modifierList) { + NbtElement modNbt = modifier.toNbt(new NbtCompound(), allowReference); + switch (modNbt.getType()) { + case NbtType.COMPOUND: + modifiersNbt.add(modNbt); + break; + case NbtType.STRING: + modifierReferences.add(modNbt); + break; + default: + throw new RuntimeException(String.format("Unexpected NbtType %d!", modNbt.getType())); + } + } + if (modifiersNbt.size() > 0) nbt.put("modifiers", modifiersNbt); + if (modifierReferences.size() > 0) nbt.put("modifier_references", modifierReferences); + + if (tags.size() > 0) { + NbtList nbtList = new NbtList(); + for (String nbtStr : tags) { + nbtList.add(NbtString.of(nbtStr)); + } + nbt.put("tags", nbtList); + } + + return nbt; + } + + public NbtElement toNbt(NbtCompound nbt) { + return toNbt(nbt, false); + } + + public void processFlags(Multimap flags) { + // TODO: discuss some flag standardization + Collection reference = flags.get("reference"); + if (reference.stream().findFirst().map(string -> string.equals("local") || string.equals("global")).orElse(false)) { + resourceKey = flags.get("resource_key").stream().findFirst().orElse(null); + } + } + + public abstract Pocket prepareAndPlacePocket(PocketGenerationContext parameters, Pocket.PocketBuilder builder); + + public abstract PocketGeneratorType getType(); + + public abstract String getKey(); + + @Override + public double getWeight(PocketGenerationContext parameters) { + return this.weightEquation.apply(parameters.toVariableMap(new HashMap<>())); + } + + public boolean isSetupLoot() { + return setupLoot != null && setupLoot; + } + + public void applyModifiers(PocketGenerationContext parameters, RiftManager manager) { + for (Modifier modifier : modifierList) { + modifier.apply(parameters, manager); + } + } + + public void applyModifiers(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + for (Modifier modifier : modifierList) { + modifier.apply(parameters, builder); + } + } + + public void setup(Pocket pocket, RiftManager manager, PocketGenerationContext parameters, boolean setupLootTables) { + ServerWorld world = parameters.world(); + + if (!(pocket instanceof LazyGenerationPocket)) { // should not iterate over that which does not exist & area may be massive, getBlockEntities() might force generation + if (setupLootTables) // temp + pocket.getBlockEntities().forEach((blockPos, blockEntity) -> { + if (/*setupLootTables &&*/ blockEntity instanceof Inventory) { // comment in if needed + Inventory inventory = (Inventory) blockEntity; + if (inventory.isEmpty()) { + if (blockEntity instanceof ChestBlockEntity || blockEntity instanceof DispenserBlockEntity) { + TemplateUtils.setupLootTable(world, blockEntity, inventory, LOGGER); + if (inventory.isEmpty()) { + LOGGER.error(", however Inventory is: empty!"); + } + } + } + } + }); + } + manager.getRifts().forEach(rift -> rift.getDestination().setLocation(new Location(world, rift.getPos()))); + TemplateUtils.registerRifts(manager.getRifts(), parameters.linkTo(), parameters.linkProperties(), pocket); + } + + public RiftManager getRiftManager(Pocket pocket) { + return new RiftManager(pocket); + } + + // why would you want to check for exact tags, but still need a blackList? Good question, but there is probably some use case for it. + public boolean checkTags(List required, List blackList, boolean exact) { + if (exact && required.size() != tags.size()) return false; + if (required != null) { + for (String req : required) { + if (!tags.contains(req)) return false; + } + } + if (blackList != null) { + for (String black : blackList) { + if (tags.contains(black)) return false; + } + } + return true; + } + + public Pocket.PocketBuilder pocketBuilder(PocketGenerationContext parameters) { // TODO: PocketBuilder from json + if (builderNbt == null){ + return Pocket.builder() + .expand(getSize(parameters)); + } + AbstractPocket.AbstractPocketBuilder abstractBuilder = AbstractPocket.deserializeBuilder(builderNbt); + if (! (abstractBuilder instanceof Pocket.PocketBuilder)) { + return Pocket.builder() + .expand(getSize(parameters)); + } + Pocket.PocketBuilder builder = (Pocket.PocketBuilder) abstractBuilder; + return builder.expand(getSize(parameters)); + } + + public abstract Vec3i getSize(PocketGenerationContext parameters); + + public interface PocketGeneratorType { + PocketGeneratorType SCHEMATIC = register(DimensionalDoors.id(SchematicGenerator.KEY), SchematicGenerator::new); + PocketGeneratorType CHUNK = register(DimensionalDoors.id(ChunkGenerator.KEY), ChunkGenerator::new); + PocketGeneratorType VOID = register(DimensionalDoors.id(VoidGenerator.KEY), VoidGenerator::new); + + PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager); + + NbtCompound toNbt(NbtCompound nbt); + + static void register() { + DimensionalDoors.apiSubscribers.forEach(d -> d.registerPocketGeneratorTypes(REGISTRY)); + } + + static PocketGeneratorType register(Identifier id, Supplier constructor) { + return Registry.register(REGISTRY, id, new PocketGeneratorType() { + @Override + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + return constructor.get().fromNbt(nbt, manager); + } + + @Override + public NbtCompound toNbt(NbtCompound nbt) { + nbt.putString("type", id.toString()); + return nbt; + } + }); + + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/generator/SchematicGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/SchematicGenerator.java new file mode 100644 index 00000000..23d6e1ea --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/SchematicGenerator.java @@ -0,0 +1,182 @@ +package org.dimdev.dimdoors.pockets.generator; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableTextContent; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.chunk.Chunk; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.BlockPlacementType; +import org.dimdev.dimdoors.api.util.Path; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.command.PocketCommand; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.PocketTemplate; +import org.dimdev.dimdoors.pockets.modifier.AbsoluteRiftBlockEntityModifier; +import org.dimdev.dimdoors.pockets.modifier.RiftManager; +import org.dimdev.dimdoors.util.schematic.Schematic; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class SchematicGenerator extends LazyPocketGenerator{ + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "schematic"; + + private String id; + private Identifier templateID; + private BlockPlacementType placementType = BlockPlacementType.SECTION_NO_UPDATE; + + private final List rifts = new ArrayList<>(); + private BlockPos origin; + + private AbsoluteRiftBlockEntityModifier queuedRiftBlockEntities; + + public SchematicGenerator() { + } + + public SchematicGenerator(String id) { + this.id = id; + + this.templateID = DimensionalDoors.id(id); + } + + public String getId() { + return this.id; + } + + public Identifier getTemplateID() { + return templateID; + } + + @Override + public void generateChunk(LazyGenerationPocket pocket, Chunk chunk) { + PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID)); + if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!"); + template.place(pocket, chunk, origin, placementType); + setupChunk(pocket, chunk, isSetupLoot()); + + super.generateChunk(pocket, chunk); + } + + @Override + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + this.id = nbt.getString("id"); // TODO: should we force having the "dimdoors:" in the json? + this.templateID = DimensionalDoors.id(id); + if (nbt.contains("origin", NbtType.INT_ARRAY)) { + int[] originInts = nbt.getIntArray("origin"); + this.origin = new BlockPos(originInts[0], originInts[1], originInts[2]); + } + if (nbt.contains("placement_type", NbtType.STRING)) placementType = BlockPlacementType.getFromId(nbt.getString("placement_type")); + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("id", this.id); + if (placementType != BlockPlacementType.SECTION_NO_UPDATE) nbt.putString("placement_type", placementType.getId()); + + if (origin != null) nbt.putIntArray("origin", new int[]{origin.getX(), origin.getY(), origin.getZ()}); + + return nbt; + } + + @Override + public RiftManager getRiftManager(Pocket pocket) { + RiftManager manager = super.getRiftManager(pocket); + + rifts.forEach(manager::add); + + return manager; + } + + @Override + public LazyPocketGenerator cloneWithLazyModifiers(BlockPos originalOrigin) { + LazyPocketGenerator generator = super.cloneWithLazyModifiers(originalOrigin); + generator.lazyModifierList.add(0, queuedRiftBlockEntities); + + return generator; + } + + @Override + public LazyPocketGenerator cloneWithEmptyModifiers(BlockPos originalOrigin) { + SchematicGenerator generator = (SchematicGenerator) super.cloneWithEmptyModifiers(originalOrigin); + + generator.id = id; + generator.templateID = templateID; + generator.origin = originalOrigin; + + return generator; + } + + @Override + public LazyPocketGenerator getNewInstance() { + return new SchematicGenerator(); + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + ServerWorld world = parameters.world(); + Map variableMap = parameters.toVariableMap(new HashMap<>()); + + PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID)); + if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!"); + + Pocket pocket = DimensionalRegistry.getPocketDirectory(world.getRegistryKey()).newPocket(builder); + BlockPos origin = pocket.getOrigin(); + LOGGER.info("Generating pocket from template " + templateID + " at location " + origin); + PocketCommand.logSetting.values().forEach(commandSource -> commandSource.sendFeedback(Text.translatable("commands.pocket.log.creation.generating", templateID, origin.getX(), origin.getY(), origin.getZ()), false)); + + + if (pocket instanceof LazyGenerationPocket) { + Map absoluteRifts = template.getAbsoluteRifts(pocket); + rifts.addAll(absoluteRifts.values()); + + queuedRiftBlockEntities = new AbsoluteRiftBlockEntityModifier(absoluteRifts); + } else { + template.place(pocket, placementType); + } + + return pocket; + } + + @Override + public PocketGeneratorType getType() { + return PocketGeneratorType.SCHEMATIC; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public Vec3i getSize(PocketGenerationContext parameters) { + PocketTemplate template = PocketLoader.getInstance().getTemplates().get(Path.stringPath(templateID)); + if (template == null) throw new RuntimeException("Pocket template of id " + templateID + " not found!"); + Schematic schem = template.getSchematic(); + return new Vec3i(schem.getWidth(), schem.getHeight(), schem.getLength()); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/generator/VoidGenerator.java b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/VoidGenerator.java new file mode 100644 index 00000000..69d9d21f --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/generator/VoidGenerator.java @@ -0,0 +1,99 @@ +package org.dimdev.dimdoors.pockets.generator; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; + +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.api.util.math.Equation.EquationParseException; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.world.level.registry.DimensionalRegistry; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class VoidGenerator extends LazyPocketGenerator { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "void"; + private String width; + private Equation heightEquation; + private String height; + private Equation widthEquation; + private String length; + private Equation lengthEquation; + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + Pocket pocket = DimensionalRegistry.getPocketDirectory(parameters.world().getRegistryKey()).newPocket(builder); + Map variableMap = parameters.toVariableMap(new HashMap<>()); + pocket.setSize((int) widthEquation.apply(variableMap), (int) heightEquation.apply(variableMap), (int) lengthEquation.apply(variableMap)); + + return pocket; + } + + @Override + public PocketGeneratorType getType() { + return PocketGeneratorType.VOID; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public Vec3i getSize(PocketGenerationContext parameters) { + Map variableMap = parameters.toVariableMap(new HashMap<>()); + return new Vec3i((int) widthEquation.apply(variableMap), (int) heightEquation.apply(variableMap), (int) lengthEquation.apply(variableMap)); + } + + @Override + public PocketGenerator fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + try { + width = nbt.getString("width"); + widthEquation = Equation.parse(width); + height = nbt.getString("height"); + heightEquation = Equation.parse(height); + length = nbt.getString("length"); + lengthEquation = Equation.parse(length); + + } catch (EquationParseException e) { + LOGGER.error(e); + } + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("width", width); + nbt.putString("height", height); + nbt.putString("length", length); + + return nbt; + } + + @Override + public LazyPocketGenerator cloneWithEmptyModifiers(BlockPos originalOrigin) { + VoidGenerator generator = (VoidGenerator) super.cloneWithEmptyModifiers(originalOrigin); + generator.width = width; + generator.height = height; + generator.length = length; + + return generator; + } + + @Override + public LazyPocketGenerator getNewInstance() { + return new VoidGenerator(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbsoluteRiftBlockEntityModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbsoluteRiftBlockEntityModifier.java new file mode 100644 index 00000000..586e8a63 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbsoluteRiftBlockEntityModifier.java @@ -0,0 +1,122 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.BlockBoxUtil; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class AbsoluteRiftBlockEntityModifier extends AbstractLazyModifier { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "block_entity"; + + private Map rifts; + private Map serializedRifts; + + public AbsoluteRiftBlockEntityModifier() { + } + + public AbsoluteRiftBlockEntityModifier(Map rifts) { + this.rifts = rifts; + + } + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + // TODO: rifts from resource + serializedRifts = nbt.getList("rifts", NbtType.COMPOUND).parallelStream().unordered().map(NbtCompound.class::cast) + .filter(compound -> { + if (compound.contains("Pos")) { + return true; + } + LOGGER.error("Discarding rift on deserialization since \"Pos\" tag was not set."); + return false; + }) + .collect(Collectors.toConcurrentMap(compound -> { + int[] ints = compound.getIntArray("Pos"); + return new BlockPos(ints[0], ints[1], ints[2]); + }, compound -> compound)); + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowResource) { + super.toNbtInternal(nbt, allowResource); + + NbtList riftsNbt; + if (rifts != null) { + riftsNbt = rifts.values().parallelStream().unordered().map(rift -> { + NbtCompound e = new NbtCompound(); + rift.writeNbt(e); + return e; + }).collect(Collectors.toCollection(NbtList::new)); + } else { + riftsNbt = new NbtList(); + riftsNbt.addAll(serializedRifts.values()); + } + nbt.put("rifts", riftsNbt); + + return nbt; + } + + @Override + public ModifierType getType() { + return ModifierType.ABSOLUTE_RIFT_BLOCK_ENTITY_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + if (!manager.isPocketLazy()) { // rifts is guaranteed to exist at this stage since this modifier is not supposed to be loaded from json + World world = DimensionalDoors.getWorld(manager.getPocket().getWorld()); + rifts.values().forEach(world::addBlockEntity); + } + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + + } + + @Override + public void applyToChunk(LazyGenerationPocket pocket, Chunk chunk) { + BlockBox chunkBox = BlockBoxUtil.getBox(chunk); + + if (rifts != null) { + rifts.entrySet().stream().unordered().filter(entry -> chunkBox.contains(entry.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + .forEach((pos, rift) -> { + rifts.remove(pos); + chunk.setBlockEntity(rift); + }); + } else { + serializedRifts.entrySet().stream().unordered().filter(entry -> chunkBox.contains(entry.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)) + .forEach((pos, riftNbt) -> { + rifts.remove(pos); + chunk.setBlockEntity(BlockEntity.createFromNbt(pos, chunk.getBlockState(pos), riftNbt)); + }); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyCompatibleModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyCompatibleModifier.java new file mode 100644 index 00000000..78e42dc5 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyCompatibleModifier.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; + +public abstract class AbstractLazyCompatibleModifier implements LazyCompatibleModifier { + private String resourceKey = null; + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return resourceKey; + } + + @Override + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.getResourceKey() != null) { + return NbtString.of(this.getResourceKey()); + } + return toNbtInternal(nbt, allowReference); + } + + // utility so the first part of toNbt can be extracted into default method + // at this point we know for a fact, that we need to serialize into the NbtCompound + // overwrite in subclass + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyModifier.java new file mode 100644 index 00000000..82754c6d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractLazyModifier.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; + +public abstract class AbstractLazyModifier implements LazyModifier { + private String resourceKey = null; + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return resourceKey; + } + + @Override + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.getResourceKey() != null) { + return NbtString.of(this.getResourceKey()); + } + return toNbtInternal(nbt, allowReference); + } + + // utility so the first part of toNbt can be extracted into default method + // at this point we know for a fact, that we need to serialize into the NbtCompound + // overwrite in subclass + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractModifier.java new file mode 100644 index 00000000..6e7da84c --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/AbstractModifier.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; + +public abstract class AbstractModifier implements Modifier { + private String resourceKey = null; + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return resourceKey; + } + + @Override + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.getResourceKey() != null) { + return NbtString.of(this.getResourceKey()); + } + return toNbtInternal(nbt, allowReference); + } + + // utility so the first part of toNbt can be extracted into default method + // at this point we know for a fact, that we need to serialize into the NbtCompound + // overwrite in subclass + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/DimensionalDoorModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/DimensionalDoorModifier.java new file mode 100644 index 00000000..333193d5 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/DimensionalDoorModifier.java @@ -0,0 +1,176 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.HashMap; +import java.util.Map; + +import com.google.common.base.MoreObjects; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.enums.DoubleBlockHalf; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.Direction; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.NbtEquations; +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.api.util.math.Equation.EquationParseException; +import org.dimdev.dimdoors.block.door.DimensionalDoorBlock; +import org.dimdev.dimdoors.block.entity.EntranceRiftBlockEntity; +import org.dimdev.dimdoors.block.entity.ModBlockEntityTypes; +import org.dimdev.dimdoors.block.entity.RiftData; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.rift.targets.IdMarker; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class DimensionalDoorModifier extends AbstractLazyCompatibleModifier { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "door"; + + private Direction facing; + private String doorTypeString; + private DimensionalDoorBlock doorType; + private NbtCompound doorData; + private String doorDataReference; + + private String x; + private String y; + private String z; + private Equation xEquation; + private Equation yEquation; + private Equation zEquation; + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + String facingString = nbt.getString("facing"); + facing = Direction.byName(nbt.getString("facing")); + if (facing == null || facing.getAxis().isVertical()) { + throw new RuntimeException("Could not interpret facing direction \"" + facingString + "\""); + } + + doorTypeString = nbt.getString("door_type"); + Block doorBlock = Registries.BLOCK.get(Identifier.tryParse(doorTypeString)); + if (!(doorBlock instanceof DimensionalDoorBlock)) { + throw new RuntimeException("Could not interpret door type \"" + doorTypeString + "\""); + } + doorType = (DimensionalDoorBlock) doorBlock; + + // TODO: rift data via ResourceManager + if (nbt.getType("rift_data") == NbtType.STRING) { + doorDataReference = nbt.getString("rift_data"); + doorData = PocketLoader.getInstance().getDataNbtCompound(doorDataReference); + } + + else if (nbt.getType("rift_data") == NbtType.COMPOUND) doorData = nbt.getCompound("rift_data"); + + try { + x = nbt.getString("x"); + y = nbt.getString("y"); + z = nbt.getString("z"); + + xEquation = Equation.parse(x); + yEquation = Equation.parse(y); + zEquation = Equation.parse(z); + } catch (EquationParseException e) { + LOGGER.error(e); + } + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("facing", facing.asString()); + nbt.putString("door_type", doorTypeString); + if (doorDataReference != null) nbt.putString("rift_data", doorDataReference); + else if (doorData != null) nbt.put("rift_data", doorData); + nbt.putString("x", x); + nbt.putString("y", y); + nbt.putString("z", z); + + return nbt; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("facing", facing) + .add("doorTypeString", doorTypeString) + .add("doorType", doorType) + .add("doorData", doorData) + .add("doorDataReference", doorDataReference) + .add("x", x) + .add("y", y) + .add("z", z) + .add("xEquation", xEquation) + .add("yEquation", yEquation) + .add("zEquation", zEquation) + .toString(); + } + + @Override + public ModifierType getType() { + return ModifierType.DIMENSIONAL_DOOR_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + Map variableMap = manager.getPocket().toVariableMap(new HashMap<>()); + BlockPos pocketOrigin = manager.getPocket().getOrigin(); + BlockPos pos = new BlockPos((int) (xEquation.apply(variableMap) + pocketOrigin.getX()), (int) (yEquation.apply(variableMap) + pocketOrigin.getY()), (int) (zEquation.apply(variableMap) + pocketOrigin.getZ())); + + BlockState lower = doorType.getDefaultState().with(DimensionalDoorBlock.HALF, DoubleBlockHalf.LOWER).with(DimensionalDoorBlock.FACING, facing); + BlockState upper = doorType.getDefaultState().with(DimensionalDoorBlock.HALF, DoubleBlockHalf.UPPER).with(DimensionalDoorBlock.FACING, facing); + EntranceRiftBlockEntity rift = ModBlockEntityTypes.ENTRANCE_RIFT.instantiate(pos, lower); + + if (doorData == null) { + rift.setDestination(new IdMarker(manager.nextId())); + } else { + NbtCompound solvedDoorData = NbtEquations.solveNbtCompoundEquations(doorData, variableMap); + rift.setData(RiftData.fromNbt(solvedDoorData)); + } + + manager.add(rift); + + if (manager.getPocket() instanceof LazyGenerationPocket) { + + // queue two separate tasks, Cubic Chunks may cause the positions to be in different chunks. + queueChunkModificationTask(new ChunkPos(pos), chunk -> { + chunk.setBlockState(pos, lower, false); + chunk.setBlockEntity(rift); + }); + queueChunkModificationTask(new ChunkPos(pos.up()), chunk -> { + chunk.setBlockState(pos.up(), upper, false); + }); + } else { + ServerWorld world = parameters.world(); + + world.setBlockState(pos, lower); + world.setBlockState(pos.up(), upper); + + world.addBlockEntity(rift); + } + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyCompatibleModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyCompatibleModifier.java new file mode 100644 index 00000000..d83d13dc --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyCompatibleModifier.java @@ -0,0 +1,39 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Queue; +import java.util.function.Consumer; + +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; + +public interface LazyCompatibleModifier extends Modifier { + LinkedHashMap>> chunkModificationQueue = new LinkedHashMap<>(); + + static void runQueuedModifications(Chunk chunk) { + Queue> tasks = chunkModificationQueue.remove(chunk.getPos()); + if (tasks == null) return; + Iterator> iterator = tasks.iterator(); + while (iterator.hasNext()) { + Consumer task = iterator.next(); + iterator.remove(); + task.accept(chunk); + } + } + + static void runLeftoverModifications(World world) { + new HashSet<>(chunkModificationQueue.keySet()).forEach(chunkPos -> world.getChunk(chunkPos.getStartPos())); // seems safest for Cubic Chunks reasons; + } + + default void queueChunkModificationTask(ChunkPos pos, Consumer task) { + chunkModificationQueue.compute(pos, ((chunkPos, chunkTaskQueue) -> { + if (chunkTaskQueue == null) chunkTaskQueue = new LinkedList<>(); + chunkTaskQueue.add(task); + return chunkTaskQueue; + })); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyModifier.java new file mode 100644 index 00000000..37f518b9 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/LazyModifier.java @@ -0,0 +1,9 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import net.minecraft.world.chunk.Chunk; + +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; + +public interface LazyModifier extends Modifier { + void applyToChunk(LazyGenerationPocket pocket, Chunk chunk); +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/Modifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/Modifier.java new file mode 100644 index 00000000..ebee35b8 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/Modifier.java @@ -0,0 +1,135 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.Collection; +import java.util.function.Supplier; + +import com.google.common.collect.Multimap; +import com.mojang.serialization.Lifecycle; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.ReferenceSerializable; +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public interface Modifier extends ReferenceSerializable { + Registry> REGISTRY = FabricRegistryBuilder.from(new SimpleRegistry>(RegistryKey.ofRegistry(DimensionalDoors.id("modifier_type")), Lifecycle.stable(), false)).buildAndRegister(); + + String RESOURCE_STARTING_PATH = "pockets/modifier"; //TODO: might want to restructure data packs + + static Modifier deserialize(NbtElement nbt, ResourceManager manager) { + switch (nbt.getType()) { + case NbtType.COMPOUND: // It's a serialized Modifier + return Modifier.deserialize((NbtCompound) nbt, manager); + case NbtType.STRING: // It's a reference to a resource location + // TODO: throw if manager is null + return ResourceUtil.loadReferencedResource(manager, RESOURCE_STARTING_PATH, nbt.asString(), ResourceUtil.NBT_READER.andThenComposable(nbtElement -> deserialize(nbtElement, manager))); + default: + throw new RuntimeException(String.format("Unexpected NbtType %d!", nbt.getType())); + } + } + + static Modifier deserialize(NbtElement nbt) { + return deserialize(nbt, null); + } + + static Modifier deserialize(NbtCompound nbt, ResourceManager manager) { + Identifier id = Identifier.tryParse(nbt.getString("type")); // TODO: return some NONE Modifier if type cannot be found or deserialization fails. + return REGISTRY.get(id).fromNbt(nbt, manager); + } + + static Modifier deserialize(NbtCompound nbt) { + return deserialize(nbt, null); + } + + static NbtElement serialize(Modifier modifier, boolean allowReference) { + return modifier.toNbt(new NbtCompound(), allowReference); + } + + static NbtElement serialize(Modifier modifier) { + return serialize(modifier, false); + } + + + Modifier fromNbt(NbtCompound nbt, ResourceManager manager); + + default Modifier fromNbt(NbtCompound nbt) { + return fromNbt(nbt, null); + } + + default NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } + + default NbtElement toNbt(NbtCompound nbt) { + return toNbt(nbt, false); + } + + void setResourceKey(String resourceKey); + + String getResourceKey(); + + default void processFlags(Multimap flags) { + // TODO: discuss some flag standardization + Collection reference = flags.get("reference"); + if (reference.stream().findFirst().map(string -> string.equals("local") || string.equals("global")).orElse(false)) { + setResourceKey(flags.get("resource_key").stream().findFirst().orElse(null)); + } + } + + ModifierType getType(); + + String getKey(); + + void apply(PocketGenerationContext parameters, RiftManager manager); + + void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder); + + interface ModifierType { + ModifierType SHELL_MODIFIER_TYPE = register(DimensionalDoors.id(ShellModifier.KEY), ShellModifier::new); + ModifierType DIMENSIONAL_DOOR_MODIFIER_TYPE = register(DimensionalDoors.id(DimensionalDoorModifier.KEY), DimensionalDoorModifier::new); + ModifierType PUBLIC_MODIFIER_TYPE = register(DimensionalDoors.id(PocketEntranceModifier.KEY), PocketEntranceModifier::new); + ModifierType RIFT_DATA_MODIFIER_TYPE = register(DimensionalDoors.id(RiftDataModifier.KEY), RiftDataModifier::new); + ModifierType RELATIVE_REFERENCE_MODIFIER_TYPE = register(DimensionalDoors.id(RelativeReferenceModifier.KEY), RelativeReferenceModifier::new); + ModifierType OFFSET_MODIFIER_TYPE = register(DimensionalDoors.id(OffsetModifier.KEY), OffsetModifier::new); + ModifierType ABSOLUTE_RIFT_BLOCK_ENTITY_MODIFIER_TYPE = register(DimensionalDoors.id(AbsoluteRiftBlockEntityModifier.KEY), AbsoluteRiftBlockEntityModifier::new); + + Modifier fromNbt(NbtCompound nbt, ResourceManager manager); + + default Modifier fromNbt(NbtCompound nbt) { + return fromNbt(nbt, null); + } + + NbtCompound toNbt(NbtCompound nbt); + + static void register() { + DimensionalDoors.apiSubscribers.forEach(d -> d.registerModifierTypes(REGISTRY)); + } + + static ModifierType register(Identifier id, Supplier factory) { + return Registry.register(REGISTRY, id, new ModifierType() { + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + return factory.get().fromNbt(nbt, manager); + } + + @Override + public NbtCompound toNbt(NbtCompound nbt) { + nbt.putString("type", id.toString()); + return nbt; + } + }); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/OffsetModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/OffsetModifier.java new file mode 100644 index 00000000..d5f4e840 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/OffsetModifier.java @@ -0,0 +1,75 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.math.Vec3i; + +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class OffsetModifier extends AbstractModifier { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "offset"; + + private String offsetX; + private Equation offsetXEquation; + private String offsetY; + private Equation offsetYEquation; + private String offsetZ; + private Equation offsetZEquation; + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + try { + offsetX = nbt.contains("offset_x") ? nbt.getString("offset_x") : "0"; + offsetXEquation = Equation.parse(offsetX); + offsetY = nbt.contains("offset_y") ? nbt.getString("offset_y") : "0"; + offsetYEquation = Equation.parse(offsetY); + offsetZ = nbt.contains("offset_z") ? nbt.getString("offset_z") : "0"; + offsetZEquation = Equation.parse(offsetZ); + } catch (Equation.EquationParseException e) { + LOGGER.error(e); + } + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + if (!offsetX.equals("0")) nbt.putString("offset_x", offsetX); + if (!offsetY.equals("0")) nbt.putString("offset_y", offsetY); + if (!offsetZ.equals("0")) nbt.putString("offset_z", offsetZ); + + return nbt; + } + + @Override + public ModifierType getType() { + return ModifierType.OFFSET_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + Map variableMap = parameters.toVariableMap(new HashMap<>()); + builder.offsetOrigin(new Vec3i((int) offsetXEquation.apply(variableMap), (int) offsetYEquation.apply(variableMap), (int) offsetZEquation.apply(variableMap))); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/PocketEntranceModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/PocketEntranceModifier.java new file mode 100644 index 00000000..f7c8b461 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/PocketEntranceModifier.java @@ -0,0 +1,69 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import com.google.common.base.MoreObjects; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; + +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.rift.targets.PocketEntranceMarker; +import org.dimdev.dimdoors.rift.targets.PocketExitMarker; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class PocketEntranceModifier extends AbstractModifier { + public static final String KEY = "pocket_entrance"; + + private int id; + + public PocketEntranceModifier(int id) { + this.id = id; + } + + public PocketEntranceModifier() { + + } + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + return new PocketEntranceModifier(nbt.getInt("id")); + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putInt("id", id); + + return nbt; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .toString(); + } + + @Override + public ModifierType getType() { + return ModifierType.PUBLIC_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + manager.consume(id, rift -> { + rift.setDestination(PocketEntranceMarker.builder().ifDestination(new PocketExitMarker()).weight(1.0f).build()); + return true; + }); + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RelativeReferenceModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RelativeReferenceModifier.java new file mode 100644 index 00000000..660a550d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RelativeReferenceModifier.java @@ -0,0 +1,106 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.Optional; + +import com.google.common.base.MoreObjects; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.StringIdentifiable; + +import org.dimdev.dimdoors.api.util.Location; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.rift.targets.LocalReference; +import org.dimdev.dimdoors.rift.targets.RiftReference; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class RelativeReferenceModifier extends AbstractModifier { + public static final String KEY = "relative"; + + private int point_a, point_b; + private ConnectionType connection = ConnectionType.BOTH; + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + point_a = nbt.getInt("point_a"); + point_b = nbt.getInt("point_b"); + connection = nbt.contains("connection") ? ConnectionType.fromString(nbt.getString("connection")) : ConnectionType.BOTH; + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + nbt.putInt("point_a", point_a); + nbt.putInt("point_b", point_b); + nbt.putString("connection", connection.asString()); + return nbt; + } + + @Override + public ModifierType getType() { + return ModifierType.RELATIVE_REFERENCE_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + Optional riftA = manager.get(point_a).map(rift -> new Location((ServerWorld) rift.getWorld(), rift.getPos())); + Optional riftB = manager.get(point_b).map(rift -> new Location((ServerWorld) rift.getWorld(), rift.getPos())); + + if(riftA.isPresent() && riftB.isPresent()) { + RiftReference link1 = LocalReference.tryMakeRelative(riftA.get(), riftB.get()); + RiftReference link2 = LocalReference.tryMakeRelative(riftB.get(), riftA.get()); + + manager.consume(point_a, rift -> addLink(rift, link1)); + + if(connection == ConnectionType.BOTH) manager.consume(point_b, rift -> addLink(rift, link2)); + } + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("point_a", point_a) + .add("point_b", point_b) + .add("connection", connection.asString()) + .toString(); + } + + private boolean addLink(RiftBlockEntity rift, RiftReference link) { + rift.setDestination(link); + return true; + } + + public enum ConnectionType implements StringIdentifiable { + BOTH("both"), + ONE_WAY("one_way"); + + private String id; + + ConnectionType(String id) { + this.id = id; + } + + @Override + public String asString() { + return id; + } + + public static ConnectionType fromString(String name) { + return "one_way".equals(name) ? ONE_WAY : BOTH; + } + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftDataModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftDataModifier.java new file mode 100644 index 00000000..728450a3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftDataModifier.java @@ -0,0 +1,115 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import com.google.common.base.MoreObjects; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.NbtEquations; +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.block.entity.RiftData; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.rift.targets.VirtualTarget; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class RiftDataModifier extends AbstractModifier { + public static final String KEY = "rift_data"; + + private NbtCompound doorData; + private String doorDataReference; + private List ids; + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + // TODO: RiftData via ResourceManager + if (nbt.getType("rift_data") == NbtType.STRING) { + doorDataReference = nbt.getString("rift_data"); + doorData = PocketLoader.getInstance().getDataNbtCompound(doorDataReference); + } + else if (nbt.getType("rift_data") == NbtType.COMPOUND) doorData = nbt.getCompound("rift_data"); + + ids = stream(nbt.getByteArray("ids")).boxed().collect(Collectors.toList()); + return this; + } + + public static IntStream stream(byte[] bytes) { + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return IntStream.generate(buffer::get).limit(buffer.remaining()); + } + + private static byte[] toByteArray(int[] ints) { + byte[] bytes = new byte[ints.length]; + for (int i = 0; i < ints.length; i++) { + bytes[i] = (byte) ints[i]; + } + return bytes; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + if (doorDataReference != null) nbt.putString("rift_data", doorDataReference); + else if (doorData != null) nbt.put("rift_data", doorData); + nbt.putByteArray("ids", toByteArray(ids.stream().mapToInt(Integer::intValue).toArray())); + return nbt; + } + + @Override + public ModifierType getType() { + return ModifierType.RIFT_DATA_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("doorData", doorData) + .add("doorDataReference", doorDataReference) + .add("ids", ids) + .toString(); + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + Map variableMap = manager.getPocket().toVariableMap(new HashMap<>()); + + Consumer riftBlockEntityConsumer; + + if (doorData == null) { + riftBlockEntityConsumer = rift -> rift.setDestination(VirtualTarget.NoneTarget.INSTANCE); + } else { + NbtCompound solvedDoorData = NbtEquations.solveNbtCompoundEquations(doorData, variableMap); + + riftBlockEntityConsumer = rift -> rift.setData(RiftData.fromNbt(solvedDoorData)); + } + + manager.foreachConsume((id, rift) -> { + if(ids.contains(id)) { + riftBlockEntityConsumer.accept(rift); + return true; + } else { + return false; + } + }); + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftManager.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftManager.java new file mode 100644 index 00000000..317a8344 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/RiftManager.java @@ -0,0 +1,103 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.BiPredicate; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import org.dimdev.dimdoors.block.entity.RiftBlockEntity; +import org.dimdev.dimdoors.rift.targets.IdMarker; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class RiftManager { + private final Map map; + private final List rifts; + private final Pocket pocket; + private int maxId; + + public RiftManager(Pocket pocket, boolean skipGatheringRifts) { + this.pocket = pocket; + if (skipGatheringRifts) { + map = new HashMap<>(); + rifts = new ArrayList<>(); + return; + } + rifts = pocket.getBlockEntities().values().stream() + .filter(RiftBlockEntity.class::isInstance).map(RiftBlockEntity.class::cast).collect(Collectors.toList()); + map = rifts.stream() + .filter(a -> a.getData().getDestination() instanceof IdMarker) + .filter(a -> ((IdMarker) a.getData().getDestination()).getId() >= 0) + .collect(Collectors.toMap(rift -> ((IdMarker) rift.getData().getDestination()).getId(), rift -> rift)); + maxId = map.keySet().stream() + .mapToInt(a -> a) + .max() + .orElse(-1); + } + + public RiftManager(Pocket pocket) { + this(pocket, false); + } + + //TODO add javadocs + public boolean add(RiftBlockEntity rift) { + rifts.add(rift); + if(rift.getData().getDestination() instanceof IdMarker) { + int id = ((IdMarker) rift.getData().getDestination()).getId(); + + if(id < 0) return false; + + map.put(id, rift); + + maxId = Math.max(id, maxId); + + return true; + } + return false; + } + + public boolean consume(int id, Predicate consumer) { + if (map.containsKey(id) && consumer.test(map.get(id))) { + map.remove(id); + return true; + } + return false; + } + + public Pocket getPocket() { + return pocket; + } + + public int nextId() { + return maxId + 1; + } + + public boolean available(int id) { // TODO: remove? method is likely redundant + return !map.containsKey(id); + } + + public void foreachConsume(BiPredicate consumer) { + for(int id : new HashSet<>(map.keySet())) { + if(consumer.test(id, map.get(id))) { + map.remove(id); + } + } + } + + public Optional get(int id) { + return Optional.ofNullable(map.get(id)); + } + + public List getRifts() { + return rifts; + } + + public boolean isPocketLazy() { + return pocket instanceof LazyGenerationPocket; + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/ShellModifier.java b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/ShellModifier.java new file mode 100644 index 00000000..7d5d5a8f --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/modifier/ShellModifier.java @@ -0,0 +1,254 @@ +package org.dimdev.dimdoors.pockets.modifier; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.google.common.base.MoreObjects; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.block.AirBlock; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3i; +import net.minecraft.world.chunk.Chunk; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.BlockBoxUtil; +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.util.schematic.SchematicBlockPalette; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class ShellModifier extends AbstractLazyModifier { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "shell"; + + private final List layers = new ArrayList<>(); + private BlockBox boxToDrawAround; + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + NbtList layersNbt = new NbtList(); + for (Layer layer : layers) { + layersNbt.add(layer.toNbt()); + } + nbt.put("layers", layersNbt); + if (boxToDrawAround != null) { + nbt.put("box_to_draw_around", BlockBoxUtil.toNbt(boxToDrawAround)); + } + + return nbt; + } + + @Override + public void applyToChunk(LazyGenerationPocket pocket, Chunk chunk) { + + int boxExpansion = 0; + for (Layer layer : layers) { + int thickness = layer.getThickness(pocket.toVariableMap(new HashMap<>())); + final BlockState blockState = layer.getBlockState(); + + BlockBox chunkBox = BlockBoxUtil.getBox(chunk); + + BlockBox temp; + + + // x-planes + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMaxX() + 1 + boxExpansion, boxToDrawAround.getMinY() - thickness - boxExpansion, boxToDrawAround.getMinZ() - thickness - boxExpansion), new Vec3i(boxToDrawAround.getMaxX() + thickness + boxExpansion, boxToDrawAround.getMaxY() + thickness + boxExpansion, boxToDrawAround.getMaxZ() + thickness + boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).isAir()) chunk.setBlockState(blockPos, blockState, false); + }); + } + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMinX() - 1 - boxExpansion, boxToDrawAround.getMinY() - thickness - boxExpansion, boxToDrawAround.getMinZ() - thickness - boxExpansion), new Vec3i(boxToDrawAround.getMinX() - thickness - boxExpansion, boxToDrawAround.getMaxY() + thickness + boxExpansion, boxToDrawAround.getMaxZ() + thickness + boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).isAir()) chunk.setBlockState(blockPos, blockState, false); + }); + } + + // y-planes + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMinX() - boxExpansion, boxToDrawAround.getMaxY() + 1 + boxExpansion, boxToDrawAround.getMinZ() - thickness - boxExpansion), new Vec3i(boxToDrawAround.getMaxX() + boxExpansion, boxToDrawAround.getMaxY() + thickness + boxExpansion, boxToDrawAround.getMaxZ() + thickness + boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).getBlock() instanceof AirBlock) + chunk.setBlockState(blockPos, blockState, false); + }); + } + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMinX() - boxExpansion, boxToDrawAround.getMinY() - 1 - boxExpansion, boxToDrawAround.getMinZ() - thickness - boxExpansion), new Vec3i(boxToDrawAround.getMaxX() + boxExpansion, boxToDrawAround.getMinY() - thickness - boxExpansion, boxToDrawAround.getMaxZ() + thickness + boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).isAir()) chunk.setBlockState(blockPos, blockState, false); + }); + } + + // z-planes + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMinX() - boxExpansion, boxToDrawAround.getMinY() - boxExpansion, boxToDrawAround.getMinZ() - 1 - boxExpansion), new Vec3i(boxToDrawAround.getMaxX() + boxExpansion, boxToDrawAround.getMaxY() + boxExpansion, boxToDrawAround.getMinZ() - thickness - boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).isAir()) chunk.setBlockState(blockPos, blockState, false); + }); + } + temp = BlockBox.create(new Vec3i(boxToDrawAround.getMinX() - boxExpansion, boxToDrawAround.getMinY() - boxExpansion, boxToDrawAround.getMaxZ() + 1 + boxExpansion), new Vec3i(boxToDrawAround.getMaxX() + boxExpansion, boxToDrawAround.getMaxY() + boxExpansion, boxToDrawAround.getMaxZ() + thickness + boxExpansion)); + if (temp.intersects(chunkBox)) { + temp = BlockBoxUtil.intersect(temp, chunkBox); + BlockPos.stream(temp) + .forEach(blockPos -> { + if (chunk.getBlockState(blockPos).isAir()) chunk.setBlockState(blockPos, blockState, false); + }); + } + + boxExpansion += thickness; + } + } + + @Override + public Modifier fromNbt(NbtCompound nbt, ResourceManager manager) { + for (NbtElement layerNbt : nbt.getList("layers", NbtType.COMPOUND)) { + NbtCompound nbtCompound = (NbtCompound) layerNbt; + try { + Layer layer = Layer.fromNbt(nbtCompound); + layers.add(layer); + } catch (CommandSyntaxException e) { + LOGGER.error("could not parse Layer: " + nbtCompound, e); + } + } + + if (nbt.contains("box_to_draw_around", NbtType.INT_ARRAY)) { + int[] box = nbt.getIntArray("box_to_draw_around"); + boxToDrawAround = BlockBox.create(new Vec3i(box[0], box[1], box[2]), new Vec3i(box[3], box[4], box[5])); + } + + return this; + } + + @Override + public ModifierType getType() { + return ModifierType.SHELL_MODIFIER_TYPE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void apply(PocketGenerationContext parameters, RiftManager manager) { + Pocket pocket = manager.getPocket(); + if (pocket instanceof LazyGenerationPocket) { + Map variableMap = pocket.toVariableMap(new HashMap<>()); + BlockBox pocketBox = pocket.getBox(); + boxToDrawAround = BlockBox.create(new Vec3i(pocketBox.getMinX(), pocketBox.getMinY(), pocketBox.getMinZ()), new Vec3i(pocketBox.getMaxX(), pocketBox.getMaxY(), pocketBox.getMaxZ())); + layers.forEach(layer -> pocket.expand(layer.getThickness(variableMap))); + } else { + layers.forEach(layer -> drawLayer(layer, manager.getPocket(), parameters.world())); + } + } + + @Override + public void apply(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + Map variableMap = parameters.toVariableMap(new HashMap<>()); + for (Layer layer : layers) { + int thickness = layer.getThickness(variableMap); + builder.expandExpected(new Vec3i(2 * thickness, 2 * thickness, 2 * thickness)); + builder.offsetOrigin(new Vec3i(thickness, thickness, thickness)); + } + } + + private void drawLayer(Layer layer, Pocket pocket, ServerWorld world) { + int thickness = layer.getThickness(pocket.toVariableMap(new HashMap<>())); + final BlockState blockState = layer.getBlockState(); + BlockBox pocketBox = pocket.getBox(); + + // x-planes + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMaxX() + 1, pocketBox.getMinY() - thickness, pocketBox.getMinZ() - thickness), new Vec3i(pocketBox.getMaxX() + thickness, pocketBox.getMaxY() + thickness, pocketBox.getMaxZ() + thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMinX() - 1, pocketBox.getMinY() - thickness, pocketBox.getMinZ() - thickness), new Vec3i(pocketBox.getMinX() - thickness, pocketBox.getMaxY() + thickness, pocketBox.getMaxZ() + thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + + // y-planes + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMinX(), pocketBox.getMaxY() + 1, pocketBox.getMinZ() - thickness), new Vec3i(pocketBox.getMaxX(), pocketBox.getMaxY() + thickness, pocketBox.getMaxZ() + thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMinX(), pocketBox.getMinY() - 1, pocketBox.getMinZ() - thickness), new Vec3i(pocketBox.getMaxX(), pocketBox.getMinY() - thickness, pocketBox.getMaxZ() + thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + + // z-planes + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMinX(), pocketBox.getMinY(), pocketBox.getMinZ() - 1), new Vec3i(pocketBox.getMaxX(), pocketBox.getMaxY(), pocketBox.getMinZ() - thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + BlockPos.stream(BlockBox.create(new Vec3i(pocketBox.getMinX(), pocketBox.getMinY(), pocketBox.getMaxZ() + 1), new Vec3i(pocketBox.getMaxX(), pocketBox.getMaxY(), pocketBox.getMaxZ() + thickness))) + .forEach(blockPos -> world.setBlockState(blockPos, blockState)); + + pocket.expand(thickness); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("layers", layers) + .toString(); + } + + public static class Layer { + private final String blockStateString; + private final String thickness; + private Equation thicknessEquation; + private final BlockState blockState; + + public Layer(String blockStateString, String thickness) { + this.blockStateString = blockStateString; + this.thickness = thickness; + try { + this.thicknessEquation = Equation.parse(thickness); + } catch (Equation.EquationParseException e) { + LOGGER.error("Could not parse layer thickness equation. Defaulting to 1"); + // FIXME: do we actually want to have it serialize to the broken String equation we input? + this.thicknessEquation = Equation.newEquation(variableMap -> 1d, stringBuilder -> stringBuilder.append(thickness)); + } + + this.blockState = SchematicBlockPalette.Entry.to(blockStateString).getOrThrow(false, LOGGER::error); + } + + public BlockState getBlockState() { + return blockState; + } + + public int getThickness(Map variableMap) { + return (int) thicknessEquation.apply(variableMap); + } + + public NbtCompound toNbt() { + NbtCompound nbt = new NbtCompound(); + nbt.putString("block_state", blockStateString); + nbt.putString("thickness", thickness); + return nbt; + } + + public static Layer fromNbt(NbtCompound nbt) throws CommandSyntaxException { + return new Layer(nbt.getString("block_state"), nbt.getString("thickness")); + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/AbstractVirtualPocket.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/AbstractVirtualPocket.java new file mode 100644 index 00000000..561658cd --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/AbstractVirtualPocket.java @@ -0,0 +1,34 @@ +package org.dimdev.dimdoors.pockets.virtual; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; + +public abstract class AbstractVirtualPocket implements ImplementedVirtualPocket { + private String resourceKey = null; + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return resourceKey; + } + + @Override + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.getResourceKey() != null) { + return NbtString.of(this.getResourceKey()); + } + return toNbtInternal(nbt, allowReference); + } + + // utility so the first part of toNbt can be extracted into default method + // at this point we know for a fact, that we need to serialize into the NbtCompound + // overwrite in subclass + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/ImplementedVirtualPocket.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/ImplementedVirtualPocket.java new file mode 100644 index 00000000..e27e5f7e --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/ImplementedVirtualPocket.java @@ -0,0 +1,158 @@ +package org.dimdev.dimdoors.pockets.virtual; + +import java.util.function.Supplier; + +import com.mojang.serialization.Lifecycle; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.SimpleRegistry; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; + +import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.virtual.reference.IdReference; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.pockets.virtual.reference.TagReference; +import org.dimdev.dimdoors.pockets.virtual.selection.ConditionalSelector; +import org.dimdev.dimdoors.pockets.virtual.selection.PathSelector; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public interface ImplementedVirtualPocket extends VirtualPocket { + String RESOURCE_STARTING_PATH = "pockets/virtual"; //TODO: might want to restructure data packs + + Registry> REGISTRY = FabricRegistryBuilder.from(new SimpleRegistry>(RegistryKey.ofRegistry(DimensionalDoors.id("virtual_pocket_type")), Lifecycle.stable(), false)).buildAndRegister(); + + static ImplementedVirtualPocket deserialize(NbtElement nbt, @Nullable ResourceManager manager) { + return switch (nbt.getType()) { + case NbtType.COMPOUND -> deserialize((NbtCompound) nbt, manager); + case NbtType.STRING -> ResourceUtil.loadReferencedResource(manager, RESOURCE_STARTING_PATH, nbt.asString(), ResourceUtil.NBT_READER.andThenComposable(nbtElement -> deserialize(nbtElement, manager))); + default -> throw new RuntimeException(String.format("Unexpected NbtType %d!", nbt.getType())); + }; + } + + static ImplementedVirtualPocket deserialize(NbtElement nbt) { + return deserialize(nbt, null); + } + + static ImplementedVirtualPocket deserialize(NbtCompound nbt, @Nullable ResourceManager manager) { + Identifier id = Identifier.tryParse(nbt.getString("type")); + VirtualPocketType type = REGISTRY.get(id); + return type != null ? type.fromNbt(nbt, manager) : VirtualPocketType.NONE.fromNbt(nbt, manager); + } + + static ImplementedVirtualPocket deserialize(NbtCompound nbt) { + return deserialize(nbt, null); + } + + static NbtElement serialize(ImplementedVirtualPocket implementedVirtualPocket, boolean allowReference) { + return implementedVirtualPocket.toNbt(new NbtCompound(), allowReference); + } + + static NbtElement serialize(ImplementedVirtualPocket implementedVirtualPocket) { + return serialize(implementedVirtualPocket, false); + } + + ImplementedVirtualPocket fromNbt(NbtCompound nbt, @Nullable ResourceManager manager); + + default ImplementedVirtualPocket fromNbt(NbtCompound nbt) { + return fromNbt(nbt, null); + } + + NbtElement toNbt(NbtCompound nbt, boolean allowReference); + + default NbtElement toNbt(NbtCompound nbt) { + return this.toNbt(nbt, false); + } + + VirtualPocketType getType(); + + String getKey(); + + interface VirtualPocketType { + VirtualPocketType NONE = register(DimensionalDoors.id(NoneVirtualPocket.KEY), () -> NoneVirtualPocket.NONE); + VirtualPocketType ID_REFERENCE = register(DimensionalDoors.id(IdReference.KEY), IdReference::new); + VirtualPocketType TAG_REFERENCE = register(DimensionalDoors.id(TagReference.KEY), TagReference::new); + VirtualPocketType CONDITIONAL_SELECTOR = register(DimensionalDoors.id(ConditionalSelector.KEY), ConditionalSelector::new); + VirtualPocketType PATH_SELECTOR = register(DimensionalDoors.id(PathSelector.KEY), PathSelector::new); + + ImplementedVirtualPocket fromNbt(NbtCompound nbt, @Nullable ResourceManager manager); + + default ImplementedVirtualPocket fromNbt(NbtCompound nbt) { + return fromNbt(nbt, null); + } + + NbtCompound toNbt(NbtCompound nbt); + + static void register() { + DimensionalDoors.apiSubscribers.forEach(d -> d.registerVirtualSingularPocketTypes(REGISTRY)); + } + + static VirtualPocketType register(Identifier id, Supplier factory) { + return Registry.register(REGISTRY, id, new VirtualPocketType() { + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + return factory.get().fromNbt(nbt, manager); + } + + @Override + public NbtCompound toNbt(NbtCompound nbt) { + nbt.putString("type", id.toString()); + return nbt; + } + }); + } + } + + // TODO: NoneReference instead? + class NoneVirtualPocket extends AbstractVirtualPocket { + public static final String KEY = "none"; + public static final NoneVirtualPocket NONE = new NoneVirtualPocket(); + + private NoneVirtualPocket() { + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters) { + throw new UnsupportedOperationException("Cannot place a NoneVirtualPocket"); + } + + @Override + public PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext parameters) { + throw new UnsupportedOperationException("Cannot get next pocket generator reference on a NoneVirtualPocket"); + } + + @Override + public PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext parameters) { + throw new UnsupportedOperationException("Cannot peek next pocket generator reference on a NoneVirtualPocket"); + } + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + return this; + } + + @Override + public VirtualPocketType getType() { + return VirtualPocketType.NONE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public double getWeight(PocketGenerationContext parameters) { + return 0; + } + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocket.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocket.java new file mode 100644 index 00000000..3f49c5f3 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocket.java @@ -0,0 +1,78 @@ +package org.dimdev.dimdoors.pockets.virtual; + +import java.util.Collection; + +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.Nullable; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.resource.ResourceManager; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.ReferenceSerializable; +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.api.util.Weighted; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public interface VirtualPocket extends Weighted, ReferenceSerializable { + String RESOURCE_STARTING_PATH = "pockets/virtual"; //TODO: might want to restructure data packs + + static VirtualPocket deserialize(NbtElement nbt) { + return deserialize(nbt, null); + } + + + //TODO: split up in ImplementedVirtualPocket and VirtualPocketList + static VirtualPocket deserialize(NbtElement nbt, @Nullable ResourceManager manager) { + switch (nbt.getType()) { + case NbtType.LIST: // It's a list of VirtualPocket + return VirtualPocketList.deserialize((NbtList) nbt, manager); + case NbtType.COMPOUND: // It's a serialized VirtualPocket + return ImplementedVirtualPocket.deserialize((NbtCompound) nbt, manager); + // TODO: throw if manager is null + case NbtType.STRING: // It's a reference to a resource location + return ResourceUtil.loadReferencedResource(manager, RESOURCE_STARTING_PATH, nbt.asString(), ResourceUtil.NBT_READER.andThenComposable(nbtElement -> deserialize(nbtElement, manager))); + default: + throw new RuntimeException(String.format("Unexpected NbtType %d!", nbt.getType())); + } + } + + static NbtElement serialize(VirtualPocket virtualPocket, boolean allowReference) { + if (virtualPocket instanceof VirtualPocketList) { + return VirtualPocketList.serialize((VirtualPocketList) virtualPocket, allowReference); + } + return ImplementedVirtualPocket.serialize((ImplementedVirtualPocket) virtualPocket, allowReference); + } + + static NbtElement serialize(VirtualPocket virtualPocket) { + return serialize(virtualPocket, false); + } + + void setResourceKey(String resourceKey); + + String getResourceKey(); + + default void processFlags(Multimap flags) { + // TODO: discuss some flag standardization + Collection reference = flags.get("reference"); + if (reference.stream().findFirst().map(string -> string.equals("local") || string.equals("global")).orElse(false)) { + setResourceKey(flags.get("resource_key").stream().findFirst().orElse(null)); + } + } + + Pocket prepareAndPlacePocket(PocketGenerationContext parameters); + + PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext parameters); + + PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext parameters); + + // Override where needed + default void init() { + + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocketList.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocketList.java new file mode 100644 index 00000000..4c3da23f --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/VirtualPocketList.java @@ -0,0 +1,115 @@ +package org.dimdev.dimdoors.pockets.virtual; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.resource.ResourceManager; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.ResourceUtil; +import org.dimdev.dimdoors.api.util.WeightedList; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class VirtualPocketList extends WeightedList implements VirtualPocket { + private String resourceKey = null; + + public static VirtualPocketList deserialize(NbtElement nbt, @Nullable ResourceManager manager) { + switch (nbt.getType()) { + case NbtType.LIST: + return deserialize((NbtList) nbt, manager); + case NbtType.STRING: + return ResourceUtil.loadReferencedResource(manager, RESOURCE_STARTING_PATH, nbt.asString(), ResourceUtil.NBT_READER.andThenComposable(nbtElement -> deserialize(nbtElement, manager))); + default: + throw new RuntimeException(String.format("Unexpected NbtType %d!", nbt.getType())); + } + } + + public static VirtualPocketList deserialize(NbtElement nbt) { + return deserialize(nbt, null); + } + + public static VirtualPocketList deserialize(NbtList nbt, @Nullable ResourceManager manager) { + return new VirtualPocketList().fromNbt(nbt, manager); + } + + public static VirtualPocketList deserialize(NbtList nbt) { + return deserialize(nbt, null); + } + + + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return this.resourceKey; + } + + public static NbtElement serialize(VirtualPocketList virtualPocketList, boolean allowReference) { + return virtualPocketList.toNbt(new NbtList(), allowReference); + } + + public static NbtElement serialize(VirtualPocketList virtualPocket) { + return serialize(virtualPocket, false); + } + + public VirtualPocketList() { + super(); + } + + public VirtualPocketList fromNbt(NbtList nbt, ResourceManager manager) { // Keep in mind, this would add onto the list instead of overwriting it if called multiple times. + for (NbtElement value : nbt) { + this.add(VirtualPocket.deserialize(value, manager)); + } + return this; + } + + public VirtualPocketList fromNbt(NbtList nbt) { + return fromNbt(nbt, null); + } + + public NbtElement toNbt(NbtList nbt, boolean allowReference) { + if (allowReference && resourceKey != null) { + return NbtString.of(resourceKey); + } + for(VirtualPocket virtualPocket : this) { + nbt.add(VirtualPocket.serialize(virtualPocket, allowReference)); + } + return nbt; + } + + public NbtElement toNbt(NbtList nbt) { + return toNbt(nbt, false); + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext context) { + return getNextPocketGeneratorReference(context).prepareAndPlacePocket(context); + } + + public PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext context) { + return getNextRandomWeighted(context).getNextPocketGeneratorReference(context); + } + + public PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext context) { + return peekNextRandomWeighted(context).peekNextPocketGeneratorReference(context); + } + + @Override + public void init() { + this.forEach(VirtualPocket::init); + } + + @Override + public double getWeight(PocketGenerationContext context) { + return getTotalWeight(context); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/IdReference.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/IdReference.java new file mode 100644 index 00000000..dc24ab45 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/IdReference.java @@ -0,0 +1,69 @@ +package org.dimdev.dimdoors.pockets.virtual.reference; + +import com.google.common.base.MoreObjects; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.generator.PocketGenerator; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; + +public class IdReference extends PocketGeneratorReference { + public static final String KEY = "id"; + + private Identifier id; + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + // TODO: make the json need the "dimdoors:" as well and load id via Identifier#tryParse instead + id = DimensionalDoors.id(nbt.getString("id")); + + return this; + } + + @Override + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("id", id.getPath()); + + return nbt; + } + + @Override + public PocketGenerator peekReferencedPocketGenerator(PocketGenerationContext parameters) { + return getReferencedPocketGenerator(parameters); + } + + @Override + public PocketGenerator getReferencedPocketGenerator(PocketGenerationContext parameters) { + return PocketLoader.getInstance().getGenerator(id); + } + + @Override + public VirtualPocketType getType() { + return VirtualPocketType.ID_REFERENCE; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("id", id) + .add("weight", weight) + .add("weightEquation", weightEquation) + .add("setupLoot", setupLoot) + .add("modifierList", modifierList) + .toString(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/PocketGeneratorReference.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/PocketGeneratorReference.java new file mode 100644 index 00000000..cac69d3d --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/PocketGeneratorReference.java @@ -0,0 +1,222 @@ +package org.dimdev.dimdoors.pockets.virtual.reference; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.resource.ResourceManager; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.ServerTask; +import net.minecraft.server.world.ChunkHolder; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.Chunk; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.DimensionalDoors; +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.api.util.math.Equation.EquationParseException; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.generator.LazyPocketGenerator; +import org.dimdev.dimdoors.pockets.generator.PocketGenerator; +import org.dimdev.dimdoors.pockets.modifier.LazyCompatibleModifier; +import org.dimdev.dimdoors.pockets.modifier.LazyModifier; +import org.dimdev.dimdoors.pockets.modifier.Modifier; +import org.dimdev.dimdoors.pockets.modifier.RiftManager; +import org.dimdev.dimdoors.pockets.virtual.AbstractVirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; +import org.dimdev.dimdoors.world.pocket.type.LazyGenerationPocket; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public abstract class PocketGeneratorReference extends AbstractVirtualPocket { + private static final Logger LOGGER = LogManager.getLogger(); + + protected String weight; + protected Equation weightEquation; + protected Boolean setupLoot; + protected final List modifierList = Lists.newArrayList(); + protected final List addons = new ArrayList<>(); + + private void parseWeight() { + try { + this.weightEquation = Equation.parse(weight); + } catch (EquationParseException e) { + LOGGER.debug("Defaulting to default weight equation for {}", this); + LOGGER.debug("Exception Stacktrace", e); + try { + // FIXME: do we actually want to have it serialize to the broken String equation we input? + this.weightEquation = Equation.newEquation(Equation.parse(DimensionalDoors.getConfig().getPocketsConfig().defaultWeightEquation)::apply, stringBuilder -> stringBuilder.append(weight)); + } catch (EquationParseException equationParseException) { + LOGGER.debug("Defaulting to default weight equation for {}", this); + LOGGER.debug("Exception Stacktrace", e); + // FIXME: do we actually want to have it serialize to the broken String equation we input? + this.weightEquation = Equation.newEquation(stringDoubleMap -> (double) DimensionalDoors.getConfig().getPocketsConfig().fallbackWeight, stringBuilder -> stringBuilder.append(weight)); + } + } + } + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + if (nbt.contains("weight")) { // override referenced pockets weight + this.weight = nbt.getString("weight"); + parseWeight(); + } + + if (nbt.contains("setup_loot")) setupLoot = nbt.getBoolean("setup_loot"); + + if (nbt.contains("modifiers")) { + NbtList modifiersNbt = nbt.getList("modifiers", 10); + for (int i = 0; i < modifiersNbt.size(); i++) { + modifierList.add(Modifier.deserialize(modifiersNbt.getCompound(i), manager)); + } + } + if (nbt.contains("modifier_references")) { + NbtList modifiersNbt = nbt.getList("modifier_references", NbtType.STRING); + for (NbtElement nbtElement : modifiersNbt) { + modifierList.add(Modifier.deserialize(nbtElement, manager)); + } + } + + if (nbt.contains("addons", NbtType.LIST)) { + NbtList addonsNbt = nbt.getList("addons", 10); + for (int i = 0; i < addonsNbt.size(); i++) { + // TODO: something with the ResourceManager??? Probably need AddonBuilder now. + addons.add(addonsNbt.getCompound(i)); + } + } + + return this; + } + + @Override + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + + if (weight != null) nbt.putString("weight", weight); + + if (setupLoot != null) nbt.putBoolean("setup_loot", setupLoot); + + NbtList modifiersNbt = new NbtList(); + // TODO: deserialize with ResourceManager + for (Modifier modifier : modifierList) { + modifiersNbt.add(modifier.toNbt(new NbtCompound())); + } + if (modifiersNbt.size() > 0) nbt.put("modifiers", modifiersNbt); + + NbtList addonsNbt = new NbtList(); + // TODO: something with the ResourceManager??? Probably need AddonBuilder now. + addonsNbt.addAll(addons); + if (addonsNbt.size() > 0) nbt.put("addons", addonsNbt); + + return nbt; + } + + @Override + public double getWeight(PocketGenerationContext parameters) { + try { + return weightEquation != null ? this.weightEquation.apply(parameters.toVariableMap(Maps.newHashMap())) : peekReferencedPocketGenerator(parameters).getWeight(parameters); + } catch (RuntimeException e) { + LOGGER.error(this.toString()); + throw new AssertionError(e); + } + } + + public void applyModifiers(PocketGenerationContext parameters, RiftManager manager) { + for (Modifier modifier : modifierList) { + modifier.apply(parameters, manager); + } + } + + public void applyModifiers(PocketGenerationContext parameters, Pocket.PocketBuilder builder) { + for (Modifier modifier : modifierList) { + modifier.apply(parameters, builder); + } + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters) { + PocketGenerator generator = getReferencedPocketGenerator(parameters); + + + Pocket.PocketBuilder builder = generator.pocketBuilder(parameters) + .virtualLocation(parameters.sourceVirtualLocation()); // TODO: virtualLocation thing still makes little sense + generator.applyModifiers(parameters, builder); + this.applyModifiers(parameters, builder); + + LazyPocketGenerator.currentlyGenerating = true; + // ensure we aren't missing any chunks that were already loaded previously + // for lazy gen + Set alreadyLoadedChunks = StreamSupport.stream(parameters.world().getChunkManager().threadedAnvilChunkStorage.entryIterator().spliterator(), false).map(ChunkHolder::getWorldChunk).filter(Objects::nonNull).collect(Collectors.toSet()); + + Pocket pocket = generator.prepareAndPlacePocket(parameters, builder); + BlockPos originalOrigin = pocket.getOrigin(); + + RiftManager manager = generator.getRiftManager(pocket); + + generator.applyModifiers(parameters, manager); + + this.applyModifiers(parameters, manager); + + if (pocket instanceof LazyGenerationPocket) { + if (!(generator instanceof LazyPocketGenerator)) throw new RuntimeException("pocket was instance of LazyGenerationPocket but generator was not instance of LazyPocketGenerator"); + LazyGenerationPocket lazyPocket = (LazyGenerationPocket) pocket; + LazyPocketGenerator clonedGenerator = ((LazyPocketGenerator) generator).cloneWithLazyModifiers(originalOrigin); + if (setupLoot != null) clonedGenerator.setSetupLoot(setupLoot); + + attachLazyModifiers(clonedGenerator); + clonedGenerator.attachToPocket(lazyPocket); + lazyPocket.init(); + + alreadyLoadedChunks.forEach(lazyPocket::chunkLoaded); + + LazyPocketGenerator.currentlyGenerating = false; + + while (!LazyPocketGenerator.generationQueue.isEmpty()) { + Chunk chunk = LazyPocketGenerator.generationQueue.remove(); + + LazyCompatibleModifier.runQueuedModifications(chunk); + MinecraftServer server = DimensionalDoors.getServer(); + DimensionalDoors.getServer().send(new ServerTask(server.getTicks(), () -> (lazyPocket).chunkLoaded(chunk))); + } + LazyCompatibleModifier.runLeftoverModifications(DimensionalDoors.getWorld(lazyPocket.getWorld())); + } else { + LazyPocketGenerator.currentlyGenerating = false; + LazyPocketGenerator.generationQueue.clear(); + } + + generator.setup(pocket, manager, parameters, setupLoot != null ? setupLoot : generator.isSetupLoot()); + + return pocket; + } + + @Override + public PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext parameters) { + return this; + } + + @Override + public PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext parameters) { + return this; + } + + public abstract PocketGenerator peekReferencedPocketGenerator(PocketGenerationContext parameters); + + public abstract PocketGenerator getReferencedPocketGenerator(PocketGenerationContext parameters); + + @Override + public abstract String toString(); + + public void attachLazyModifiers(LazyPocketGenerator generator) { + generator.attachLazyModifiers(modifierList.stream().filter(LazyModifier.class::isInstance).map(LazyModifier.class::cast).collect(Collectors.toList())); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/TagReference.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/TagReference.java new file mode 100644 index 00000000..15ae1d52 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/reference/TagReference.java @@ -0,0 +1,116 @@ +package org.dimdev.dimdoors.pockets.virtual.reference; + +import java.util.ArrayList; +import java.util.List; + +import com.google.common.base.MoreObjects; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtString; +import net.minecraft.resource.ResourceManager; + +import net.fabricmc.fabric.api.util.NbtType; + +import org.dimdev.dimdoors.api.util.WeightedList; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.generator.PocketGenerator; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; + +public class TagReference extends PocketGeneratorReference { + public static final String KEY = "tag"; + + private final List required = new ArrayList<>(); + private final List blackList = new ArrayList<>(); + private Boolean exact; + + private WeightedList pockets; + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + super.fromNbt(nbt, manager); + + if (nbt.contains("required")) { + NbtList listNbt = nbt.getList("required", NbtType.STRING); + for (int i = 0; i < listNbt.size(); i++) { + required.add(listNbt.getString(i)); + } + } + + if (nbt.contains("blackList")) { + NbtList listNbt = nbt.getList("blackList", NbtType.STRING); + for (int i = 0; i < listNbt.size(); i++) { + blackList.add(listNbt.getString(i)); + } + } + + if (nbt.contains("exact")) exact = nbt.getBoolean("exact"); + + return this; + } + + @Override + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + if (required.size() > 0) { + NbtList listNbt = new NbtList(); + for (String nbtStr : required) { + listNbt.add(NbtString.of(nbtStr)); + } + nbt.put("required", listNbt); + } + + if (blackList.size() > 0) { + NbtList list = new NbtList(); + for (String nbtStr : blackList) { + list.add(NbtString.of(nbtStr)); + } + nbt.put("blackList", list); + } + + if (exact != null) { + nbt.putBoolean("exact", exact); + } + + return nbt; + } + + + @Override + public VirtualPocketType getType() { + return VirtualPocketType.TAG_REFERENCE; + } + + @Override + public String getKey() { + return KEY; + } + // TODO: this will break if pockets change in between (which they could if we add a tool for creating pocket json config stuff ingame) + @Override + public PocketGenerator peekReferencedPocketGenerator(PocketGenerationContext parameters) { + if (pockets == null) pockets = PocketLoader.getInstance().getPocketsMatchingTags(required, blackList, exact); + return pockets.peekNextRandomWeighted(parameters); + } + + @Override + public PocketGenerator getReferencedPocketGenerator(PocketGenerationContext parameters) { + if (pockets == null) pockets = PocketLoader.getInstance().getPocketsMatchingTags(required, blackList, exact); + return pockets.getNextRandomWeighted(parameters); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("weight", weight) + .add("weightEquation", weightEquation) + .add("setupLoot", setupLoot) + .add("modifierList", modifierList) + .add("required", required) + .add("blackList", blackList) + .add("exact", exact) + .add("pockets", pockets) + .toString(); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/AbstractVirtualPocketList.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/AbstractVirtualPocketList.java new file mode 100644 index 00000000..c8da2ff4 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/AbstractVirtualPocketList.java @@ -0,0 +1,64 @@ +package org.dimdev.dimdoors.pockets.virtual.selection; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtString; + +import org.dimdev.dimdoors.api.util.WeightedList; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.VirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public abstract class AbstractVirtualPocketList extends WeightedList implements ImplementedVirtualPocket { + private String resourceKey = null; + + @Override + public void setResourceKey(String resourceKey) { + this.resourceKey = resourceKey; + } + + @Override + public String getResourceKey() { + return resourceKey; + } + + @Override + public NbtElement toNbt(NbtCompound nbt, boolean allowReference) { + if (allowReference && this.getResourceKey() != null) { + return NbtString.of(this.getResourceKey()); + } + return toNbtInternal(nbt, allowReference); + } + + // utility so the first part of toNbt can be extracted into default method + // at this point we know for a fact, that we need to serialize into the NbtCompound + // overwrite in subclass + protected NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + return this.getType().toNbt(nbt); + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext context) { + return getNextPocketGeneratorReference(context).prepareAndPlacePocket(context); + } + + public PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext context) { + return getNextRandomWeighted(context).getNextPocketGeneratorReference(context); + } + + public PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext context) { + return peekNextRandomWeighted(context).peekNextPocketGeneratorReference(context); + } + + @Override + public double getWeight(PocketGenerationContext context) { + return getTotalWeight(context); + } + + @Override + public void init() { + this.forEach(VirtualPocket::init); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/ConditionalSelector.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/ConditionalSelector.java new file mode 100644 index 00000000..8ac059b8 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/ConditionalSelector.java @@ -0,0 +1,117 @@ +package org.dimdev.dimdoors.pockets.virtual.selection; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import com.google.common.collect.Maps; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtList; +import net.minecraft.resource.ResourceManager; + +import org.dimdev.dimdoors.api.util.math.Equation; +import org.dimdev.dimdoors.pockets.PocketGenerationContext; +import org.dimdev.dimdoors.pockets.virtual.AbstractVirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.VirtualPocket; +import org.dimdev.dimdoors.pockets.virtual.reference.PocketGeneratorReference; +import org.dimdev.dimdoors.world.pocket.type.Pocket; + +public class ConditionalSelector extends AbstractVirtualPocket { + private static final Logger LOGGER = LogManager.getLogger(); + public static final String KEY = "conditional"; + + // TODO: redo this weird map part, Equations now have Equation.asString() + private LinkedHashMap pocketMap = Maps.newLinkedHashMap(); + private LinkedHashMap equationMap = Maps.newLinkedHashMap(); + + public ConditionalSelector() { + } + + public ConditionalSelector(LinkedHashMap pocketMap) { + this.pocketMap = pocketMap; + } + + public LinkedHashMap getPocketMap() { + return pocketMap; + } + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + NbtList conditionalPockets = nbt.getList("pockets", 10); + for (int i = 0; i < conditionalPockets.size(); i++) { + NbtCompound pocket = conditionalPockets.getCompound(i); + String condition = pocket.getString("condition"); + if (pocketMap.containsKey(condition)) continue; + try { + equationMap.put(condition, Equation.parse(condition)); + pocketMap.put(condition, VirtualPocket.deserialize(pocket.get("pocket"), manager)); + } catch (Equation.EquationParseException e) { + LOGGER.error("Could not parse pocket condition equation!", e); + } + } + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + NbtList conditionalPockets = new NbtList(); + pocketMap.forEach((condition, pocket) -> { + NbtCompound compound = new NbtCompound(); + compound.putString("condition", condition); + compound.put("pocket", VirtualPocket.serialize(pocket, allowReference)); + conditionalPockets.add(compound); + }); + nbt.put("pockets", conditionalPockets); + return nbt; + } + + @Override + public Pocket prepareAndPlacePocket(PocketGenerationContext parameters) { + return getNextPocket(parameters).prepareAndPlacePocket(parameters); + } + + @Override + public PocketGeneratorReference getNextPocketGeneratorReference(PocketGenerationContext parameters) { + return getNextPocket(parameters).getNextPocketGeneratorReference(parameters); + } + + @Override + public PocketGeneratorReference peekNextPocketGeneratorReference(PocketGenerationContext parameters) { + return getNextPocket(parameters).peekNextPocketGeneratorReference(parameters); + } + + @Override + public void init() { + pocketMap.values().forEach(VirtualPocket::init); + } + + @Override + public VirtualPocketType getType() { + return VirtualPocketType.CONDITIONAL_SELECTOR; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public double getWeight(PocketGenerationContext parameters) { + return getNextPocket(parameters).getWeight(parameters); + } + + private VirtualPocket getNextPocket(PocketGenerationContext parameters) { + for (Map.Entry entry : pocketMap.entrySet()) { + if (equationMap.get(entry.getKey()).asBoolean(parameters.toVariableMap(new HashMap<>()))) { + return entry.getValue(); + } + } + return pocketMap.values().stream().findFirst().orElse(NoneVirtualPocket.NONE); + } +} diff --git a/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/PathSelector.java b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/PathSelector.java new file mode 100644 index 00000000..a903da54 --- /dev/null +++ b/common/src/main/java/org/dimdev/dimdoors/pockets/virtual/selection/PathSelector.java @@ -0,0 +1,46 @@ +package org.dimdev.dimdoors.pockets.virtual.selection; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.resource.ResourceManager; + +import org.dimdev.dimdoors.api.util.Path; +import org.dimdev.dimdoors.pockets.PocketLoader; +import org.dimdev.dimdoors.pockets.virtual.ImplementedVirtualPocket; + +// TODO: Override equals +public class PathSelector extends AbstractVirtualPocketList { + public static final String KEY = "path"; + + private String path; + + @Override + public ImplementedVirtualPocket fromNbt(NbtCompound nbt, ResourceManager manager) { + this.path = nbt.getString("path"); + + return this; + } + + @Override + public NbtCompound toNbtInternal(NbtCompound nbt, boolean allowReference) { + super.toNbtInternal(nbt, allowReference); + + nbt.putString("path", path); + + return nbt; + } + + @Override + public VirtualPocketType getType() { + return VirtualPocketType.PATH_SELECTOR; + } + + @Override + public String getKey() { + return KEY; + } + + @Override + public void init() { + this.addAll(PocketLoader.getInstance().getVirtualPockets().getNode(Path.stringPath(path)).values()); + } +} diff --git a/common/src/main/resources/dimdoors.accesswidener b/common/src/main/resources/dimdoors.accesswidener index c36b93ea..780a422f 100644 --- a/common/src/main/resources/dimdoors.accesswidener +++ b/common/src/main/resources/dimdoors.accesswidener @@ -11,9 +11,8 @@ accessible method net/minecraft/entity/Entity setRotation (FF)V extendable class net/minecraft/block/entity/BlockEntityType$BlockEntityFactory # for HackyFluidState -extendable class net/minecraft/fluid/FluidState -accessible field net/minecraft/state/State codec Lcom/mojang/serialization/MapCodec; -#Lnet/minecraft/state/State;codec:Lcom/mojang/serialization/MapCodec; +extendable class net/minecraft/world/level/material/FluidState +accessible field net/minecraft/world/level/block/state/StateHolder propertiesCodec Lcom/mojang/serialization/MapCodec;#Lnet/minecraft/state/State;codec:Lcom/mojang/serialization/MapCodec; # for lazy pocket gen/ getting all currently loaded chunks accessible method net/minecraft/server/world/ThreadedAnvilChunkStorage entryIterator ()Ljava/lang/Iterable;