diff --git a/common/src/main/java/quaedam/mixin/MixinBuiltInRegistries.java b/common/src/main/java/quaedam/mixin/MixinBuiltInRegistries.java new file mode 100644 index 0000000..a639d14 --- /dev/null +++ b/common/src/main/java/quaedam/mixin/MixinBuiltInRegistries.java @@ -0,0 +1,19 @@ +package quaedam.mixin; + +import net.minecraft.core.registries.BuiltInRegistries; +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 quaedam.projection.ProjectionEffectType; + +@Mixin(BuiltInRegistries.class) +public class MixinBuiltInRegistries { + + @Inject(at = @At("HEAD"), method = "bootStrap()V") + private static void bootStrap(CallbackInfo info) { + // init projection effect type registry + ProjectionEffectType.Companion.getRegistry(); + } + +} diff --git a/common/src/main/kotlin/quaedam/Projector.kt b/common/src/main/kotlin/quaedam/Projector.kt deleted file mode 100644 index 8069df9..0000000 --- a/common/src/main/kotlin/quaedam/Projector.kt +++ /dev/null @@ -1,63 +0,0 @@ -package quaedam - -import net.minecraft.core.BlockPos -import net.minecraft.server.level.ServerLevel -import net.minecraft.util.RandomSource -import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionResult -import net.minecraft.world.entity.player.Player -import net.minecraft.world.item.BlockItem -import net.minecraft.world.item.Item -import net.minecraft.world.level.Level -import net.minecraft.world.level.block.Block -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.level.material.MapColor -import net.minecraft.world.phys.BlockHitResult - -object Projector { - - const val ID = "projector" - - val block = Quaedam.blocks.register(ID) { ProjectorBlock } - - val item = Quaedam.items.register(ID) { - BlockItem( - ProjectorBlock, Item.Properties() - .stacksTo(1) - .`arch$tab`(Quaedam.creativeModeTab) - ) - }!! - -} - -object ProjectorBlock : Block(Properties.of() - .jumpFactor(0.8f) - .lightLevel { 3 } - .mapColor(MapColor.COLOR_BLACK) - .randomTicks() - .strength(4.0f) - .requiresCorrectToolForDrops()) { - - @Suppress("OVERRIDE_DEPRECATION") - override fun use( - blockState: BlockState, - level: Level, - blockPos: BlockPos, - player: Player, - interactionHand: InteractionHand, - blockHitResult: BlockHitResult - ): InteractionResult { - return InteractionResult.SUCCESS - } - - @Suppress("OVERRIDE_DEPRECATION") - override fun randomTick( - blockState: BlockState, - serverLevel: ServerLevel, - blockPos: BlockPos, - randomSource: RandomSource - ) { - // @TODO: call projectorRandomTick - } - -} diff --git a/common/src/main/kotlin/quaedam/Quaedam.kt b/common/src/main/kotlin/quaedam/Quaedam.kt index b8578f1..8a44ad8 100644 --- a/common/src/main/kotlin/quaedam/Quaedam.kt +++ b/common/src/main/kotlin/quaedam/Quaedam.kt @@ -6,10 +6,11 @@ import dev.architectury.registry.registries.RegistrySupplier import net.minecraft.core.registries.Registries import net.minecraft.network.chat.Component import net.minecraft.world.item.CreativeModeTab -import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items +import quaedam.projection.ProjectionEffectType import quaedam.projection.SkylightProjection +import quaedam.projector.Projector object Quaedam { @@ -18,6 +19,8 @@ object Quaedam { val creativeModeTabs = DeferredRegister.create(ID, Registries.CREATIVE_MODE_TAB)!! val items = DeferredRegister.create(ID, Registries.ITEM)!! val blocks = DeferredRegister.create(ID, Registries.BLOCK)!! + val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!! + val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!! val creativeModeTab: RegistrySupplier = creativeModeTabs.register("quaedam") { CreativeTabRegistry.create(Component.translatable("category.quaedam")) { @@ -28,9 +31,12 @@ object Quaedam { fun init() { Projector SkylightProjection + creativeModeTabs.register() items.register() blocks.register() + blockEntities.register() + projectionEffects.register() } } \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/projection/ProjectionBlock.kt b/common/src/main/kotlin/quaedam/projection/ProjectionBlock.kt index 3967255..92a6b75 100644 --- a/common/src/main/kotlin/quaedam/projection/ProjectionBlock.kt +++ b/common/src/main/kotlin/quaedam/projection/ProjectionBlock.kt @@ -1,15 +1,19 @@ package quaedam.projection import net.minecraft.core.BlockPos -import net.minecraft.server.level.ServerLevel -import net.minecraft.util.RandomSource +import net.minecraft.world.entity.LivingEntity import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.Level +import net.minecraft.world.level.LevelAccessor import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.material.MapColor import net.minecraft.world.level.storage.loot.LootParams +import quaedam.projector.ProjectorBlockEntity +import quaedam.utils.getChunksNearby -abstract class ProjectionBlock(properties: Properties = createProperties()) : Block(properties) { +abstract class ProjectionBlock

(properties: Properties = createProperties()) : Block(properties), + ProjectionProvider

{ companion object { fun createProperties(): Properties = Properties.of() @@ -17,18 +21,39 @@ abstract class ProjectionBlock(properties: Properties = createProperties()) : Bl .requiresCorrectToolForDrops() .mapColor(MapColor.COLOR_GRAY) + fun findNearbyProjectors(level: Level, pos: BlockPos) = level.getChunksNearby(pos, 1) + .flatMap { + it.blockEntities.filter { (k, v) -> v is ProjectorBlockEntity } + .keys + .filterNotNull() + } + .toSet() + } @Suppress("OVERRIDE_DEPRECATION") override fun getDrops(blockState: BlockState, builder: LootParams.Builder) = listOf(ItemStack(asItem())) - fun projectionActivated(level: ServerLevel, projectorPos: BlockPos, projectionPos: BlockPos) { + override fun setPlacedBy( + level: Level, + pos: BlockPos, + state: BlockState, + placer: LivingEntity?, + itemStack: ItemStack + ) { + super.setPlacedBy(level, pos, state, placer, itemStack) + if (!level.isClientSide) { + findNearbyProjectors(level, pos) + .forEach { (level.getBlockEntity(it) as ProjectorBlockEntity).checkUpdate() } + } } - fun projectionDeactivated(level: ServerLevel, projectorPos: BlockPos, projectionPos: BlockPos) { + override fun destroy(level: LevelAccessor, pos: BlockPos, state: BlockState) { + super.destroy(level, pos, state) + if (level is Level && !level.isClientSide) { + findNearbyProjectors(level, pos) + .forEach { (level.getBlockEntity(it) as ProjectorBlockEntity).checkUpdate() } + } } - fun projectorRandomTick(level: ServerLevel, projectorPos: BlockPos, projectionPos: BlockPos, random: RandomSource) { - } - -} \ No newline at end of file +} diff --git a/common/src/main/kotlin/quaedam/projection/ProjectionEffect.kt b/common/src/main/kotlin/quaedam/projection/ProjectionEffect.kt new file mode 100644 index 0000000..ead8945 --- /dev/null +++ b/common/src/main/kotlin/quaedam/projection/ProjectionEffect.kt @@ -0,0 +1,54 @@ +package quaedam.projection + +import net.minecraft.core.BlockPos +import net.minecraft.core.Registry +import net.minecraft.core.registries.BuiltInRegistries +import net.minecraft.nbt.CompoundTag +import net.minecraft.resources.ResourceKey +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel +import net.minecraft.util.RandomSource +import net.minecraft.world.level.block.state.BlockState + +abstract class ProjectionEffect { + + abstract val type: ProjectionEffectType<*> + + abstract fun toNbt(tag: CompoundTag) + + abstract fun fromNbt(tag: CompoundTag) + + fun toNbt() = CompoundTag().apply { toNbt(this) } + + override fun equals(other: Any?) = other === this + + override fun hashCode() = type.hashCode() + + fun activated(level: ServerLevel, projectorPos: BlockPos) { + } + + fun deactivated(level: ServerLevel, projectorPos: BlockPos) { + } + + fun randomTick(level: ServerLevel, projectorPos: BlockPos, random: RandomSource) { + } + +} + +data class ProjectionEffectType(val constructor: () -> T) { + + companion object { + + val registryKey: ResourceKey>> = + ResourceKey.createRegistryKey(ResourceLocation("quaedam", "projection_effect")) + val registry: Registry> = BuiltInRegistries.registerSimple(registryKey) { null } + + } + + val id by lazy { registry.getResourceKey(this).get().location()!! } + +} + +interface ProjectionProvider

{ + fun createProjectionEffect(level: ServerLevel, state: BlockState, pos: BlockPos): P? +} diff --git a/common/src/main/kotlin/quaedam/projection/SkylightProjection.kt b/common/src/main/kotlin/quaedam/projection/SkylightProjection.kt index 758ae48..92a8c8a 100644 --- a/common/src/main/kotlin/quaedam/projection/SkylightProjection.kt +++ b/common/src/main/kotlin/quaedam/projection/SkylightProjection.kt @@ -1,14 +1,18 @@ package quaedam.projection +import net.minecraft.core.BlockPos +import net.minecraft.nbt.CompoundTag +import net.minecraft.server.level.ServerLevel import net.minecraft.world.item.BlockItem import net.minecraft.world.item.Item +import net.minecraft.world.level.block.state.BlockState import quaedam.Quaedam object SkylightProjection { const val ID = "skylight_projection" - val block = Quaedam.blocks.register(ID) { SkylightProjectionBlock } + val block = Quaedam.blocks.register(ID) { SkylightProjectionBlock }!! val item = Quaedam.items.register(ID) { BlockItem( @@ -17,6 +21,31 @@ object SkylightProjection { ) }!! + val effect = Quaedam.projectionEffects.register(ID) { + ProjectionEffectType { SkylightProjectionEffect } + }!! + } -object SkylightProjectionBlock : ProjectionBlock(createProperties().lightLevel { 3 }) +object SkylightProjectionBlock : ProjectionBlock(createProperties().lightLevel { 3 }) { + + override fun createProjectionEffect( + level: ServerLevel, + state: BlockState, + pos: BlockPos + ) = SkylightProjectionEffect + +} + +object SkylightProjectionEffect : ProjectionEffect() { + + override val type + get() = SkylightProjection.effect.get()!! + + override fun toNbt(tag: CompoundTag) { + } + + override fun fromNbt(tag: CompoundTag) { + } + +} diff --git a/common/src/main/kotlin/quaedam/projector/Projector.kt b/common/src/main/kotlin/quaedam/projector/Projector.kt new file mode 100644 index 0000000..f3a68c2 --- /dev/null +++ b/common/src/main/kotlin/quaedam/projector/Projector.kt @@ -0,0 +1,26 @@ +package quaedam.projector + +import net.minecraft.world.item.BlockItem +import net.minecraft.world.item.Item +import net.minecraft.world.level.block.entity.BlockEntityType +import quaedam.Quaedam + +object Projector { + + const val ID = "projector" + + val block = Quaedam.blocks.register(ID) { ProjectorBlock }!! + + val item = Quaedam.items.register(ID) { + BlockItem( + ProjectorBlock, Item.Properties() + .stacksTo(1) + .`arch$tab`(Quaedam.creativeModeTab) + ) + }!! + + val blockEntity = Quaedam.blockEntities.register(ID) { + BlockEntityType.Builder.of(::ProjectorBlockEntity, block.get()).build(null) + }!! + +} diff --git a/common/src/main/kotlin/quaedam/projector/ProjectorBlock.kt b/common/src/main/kotlin/quaedam/projector/ProjectorBlock.kt new file mode 100644 index 0000000..54a12f4 --- /dev/null +++ b/common/src/main/kotlin/quaedam/projector/ProjectorBlock.kt @@ -0,0 +1,84 @@ +package quaedam.projector + +import net.minecraft.core.BlockPos +import net.minecraft.server.level.ServerLevel +import net.minecraft.util.RandomSource +import net.minecraft.world.InteractionHand +import net.minecraft.world.InteractionResult +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.ItemStack +import net.minecraft.world.level.Level +import net.minecraft.world.level.LevelAccessor +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.MapColor +import net.minecraft.world.phys.BlockHitResult +import quaedam.projection.ProjectionBlock + +object ProjectorBlock : Block(Properties.of() + .jumpFactor(0.8f) + .lightLevel { 3 } + .mapColor(MapColor.COLOR_BLACK) + .randomTicks() + .strength(4.0f) + .requiresCorrectToolForDrops()), EntityBlock { + + fun checkUpdate(level: Level, pos: BlockPos) { + if (!level.isClientSide) { + (level.getBlockEntity(pos) as ProjectorBlockEntity).checkUpdate() + } + } + + @Suppress("OVERRIDE_DEPRECATION") + override fun use( + blockState: BlockState, + level: Level, + blockPos: BlockPos, + player: Player, + interactionHand: InteractionHand, + blockHitResult: BlockHitResult + ): InteractionResult { + checkUpdate(level, blockPos) + return InteractionResult.SUCCESS + } + + override fun newBlockEntity(pos: BlockPos, state: BlockState) = ProjectorBlockEntity(pos, state) + + @Suppress("OVERRIDE_DEPRECATION") + override fun randomTick( + state: BlockState, + level: ServerLevel, + pos: BlockPos, + random: RandomSource + ) { + // @TODO: call projectorRandomTick + checkUpdate(level, pos) + } + + @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") + override fun neighborChanged( + state: BlockState, + level: Level, + pos: BlockPos, + sourceBlock: Block, + sourcePos: BlockPos, + notify: Boolean + ) { + super.neighborChanged(state, level, pos, sourceBlock, sourcePos, notify) + checkUpdate(level, pos) + } + + override fun setPlacedBy( + level: Level, + pos: BlockPos, + state: BlockState, + placer: LivingEntity?, + itemStack: ItemStack + ) { + super.setPlacedBy(level, pos, state, placer, itemStack) + checkUpdate(level, pos) + } + +} diff --git a/common/src/main/kotlin/quaedam/projector/ProjectorBlockEntity.kt b/common/src/main/kotlin/quaedam/projector/ProjectorBlockEntity.kt new file mode 100644 index 0000000..2af9b7a --- /dev/null +++ b/common/src/main/kotlin/quaedam/projector/ProjectorBlockEntity.kt @@ -0,0 +1,126 @@ +package quaedam.projector + +import net.minecraft.core.BlockPos +import net.minecraft.core.Vec3i +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.protocol.Packet +import net.minecraft.network.protocol.game.ClientGamePacketListener +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.ChunkPos +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.levelgen.structure.BoundingBox +import quaedam.projection.ProjectionEffect +import quaedam.projection.ProjectionEffectType +import quaedam.projection.ProjectionProvider +import quaedam.utils.sendBlockUpdated + +class ProjectorBlockEntity(pos: BlockPos, state: BlockState) : + BlockEntity(Projector.blockEntity.get(), pos, state) { + + companion object { + const val EFFECT_RADIUS = 4 + } + + val effectAreaChunk by lazy { + val chunk = level!!.getChunk(pos).pos + ChunkPos(chunk.x - EFFECT_RADIUS, chunk.z - EFFECT_RADIUS) to + ChunkPos(chunk.x + EFFECT_RADIUS, chunk.z + EFFECT_RADIUS) + } + + val effectArea: BoundingBox by lazy { + val chunk = level!!.getChunk(pos).pos + val (minChunk, maxChunk) = effectAreaChunk + val minBlock = BlockPos(minChunk.minBlockX, level!!.minBuildHeight, minChunk.minBlockZ) + val maxBlock = BlockPos(maxChunk.maxBlockX, level!!.maxBuildHeight, maxChunk.maxBlockZ) + BoundingBox.fromCorners(minBlock, maxBlock) + } + + val checkArea: BoundingBox by lazy { + BoundingBox.fromCorners(pos.offset(-2, -1, -2), pos.offset(2, -2, 2)) + } + + var effects: Map, ProjectionEffect> = emptyMap() + + override fun saveAdditional(tag: CompoundTag) { + super.saveAdditional(tag) + val effectsTag = CompoundTag() + effects.map { (type, effect) -> + effectsTag.put(type.id.toString(), effect.toNbt()) + } + tag.put("ProjectionEffects", effectsTag) + } + + override fun load(tag: CompoundTag) { + super.load(tag) + val effectsTag = tag["ProjectionEffects"] + val effects = mutableMapOf, ProjectionEffect>() + if (effectsTag != null && effectsTag is CompoundTag) { + effectsTag.allKeys.forEach { id -> + val type = ProjectionEffectType.registry[ResourceLocation(id)] + if (type != null) { + val effect = type.constructor().apply { fromNbt(effectsTag[id] as CompoundTag) } + effects[type] = effect + } + } + } + updateEffects(effects) + } + + override fun getUpdateTag(): CompoundTag = saveWithoutMetadata() + + override fun getUpdatePacket(): Packet = ClientboundBlockEntityDataPacket.create(this) + + override fun setRemoved() { + super.setRemoved() + updateEffects(emptyMap(), notify = false) + } + + operator fun contains(pos: Vec3i) = effectArea.isInside(pos) + + operator fun contains(pos: ChunkPos) = + this.contains(Vec3i(pos.middleBlockX, level!!.minBuildHeight, pos.middleBlockZ)) + + fun checkUpdate() { + if (level!!.isClientSide) + return + val effects = collectEffects() + updateEffects(effects) + } + + fun updateEffects(effects: Map, ProjectionEffect>, notify: Boolean = true) { + if (effects != this.effects) { + this.effects = effects + if (!level!!.isClientSide) { + sendBlockUpdated() + } + } + } + + fun collectEffects(): Map, ProjectionEffect> { + val level = level!! as ServerLevel + if (!level.getBlockState(blockPos.below()).isAir) { + return emptyMap() + } + val effects = mutableMapOf, ProjectionEffect>() + for (x in checkArea.minX()..checkArea.maxX()) { + for (y in checkArea.minY()..checkArea.maxY()) { + for (z in checkArea.minZ()..checkArea.maxZ()) { + val pos = BlockPos(x, y, z) + val blockState = level.getBlockState(pos) + val block = blockState.block + if (block is ProjectionProvider<*>) { + val projection = block.createProjectionEffect(level, blockState, pos) + if (projection != null) { + effects[projection.type] = projection + } + } + } + } + } + return effects + } + +} diff --git a/common/src/main/kotlin/quaedam/utils/BlockEntity.kt b/common/src/main/kotlin/quaedam/utils/BlockEntity.kt new file mode 100644 index 0000000..8545806 --- /dev/null +++ b/common/src/main/kotlin/quaedam/utils/BlockEntity.kt @@ -0,0 +1,7 @@ +package quaedam.utils + +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.entity.BlockEntity + +fun BlockEntity.sendBlockUpdated() = + level!!.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS) diff --git a/common/src/main/kotlin/quaedam/utils/Chunks.kt b/common/src/main/kotlin/quaedam/utils/Chunks.kt new file mode 100644 index 0000000..49d175f --- /dev/null +++ b/common/src/main/kotlin/quaedam/utils/Chunks.kt @@ -0,0 +1,16 @@ +package quaedam.utils + +import net.minecraft.core.BlockPos +import net.minecraft.core.SectionPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.chunk.LevelChunk + +fun Level.getChunksNearby(pos: BlockPos, radius: Int): Set { + val chunkX = SectionPos.blockToSectionCoord(pos.x) + val chunkZ = SectionPos.blockToSectionCoord(pos.z) + return (chunkX - radius..chunkX + radius).flatMap { x -> + (chunkZ - radius..chunkZ + radius).map { z -> + getChunk(x, z) + } + }.toSet() +} diff --git a/common/src/main/resources/assets/quaedam/models/item/skylight_projection.json b/common/src/main/resources/assets/quaedam/models/item/skylight_projection.json new file mode 100644 index 0000000..e409c97 --- /dev/null +++ b/common/src/main/resources/assets/quaedam/models/item/skylight_projection.json @@ -0,0 +1,3 @@ +{ + "parent": "quaedam:block/skylight_projection" +} \ No newline at end of file diff --git a/common/src/main/resources/quaedam-common.mixins.json b/common/src/main/resources/quaedam-common.mixins.json index 93e28e7..8c01e12 100644 --- a/common/src/main/resources/quaedam-common.mixins.json +++ b/common/src/main/resources/quaedam-common.mixins.json @@ -5,6 +5,7 @@ "client": [ ], "mixins": [ + "MixinBuiltInRegistries" ], "injectors": { "defaultRequire": 1 diff --git a/common/src/main/resources/quaedam.accesswidener b/common/src/main/resources/quaedam.accesswidener index 13268c3..fe16b81 100644 --- a/common/src/main/resources/quaedam.accesswidener +++ b/common/src/main/resources/quaedam.accesswidener @@ -1 +1,3 @@ -accessWidener v2 named \ No newline at end of file +accessWidener v2 named +# Custom Registry for ProjectionEffect +accessible method net/minecraft/core/registries/BuiltInRegistries registerSimple (Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap;)Lnet/minecraft/core/Registry;