diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt new file mode 100644 index 000000000..20997844e --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MultiblockTestBlock.kt @@ -0,0 +1,23 @@ +package ru.dbotthepony.mc.otm.block + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.block.entity.MultiblockTestBlockEntity + +class MultiblockTestBlock : MatteryBlock(), EntityBlock { + override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity { + return MultiblockTestBlockEntity(blockPos, blockState) + } + + override fun getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType): BlockEntityTicker? { + if (level.isClientSide) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is MultiblockTestBlockEntity) tile.tick() } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt new file mode 100644 index 000000000..a5bdd18f7 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MultiblockTestBlockEntity.kt @@ -0,0 +1,75 @@ +package ru.dbotthepony.mc.otm.block.entity + +import net.minecraft.core.BlockPos +import net.minecraft.tags.BlockTags +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.FurnaceBlockEntity +import net.minecraft.world.level.block.entity.HopperBlockEntity +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.core.multiblockConfiguration +import ru.dbotthepony.mc.otm.core.multiblockEntity +import ru.dbotthepony.mc.otm.registry.MBlockEntities +import ru.dbotthepony.mc.otm.registry.MBlocks + +class MultiblockTestBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.MULTIBLOCK_TEST, blockPos, blockState) { + val config = CONFIG.create(blockPos) + + override fun tick() { + super.tick() + + if (config.update(level!!)) { + println("It matches!") + println("hopper block entities: ${config.blockEntities(HOPPERS)}") + } + } + + companion object { + private val HOPPERS = multiblockEntity() + private val FURNACES = multiblockEntity() + + val CONFIG = multiblockConfiguration { + block(MBlocks.MULTIBLOCK_TEST) + + and { + top { + or { + block(Blocks.HOPPER) + block(Blocks.CHEST) + block(BlockTags.PLANKS) + } + + tag(HOPPERS) + } + + left(Blocks.FURNACE) { + tag(FURNACES) + + top { + or { + block(Blocks.HOPPER) + block(Blocks.CHEST) + block(BlockTags.PLANKS) + } + + tag(HOPPERS) + } + } + + right(Blocks.FURNACE) { + tag(FURNACES) + + top { + or { + block(Blocks.HOPPER) + block(Blocks.CHEST) + block(BlockTags.PLANKS) + } + + tag(HOPPERS) + } + } + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt new file mode 100644 index 000000000..b2b571c68 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Multiblock.kt @@ -0,0 +1,619 @@ +package ru.dbotthepony.mc.otm.core + +import com.google.common.collect.ImmutableList +import com.google.common.collect.ImmutableSet +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap +import it.unimi.dsi.fastutil.objects.Object2ObjectFunction +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.ObjectArraySet +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.core.Vec3i +import net.minecraft.tags.TagKey +import net.minecraft.world.level.LevelAccessor +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.Rotation +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import ru.dbotthepony.mc.otm.core.collect.collect +import ru.dbotthepony.mc.otm.core.collect.map +import ru.dbotthepony.mc.otm.core.math.RelativeSide +import ru.dbotthepony.mc.otm.core.math.plus +import java.util.Collections +import java.util.function.Predicate +import kotlin.reflect.KClass + +fun interface BlockPredicate { + fun test(pos: BlockPos, access: LevelAccessor): Boolean + + fun and(other: BlockPredicate): BlockPredicate { + return BlockPredicate { pos, access -> test(pos, access) && other.test(pos, access) } + } + + fun or(other: BlockPredicate): BlockPredicate { + return BlockPredicate { pos, access -> test(pos, access) || other.test(pos, access) } + } + + fun offset(offset: BlockPos): Positioned { + return Positioned(offset, this) + } + + fun offset(offset: Vec3i): Positioned { + return offset(BlockPos(offset)) + } + + data class Positioned(val offset: BlockPos, val parent: BlockPredicate) : BlockPredicate { + override fun test(pos: BlockPos, access: LevelAccessor): Boolean { + return parent.test(offset + pos, access) + } + + override fun offset(offset: BlockPos): Positioned { + return Positioned(this.offset + offset, parent) + } + } + + data class And(val nodes: ImmutableSet) : BlockPredicate { + constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes)) + constructor(nodes: Set) : this(ImmutableSet.copyOf(nodes)) + + override fun test(pos: BlockPos, access: LevelAccessor): Boolean { + return nodes.all { it.test(pos, access) } + } + } + + data class Or(val nodes: ImmutableSet) : BlockPredicate { + constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes)) + constructor(nodes: Set) : this(ImmutableSet.copyOf(nodes)) + + override fun test(pos: BlockPos, access: LevelAccessor): Boolean { + return nodes.any { it.test(pos, access) } + } + } + + object Air : BlockPredicate { + override fun test(pos: BlockPos, access: LevelAccessor): Boolean { + return access.isEmptyBlock(pos) + } + } + + object NotAir : BlockPredicate { + override fun test(pos: BlockPos, access: LevelAccessor): Boolean { + return !access.isEmptyBlock(pos) + } + } +} + +inline fun multiblockConfiguration(configurator: MultiblockBuilder.Node.() -> Unit): MultiblockFactory { + val builder = MultiblockBuilder() + configurator.invoke(builder.root()) + return builder.build() +} + +inline fun multiblockEntity(): MultiblockBuilder.EntityTag { + return MultiblockBuilder.EntityTag(T::class) +} + +class MultiblockBuilder { + private val nodes = Object2ObjectOpenHashMap() + + // not data classes to allow having multiple tags with same target + class EntityTag(val clazz: KClass) : Predicate { + override fun test(t: BlockEntity): Boolean { + return clazz.isInstance(t) + } + } + + enum class Strategy { + OR, AND + } + + @Suppress("unchecked_cast") + abstract class BlockPredicates> { + val predicates = ObjectArraySet() + val children = ObjectArraySet>() + + val blockStateTags = ObjectArraySet() + val blockTags = ObjectArraySet() + val blockEntityTags = ObjectArraySet>() + + fun tagBlockState(value: Any): T { + blockStateTags.add(value) + return this as T + } + + fun tagBlock(value: Any): T { + blockTags.add(value) + return this as T + } + + fun tag(value: EntityTag<*>): T { + blockEntityTags.add(value) + return this as T + } + + fun clear(): T { + predicates.clear() + return this as T + } + + fun predicate(predicate: BlockPredicate): T { + predicates.add(predicate) + return this as T + } + + fun block(block: Block): T { + predicates.add { pos, access -> access.getBlockState(pos).`is`(block) } + return this as T + } + + fun block(block: TagKey): T { + predicates.add { pos, access -> access.getBlockState(pos).`is`(block) } + return this as T + } + + fun air(): T { + predicate(BlockPredicate.Air) + return this as T + } + + fun block(state: BlockState): T { + predicates.add { pos, access -> access.getBlockState(pos) == state } + return this as T + } + + fun and(): And { + return And(this as T) + } + + fun or(): Or { + return Or(this as T) + } + + fun and(configurator: And.() -> Unit): And { + return And(this as T).also(configurator) + } + + fun or(configurator: Or.() -> Unit): Or { + return Or(this as T).also(configurator) + } + + protected fun build(pos: BlockPos): MultiblockFactory.Part { + return MultiblockFactory.Part( + pos, strategy, ImmutableList.copyOf(predicates), + children.stream().map { it.build(pos) }.collect(ImmutableList.toImmutableList()), + ImmutableSet.copyOf(blockStateTags), + ImmutableSet.copyOf(blockTags), + ImmutableSet.copyOf(blockEntityTags), + ) + } + + abstract val strategy: Strategy + } + + class And

>(val parent: P) : BlockPredicates>() { + init { + parent.children.add(this) + } + + fun end() = parent + + override val strategy: Strategy + get() = Strategy.AND + } + + class Or

>(val parent: P) : BlockPredicates>() { + init { + parent.children.add(this) + } + + fun end() = parent + + override val strategy: Strategy + get() = Strategy.OR + } + + /** + * Behaves as if being [Or] node + */ + inner class Node(val pos: BlockPos) : BlockPredicates() { + init { + check(nodes.put(pos, this) == null) { "Trying to create new node at $pos while already having one" } + } + + fun relative(diff: Vec3i): Node { + return node(pos + diff) + } + + fun relative(dir: Direction): Node { + return relative(dir.normal) + } + + fun relative(dir: RelativeSide): Node { + return relative(dir.default) + } + + fun front() = relative(RelativeSide.FRONT) + fun back() = relative(RelativeSide.BACK) + fun left() = relative(RelativeSide.LEFT) + fun right() = relative(RelativeSide.RIGHT) + fun top() = relative(RelativeSide.TOP) + fun bottom() = relative(RelativeSide.BOTTOM) + + fun front(block: Block) = relative(RelativeSide.FRONT).also { it.block(block) } + fun back(block: Block) = relative(RelativeSide.BACK).also { it.block(block) } + fun left(block: Block) = relative(RelativeSide.LEFT).also { it.block(block) } + fun right(block: Block) = relative(RelativeSide.RIGHT).also { it.block(block) } + fun top(block: Block) = relative(RelativeSide.TOP).also { it.block(block) } + fun bottom(block: Block) = relative(RelativeSide.BOTTOM).also { it.block(block) } + + fun front(block: BlockState) = relative(RelativeSide.FRONT).also { it.block(block) } + fun back(block: BlockState) = relative(RelativeSide.BACK).also { it.block(block) } + fun left(block: BlockState) = relative(RelativeSide.LEFT).also { it.block(block) } + fun right(block: BlockState) = relative(RelativeSide.RIGHT).also { it.block(block) } + fun top(block: BlockState) = relative(RelativeSide.TOP).also { it.block(block) } + fun bottom(block: BlockState) = relative(RelativeSide.BOTTOM).also { it.block(block) } + + fun front(predicate: BlockPredicate) = relative(RelativeSide.FRONT).also { it.predicate(predicate) } + fun back(predicate: BlockPredicate) = relative(RelativeSide.BACK).also { it.predicate(predicate) } + fun left(predicate: BlockPredicate) = relative(RelativeSide.LEFT).also { it.predicate(predicate) } + fun right(predicate: BlockPredicate) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) } + fun top(predicate: BlockPredicate) = relative(RelativeSide.TOP).also { it.predicate(predicate) } + fun bottom(predicate: BlockPredicate) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) } + + fun front(configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also(configurator) + fun back(configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also(configurator) + fun left(configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also(configurator) + fun right(configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also(configurator) + fun top(configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also(configurator) + fun bottom(configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also(configurator) + + fun front(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator) + fun back(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator) + fun left(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator) + fun right(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator) + fun top(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator) + fun bottom(block: Block, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator) + + fun front(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.block(block) }.also(configurator) + fun back(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.block(block) }.also(configurator) + fun left(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.block(block) }.also(configurator) + fun right(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.block(block) }.also(configurator) + fun top(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.block(block) }.also(configurator) + fun bottom(block: BlockState, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.block(block) }.also(configurator) + + fun front(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.FRONT).also { it.predicate(predicate) }.also(configurator) + fun back(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BACK).also { it.predicate(predicate) }.also(configurator) + fun left(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.LEFT).also { it.predicate(predicate) }.also(configurator) + fun right(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.RIGHT).also { it.predicate(predicate) }.also(configurator) + fun top(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.TOP).also { it.predicate(predicate) }.also(configurator) + fun bottom(predicate: BlockPredicate, configurator: Node.() -> Unit) = relative(RelativeSide.BOTTOM).also { it.predicate(predicate) }.also(configurator) + + fun build(): MultiblockFactory.Part { + return build(pos) + } + + override var strategy: Strategy = Strategy.OR + } + + fun node(at: BlockPos): Node { + return nodes[at] ?: Node(at) + } + + fun root() = node(BlockPos.ZERO) + fun root(configurator: Node.() -> Unit) = node(BlockPos.ZERO).also(configurator) + + fun copy(): MultiblockBuilder { + val copied = MultiblockBuilder() + + for ((k, v) in nodes) { + val node = copied.Node(k) + copied.nodes[k] = node + node.predicates.addAll(v.predicates) + } + + return copied + } + + fun build(): MultiblockFactory { + return MultiblockFactory(nodes.values.iterator().map { it.build() }.collect(ImmutableSet.toImmutableSet())) + } +} + +class MultiblockFactory(val north: ImmutableSet) { + data class Part( + val pos: BlockPos, + val strategy: MultiblockBuilder.Strategy, + val predicate: ImmutableList, + val children: ImmutableList, + val blockStateTags: ImmutableSet, + val blockTags: ImmutableSet, + val blockEntityTags: ImmutableSet>, + ) + + fun create(pos: BlockPos): Multiblock { + return Multiblock( + pos, + north, + south, + west, + east, + ) + } + + val south: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet()) + val west: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.COUNTERCLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet()) + val east: ImmutableSet = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet()) +} + +class Multiblock( + pos: BlockPos, + north: Collection, + south: Collection, + west: Collection, + east: Collection, +) { + var isValid = false + private set + + private val configurations = ArrayList() + + private class BEList(val tag: MultiblockBuilder.EntityTag) { + val list = ArrayList() + val set = ObjectArraySet() + val setView: Set = Collections.unmodifiableSet(set) + + fun add(blockEntity: BlockEntity) { + if (tag.test(blockEntity)) { + set.add(blockEntity as T) + list.add(blockEntity) + } + } + + fun remove(blockEntity: BlockEntity) { + if (tag.test(blockEntity)) { + check(list.remove(blockEntity as T)) { "Unable to remove $blockEntity from tag $tag" } + + if (blockEntity !in list) { + set.remove(blockEntity) + } + } + } + + fun clear() { + list.clear() + set.clear() + } + } + + private inner class Config(val pos: BlockPos, parts: Collection) { + private inner class Part(val pos: BlockPos, val prototype: MultiblockFactory.Part) { + private var blockEntity: BlockEntity? = null + private var blockState: BlockState? = null + private val assignedBlockEntityLists = ArrayList>(prototype.blockEntityTags.size) + private val assignedBlockStateLists = ArrayList>() + private val assignedBlockLists = ArrayList>() + private val children: ImmutableList = prototype.children.stream().map { Part(pos, it) }.collect(ImmutableList.toImmutableList()) + + init { + prototype.blockEntityTags.forEach { + assignedBlockEntityLists.add(tag2BlockEntity.computeIfAbsent(it, Object2ObjectFunction { _ -> BEList(it as MultiblockBuilder.EntityTag).also { blockEntityLists.add(it) } })) + } + + prototype.blockStateTags.forEach { + assignedBlockStateLists.add(tag2BlockState.computeIfAbsent(it, Object2ObjectFunction { Reference2IntOpenHashMap().also { blockStateLists.add(it) } })) + } + + prototype.blockTags.forEach { + assignedBlockLists.add(tag2Block.computeIfAbsent(it, Object2ObjectFunction { Object2IntOpenHashMap().also { blockLists.add(it) } })) + } + } + + private var lastSuccessfulPathPredicate = -1 + private var lastSuccessfulPathChildren = -1 + + fun test(levelAccessor: LevelAccessor): Boolean { + val test = when (prototype.strategy) { + MultiblockBuilder.Strategy.OR -> { + var status = true + + if (prototype.predicate.isNotEmpty()) { + if (lastSuccessfulPathPredicate != -1 && !prototype.predicate[lastSuccessfulPathPredicate].test(pos, levelAccessor)) { + lastSuccessfulPathPredicate = -1 + } + + if (lastSuccessfulPathPredicate == -1) { + lastSuccessfulPathPredicate = prototype.predicate.indexOfFirst { it.test(pos, levelAccessor) } + status = lastSuccessfulPathPredicate != -1 + } + } + + if (status && children.isNotEmpty()) { + if (lastSuccessfulPathChildren != -1 && !children[lastSuccessfulPathChildren].test(levelAccessor)) { + lastSuccessfulPathChildren = -1 + } + + if (lastSuccessfulPathChildren == -1) { + lastSuccessfulPathChildren = children.indexOfFirst { it.test(levelAccessor) } + status = lastSuccessfulPathChildren != -1 + } + } + + status + } + + MultiblockBuilder.Strategy.AND -> prototype.predicate.all { it.test(pos, levelAccessor) } && children.all { it.test(levelAccessor) } + } + + if (test) { + if (assignedBlockEntityLists.isNotEmpty()) { + val be1 = blockEntity + val be2 = levelAccessor.getBlockEntity(pos) + + if (be1 != be2) { + if (be1 != null) { + assignedBlockEntityLists.forEach { it.remove(be1) } + } + + if (be2 != null) { + assignedBlockEntityLists.forEach { it.add(be2) } + } + + blockEntity = be2 + } + } + + if (assignedBlockStateLists.isNotEmpty() || assignedBlockLists.isNotEmpty()) { + val state = levelAccessor.getBlockState(pos) + val old = blockState + + if (state != old) { + if (old != null) { + assignedBlockStateLists.forEach { + it[old] = it.getInt(old) - 1 + check(it.getInt(old) >= 0) { "Counter for block state $old turned negative" } + } + + assignedBlockLists.forEach { + it[old.block] = it.getInt(old.block) - 1 + check(it.getInt(old.block) >= 0) { "Counter for block ${old.block.registryName} turned negative" } + } + } + + assignedBlockStateLists.forEach { + it[state] = it.getInt(state) + 1 + } + + assignedBlockLists.forEach { + it[state.block] = it.getInt(state.block) + 1 + } + + blockState = state + } + } + } else { + clearFull() + } + + return test + } + + private fun clearFull() { + val blockEntity = blockEntity + + if (blockEntity != null) { + assignedBlockEntityLists.forEach { it.remove(blockEntity) } + } + + val blockState = blockState + + if (blockState != null) { + assignedBlockStateLists.forEach { + it[blockState] = it.getInt(blockState) - 1 + check(it.getInt(blockState) >= 0) { "Counter for block state $blockState turned negative" } + } + + assignedBlockLists.forEach { + it[blockState.block] = it.getInt(blockState.block) - 1 + check(it.getInt(blockState.block) >= 0) { "Counter for block ${blockState.block.registryName} turned negative" } + } + } + + this.blockEntity = null + this.blockState = null + + lastSuccessfulPathPredicate = -1 + lastSuccessfulPathChildren = -1 + + // avoid allocating iterator when empty + if (children.isNotEmpty()) + children.forEach { it.clearFull() } + } + + fun clear() { + blockEntity = null + blockState = null + + lastSuccessfulPathPredicate = -1 + lastSuccessfulPathChildren = -1 + + children.forEach { it.clear() } + } + } + + private val tag2BlockEntity = Object2ObjectOpenHashMap, BEList<*>>() + private val tag2BlockState = Object2ObjectOpenHashMap>() + private val tag2Block = Object2ObjectOpenHashMap>() + + private val blockEntityLists = ArrayList>() + private val blockStateLists = ArrayList>() + private val blockLists = ArrayList>() + + private val parts: ImmutableSet = parts.stream().map { Part(it.pos + pos, it) }.collect(ImmutableSet.toImmutableSet()) + + fun clear() { + blockEntityLists.forEach { it.clear() } + blockStateLists.forEach { it.clear() } + blockLists.forEach { it.clear() } + parts.forEach { it.clear() } + } + + fun update(levelAccessor: LevelAccessor): Boolean { + var valid = true + + for (part in parts) { + valid = part.test(levelAccessor) + if (!valid) break + } + + if (!valid) + clear() + + return valid + } + + fun blockEntities(tag: MultiblockBuilder.EntityTag): Set { + return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set + } + } + + private val north = Config(pos, north).also { configurations.add(it) } + private val south = Config(pos, south).also { configurations.add(it) } + private val west = Config(pos, west).also { configurations.add(it) } + private val east = Config(pos, east).also { configurations.add(it) } + + private var activeConfig: Config? = null + + fun blockEntities(tag: MultiblockBuilder.EntityTag): Set { + return activeConfig?.blockEntities(tag) ?: setOf() + } + + fun update(levelAccessor: LevelAccessor): Boolean { + val activeConfig = activeConfig + + if (activeConfig != null && activeConfig.update(levelAccessor)) { + return true + } else if (activeConfig != null) { + for (config in configurations) { + if (config !== activeConfig && config.update(levelAccessor)) { + this.activeConfig = config + return true + } + } + + this.activeConfig = null + this.isValid = false + return false + } else { + for (config in configurations) { + if (config.update(levelAccessor)) { + this.activeConfig = config + this.isValid = true + return true + } + } + + return false + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt index 1985f0925..fee43c2be 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/math/BlockRotation.kt @@ -1,6 +1,5 @@ package ru.dbotthepony.mc.otm.core.math -import com.google.common.collect.ImmutableMap import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.core.Vec3i @@ -27,8 +26,8 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos { return this + other.normal } -enum class RelativeSide { - FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM +enum class RelativeSide(val default: Direction) { + FRONT(Direction.NORTH), BACK(Direction.SOUTH), LEFT(Direction.WEST), RIGHT(Direction.EAST), TOP(Direction.UP), BOTTOM(Direction.DOWN) } /** diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt index fea6ebb36..4db354925 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlockEntities.kt @@ -44,6 +44,10 @@ object MBlockEntities { return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) } } + private fun register(name: String, factory: BlockEntityType.BlockEntitySupplier, blocks: Supplier): MDeferredRegister<*>.Entry> { + return registry.register(name) { BlockEntityType.Builder.of(factory, blocks.get()).build(null) } + } + private fun register(name: String, factory: BlockEntityType.BlockEntitySupplier, blocks: Map<*, Block>): MDeferredRegister<*>.Entry> { return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.values.toTypedArray()).build(null) } } @@ -87,6 +91,8 @@ object MBlockEntities { val POWERED_BLAST_FURNACE by register(MNames.POWERED_BLAST_FURNACE, ::PoweredBlastFurnaceBlockEntity, MBlocks.POWERED_BLAST_FURNACE) val POWERED_SMOKER by register(MNames.POWERED_SMOKER, ::PoweredSmokerBlockEntity, MBlocks.POWERED_SMOKER) + val MULTIBLOCK_TEST by register("multiblock_test", ::MultiblockTestBlockEntity, MBlocks::MULTIBLOCK_TEST) + val ENERGY_CABLES: Map> = SupplierMap(CablesConfig.E.entries.map { conf -> var selfFeed: Supplier> = Supplier { TODO() } selfFeed = register("${conf.name.lowercase()}_energy_cable", { a, b -> SimpleEnergyCableBlockEntity(selfFeed.get(), a, b, conf) }) as Supplier> diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt index f384631cf..b2539b7b2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MBlocks.kt @@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.block.BlockSphereDebugger import ru.dbotthepony.mc.otm.block.EnergyCableBlock import ru.dbotthepony.mc.otm.block.MatterCableBlock import ru.dbotthepony.mc.otm.block.MatteryBlock +import ru.dbotthepony.mc.otm.block.MultiblockTestBlock import ru.dbotthepony.mc.otm.block.StorageCableBlock import ru.dbotthepony.mc.otm.block.addSimpleDescription import ru.dbotthepony.mc.otm.block.decorative.DevChestBlock @@ -253,6 +254,8 @@ object MBlocks { .strength(3f) ) } + val MULTIBLOCK_TEST by registry.register("multiblock_test") { MultiblockTestBlock() } + init { MRegistry.registerBlocks(registry) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index 04411e1ea..96dc11e0a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -160,6 +160,7 @@ object MItems { val DEBUG_EXPLOSION_SMALL: Item by registry.register(MNames.DEBUG_EXPLOSION_SMALL) { BlockItem(MBlocks.DEBUG_EXPLOSION_SMALL, Item.Properties().stacksTo(64)) } val DEBUG_SPHERE_POINTS: Item by registry.register(MNames.DEBUG_SPHERE_POINTS) { BlockItem(MBlocks.DEBUG_SPHERE_POINTS, Item.Properties().stacksTo(64)) } + val MULTIBLOCK_TEST by registry.register("multiblock_test") { BlockItem(MBlocks.MULTIBLOCK_TEST, Properties().stacksTo(64)) } val TRITANIUM_ANVIL: List