Multiblock test

This commit is contained in:
DBotThePony 2024-01-17 13:15:01 +07:00
parent 94afd4f19a
commit f97ad565d4
Signed by: DBot
GPG Key ID: DCC23B5715498507
7 changed files with 729 additions and 3 deletions

View File

@ -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 <T : BlockEntity?> getTicker(level: Level, blockState: BlockState, blockEntityType: BlockEntityType<T>): BlockEntityTicker<T>? {
if (level.isClientSide)
return null
return BlockEntityTicker { _, _, _, tile -> if (tile is MultiblockTestBlockEntity) tile.tick() }
}
}

View File

@ -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<HopperBlockEntity>()
private val FURNACES = multiblockEntity<FurnaceBlockEntity>()
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)
}
}
}
}
}
}

View File

@ -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>) : BlockPredicate {
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
constructor(nodes: Set<BlockPredicate>) : 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>) : BlockPredicate {
constructor(vararg nodes: BlockPredicate) : this(ImmutableSet.copyOf(nodes))
constructor(nodes: Set<BlockPredicate>) : 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 <reified T : BlockEntity> multiblockEntity(): MultiblockBuilder.EntityTag<T> {
return MultiblockBuilder.EntityTag(T::class)
}
class MultiblockBuilder {
private val nodes = Object2ObjectOpenHashMap<BlockPos, Node>()
// not data classes to allow having multiple tags with same target
class EntityTag<T : BlockEntity>(val clazz: KClass<T>) : Predicate<BlockEntity> {
override fun test(t: BlockEntity): Boolean {
return clazz.isInstance(t)
}
}
enum class Strategy {
OR, AND
}
@Suppress("unchecked_cast")
abstract class BlockPredicates<T : BlockPredicates<T>> {
val predicates = ObjectArraySet<BlockPredicate>()
val children = ObjectArraySet<BlockPredicates<*>>()
val blockStateTags = ObjectArraySet<Any>()
val blockTags = ObjectArraySet<Any>()
val blockEntityTags = ObjectArraySet<EntityTag<*>>()
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<Block>): 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<T> {
return And(this as T)
}
fun or(): Or<T> {
return Or(this as T)
}
fun and(configurator: And<T>.() -> Unit): And<T> {
return And(this as T).also(configurator)
}
fun or(configurator: Or<T>.() -> Unit): Or<T> {
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<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<And<P>>() {
init {
parent.children.add(this)
}
fun end() = parent
override val strategy: Strategy
get() = Strategy.AND
}
class Or<P : BlockPredicates<P>>(val parent: P) : BlockPredicates<Or<P>>() {
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<Node>() {
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<Part>) {
data class Part(
val pos: BlockPos,
val strategy: MultiblockBuilder.Strategy,
val predicate: ImmutableList<BlockPredicate>,
val children: ImmutableList<Part>,
val blockStateTags: ImmutableSet<Any>,
val blockTags: ImmutableSet<Any>,
val blockEntityTags: ImmutableSet<MultiblockBuilder.EntityTag<*>>,
)
fun create(pos: BlockPos): Multiblock {
return Multiblock(
pos,
north,
south,
west,
east,
)
}
val south: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_180)) }.collect(ImmutableSet.toImmutableSet())
val west: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.COUNTERCLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
val east: ImmutableSet<Part> = north.iterator().map { it.copy(pos = it.pos.rotate(Rotation.CLOCKWISE_90)) }.collect(ImmutableSet.toImmutableSet())
}
class Multiblock(
pos: BlockPos,
north: Collection<MultiblockFactory.Part>,
south: Collection<MultiblockFactory.Part>,
west: Collection<MultiblockFactory.Part>,
east: Collection<MultiblockFactory.Part>,
) {
var isValid = false
private set
private val configurations = ArrayList<Config>()
private class BEList<T : BlockEntity>(val tag: MultiblockBuilder.EntityTag<T>) {
val list = ArrayList<T>()
val set = ObjectArraySet<T>()
val setView: Set<T> = 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<MultiblockFactory.Part>) {
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<BEList<*>>(prototype.blockEntityTags.size)
private val assignedBlockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>()
private val assignedBlockLists = ArrayList<Object2IntOpenHashMap<Block>>()
private val children: ImmutableList<Part> = 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<BlockEntity>).also { blockEntityLists.add(it) } }))
}
prototype.blockStateTags.forEach {
assignedBlockStateLists.add(tag2BlockState.computeIfAbsent(it, Object2ObjectFunction { Reference2IntOpenHashMap<BlockState>().also { blockStateLists.add(it) } }))
}
prototype.blockTags.forEach {
assignedBlockLists.add(tag2Block.computeIfAbsent(it, Object2ObjectFunction { Object2IntOpenHashMap<Block>().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<MultiblockBuilder.EntityTag<*>, BEList<*>>()
private val tag2BlockState = Object2ObjectOpenHashMap<Any, Reference2IntOpenHashMap<BlockState>>()
private val tag2Block = Object2ObjectOpenHashMap<Any, Object2IntOpenHashMap<Block>>()
private val blockEntityLists = ArrayList<BEList<*>>()
private val blockStateLists = ArrayList<Reference2IntOpenHashMap<BlockState>>()
private val blockLists = ArrayList<Object2IntOpenHashMap<Block>>()
private val parts: ImmutableSet<Part> = 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 <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
return (tag2BlockEntity[tag]?.setView ?: setOf()) as Set<T>
}
}
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 <T : BlockEntity> blockEntities(tag: MultiblockBuilder.EntityTag<T>): Set<T> {
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
}
}
}

View File

@ -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)
}
/**

View File

@ -44,6 +44,10 @@ object MBlockEntities {
return registry.register(name) { BlockEntityType.Builder.of(factory, *blocks.map { it.get() }.toTypedArray()).build(null) }
}
private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Supplier<Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
return registry.register(name) { BlockEntityType.Builder.of(factory, blocks.get()).build(null) }
}
private fun <T : BlockEntity> register(name: String, factory: BlockEntityType.BlockEntitySupplier<T>, blocks: Map<*, Block>): MDeferredRegister<*>.Entry<BlockEntityType<T>> {
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<CablesConfig.E, BlockEntityType<*>> = SupplierMap(CablesConfig.E.entries.map { conf ->
var selfFeed: Supplier<BlockEntityType<*>> = Supplier { TODO() }
selfFeed = register("${conf.name.lowercase()}_energy_cable", { a, b -> SimpleEnergyCableBlockEntity(selfFeed.get(), a, b, conf) }) as Supplier<BlockEntityType<*>>

View File

@ -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)
}

View File

@ -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<BlockItem>