This commit is contained in:
DBotThePony 2024-01-22 11:45:00 +07:00
commit 0b7e54b4ef
Signed by: DBot
GPG Key ID: DCC23B5715498507
15 changed files with 906 additions and 10 deletions

View File

@ -0,0 +1,24 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.world.level.block.entity.BlockEntity;
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 ru.dbotthepony.mc.otm.core.MultiblockKt;
// because i know
// someone
// somewhere
// will try to break the system
// either on purpose or accidentally because of other mod
@Mixin(BlockEntity.class)
public abstract class BlockEntityMixin {
@Inject(
at = @At("TAIL"),
method = "setRemoved()V"
)
public void setRemovedListener(CallbackInfo ci) {
MultiblockKt.onBlockEntityInvalidated((BlockEntity) (Object) this);
}
}

View File

@ -0,0 +1,40 @@
package ru.dbotthepony.mc.otm.mixin;
import net.minecraft.client.resources.SplashManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.util.profiling.ProfilerFiller;
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.List;
@Mixin(SplashManager.class)
public abstract class SplashManagerMixin {
@Shadow
public List<String> splashes;
@Inject(
at = @At("TAIL"),
method = "apply(Ljava/util/List;Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/util/profiling/ProfilerFiller;)V"
)
public void otmSplashes(List<String> splashes, ResourceManager p_118879_, ProfilerFiller p_118880_, CallbackInfo ci) {
this.splashes.add("42 is the answer!");
this.splashes.add("Now with matter!");
this.splashes.add("Now with four dimensions!");
this.splashes.add("Now with more ways to slice Creepers!");
this.splashes.add("Batteries not included!");
this.splashes.add("As it was 13 nanoseconds ago!");
this.splashes.add("Smart AI is smart enough to fail Turing test!");
this.splashes.add("Neural computing!");
this.splashes.add("Swimming through quantum field!");
this.splashes.add("For biological and digital alike!");
this.splashes.add("Digital is the next form of Biological!");
this.splashes.add("Also try Starbound!");
this.splashes.add("Also try No Man's Sky!");
this.splashes.add("Also try Factorio!");
}
}

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

@ -66,7 +66,7 @@ class PainterBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDe
}
override fun fill(resource: FluidStack, action: IFluidHandler.FluidAction): Int {
if (resource.isEmpty || resource.fluid != Fluids.WATER || waterStored() >= MAX_WATER_STORAGE) return 0
if (resource.amount <= 0 || resource.fluid != Fluids.WATER || waterStored() >= MAX_WATER_STORAGE) return 0
val diff = ((waterStored().toLong() + resource.amount.toLong()).coerceAtMost(MAX_WATER_STORAGE.toLong()) - waterStored().toLong()).toInt()
if (action.simulate() || diff == 0) return diff
dyeStored[null] = waterStored() + diff

View File

@ -143,8 +143,8 @@ sealed class AbstractPoweredFurnaceBlockEntity<P : AbstractCookingRecipe, S : Ma
return JobContainer.success(
ItemJob(
recipe.getResultItem(level.registryAccess()).copyWithCount(toProcess),
recipe.workTime * MachinesConfig.PLATE_PRESS.workTimeMultiplier,
MachinesConfig.PLATE_PRESS.energyConsumption * toProcess,
recipe.workTime * config.workTimeMultiplier,
config.energyConsumption * toProcess,
experience = recipe.experience.sample(level.random) * toProcess))
}
}

View File

@ -157,8 +157,8 @@ object Widgets18 {
}
}
val LEFT_CONTROLS = SideControls()
val RIGHT_CONTROLS = SideControls()
val LEFT_CONTROLS = SideControls()
val TOP_CONTROLS = SideControls()
val BOTTOM_CONTROLS = SideControls()
val FRONT_CONTROLS = SideControls()

View File

@ -16,6 +16,7 @@ import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.container.util.IContainerSlot
import ru.dbotthepony.mc.otm.container.util.containerSlot
import ru.dbotthepony.mc.otm.container.util.iterator
import ru.dbotthepony.mc.otm.container.util.slotIterator
import ru.dbotthepony.mc.otm.core.collect.concatIterators
import ru.dbotthepony.mc.otm.core.collect.filter
import ru.dbotthepony.mc.otm.core.collect.flatMap
@ -154,6 +155,9 @@ class CombinedContainer(containers: Stream<Pair<Container, Iterable<Int>>>) : IM
}
override fun iterator(nonEmpty: Boolean): Iterator<ItemStack> {
if (notFullCoverage.isEmpty())
return fullCoverage.iterator().flatMap { it.iterator(nonEmpty) }
return concatIterators(
fullCoverage.iterator().flatMap { it.iterator(nonEmpty) },
notFullCoverage.values.iterator().flatMap { it.iterator() }.map { it.item }.let { if (nonEmpty) it.filter { it.isNotEmpty } else it }

View File

@ -66,6 +66,44 @@ interface ISubscriptable<V> {
}
}
interface IUnitSubscripable : ISubscriptable<Unit> {
fun addListener(listener: Runnable): ISubscriptable.L
override fun addListener(listener: Consumer<Unit>): ISubscriptable.L {
return addListener(Runnable { listener.accept(Unit) })
}
class Impl : IUnitSubscripable, Runnable {
private inner class L(val callback: Runnable) : ISubscriptable.L {
private var isRemoved = false
init {
subscribers.add(this)
}
override fun remove() {
if (!isRemoved) {
isRemoved = true
queue.add(this)
}
}
}
private val subscribers = ReferenceLinkedOpenHashSet<L>(0)
private val queue = ReferenceArraySet<L>(0)
override fun addListener(listener: Runnable): ISubscriptable.L {
return L(listener)
}
override fun run() {
queue.forEach { subscribers.remove(it) }
queue.clear()
subscribers.forEach { it.callback.run() }
}
}
}
interface IFloatSubcripable : ISubscriptable<Float> {
@Deprecated("Use type specific listener")
override fun addListener(listener: Consumer<Float>): ISubscriptable.L {

View File

@ -0,0 +1,681 @@
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.Level
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.WeakHashSet
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.WeakHashMap
import java.util.function.Predicate
import kotlin.reflect.KClass
private val blockEntityListeners = WeakHashMap<Level, WeakHashMap<BlockEntity, WeakHashSet<Multiblock>>>()
fun onBlockEntityInvalidated(blockEntity: BlockEntity) {
blockEntityListeners[blockEntity.level]?.get(blockEntity)?.forEach { it.blockEntityRemoved(blockEntity) }
blockEntityListeners[blockEntity.level]?.remove(blockEntity)
}
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 inner 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)) {
if (set.add(blockEntity as T)) {
blockEntityListeners
.computeIfAbsent(blockEntity.level) { WeakHashMap() }
.computeIfAbsent(blockEntity) { WeakHashSet() }
.add(this@Multiblock)
}
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)
val getSet = blockEntityListeners[blockEntity.level]?.get(blockEntity)
if (getSet != null) {
getSet.remove(this@Multiblock)
if (getSet.isEmpty()) {
blockEntityListeners[blockEntity.level]?.remove(blockEntity)
}
}
}
}
}
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
if (blockEntity in list) {
while (list.remove(blockEntity)) {}
set.remove(blockEntity)
return true
}
return false
}
fun clear() {
set.forEach {
val getSet = checkNotNull(blockEntityListeners[it.level]) { "Consistency check failed: No subscriber lists for level ${it.level}" }.get(it)
checkNotNull(getSet) { "Consistency check failed: No subscriber list for $it" }
check(getSet.remove(this@Multiblock)) { "Consistency check failed: Can't remove ${this@Multiblock} from $it subscriber list" }
if (getSet.isEmpty()) {
blockEntityListeners[it.level]?.remove(it)
}
}
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) { "Consistency check failed: Counter for block state $old turned negative" }
}
assignedBlockLists.forEach {
it[old.block] = it.getInt(old.block) - 1
check(it.getInt(old.block) >= 0) { "Consistency check failed: 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) { "Consistency check failed: Counter for block state $blockState turned negative" }
}
assignedBlockLists.forEach {
it[blockState.block] = it.getInt(blockState.block) - 1
check(it.getInt(blockState.block) >= 0) { "Consistency check failed: 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>
}
fun blockEntityRemoved(blockEntity: BlockEntity): Boolean {
var any = false
blockEntityLists.forEach {
any = it.blockEntityRemoved(blockEntity) || any
}
return any
}
}
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> {
if (!isValid) return emptySet()
return activeConfig?.blockEntities(tag) ?: setOf()
}
fun blockEntityRemoved(blockEntity: BlockEntity) {
activeConfig?.blockEntityRemoved(blockEntity)
}
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
@ -228,7 +229,7 @@ object MBlocks {
.sound(SoundType.BASALT)
.requiresCorrectToolForDrops()
.explosionResistance(80f)
.strength(4f)
.destroyTime(2.5f)
) }
val TRITANIUM_STRIPED_STAIRS: Block by registry.register(MNames.TRITANIUM_STRIPED_STAIRS) { StairBlock(
@ -250,9 +251,11 @@ object MBlocks {
.sound(SoundType.BASALT)
.requiresCorrectToolForDrops()
.explosionResistance(40f)
.strength(3f)
.destroyTime(1.5f)
) }
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>

View File

@ -6,6 +6,7 @@
"minVersion": "0.8",
"refmap": "overdrive_that_matters.refmap.json",
"mixins": [
"BlockEntityMixin",
"EnchantmentHelperMixin",
"FoodDataMixin",
"MixinPatchProjectileFinder",
@ -22,6 +23,7 @@
],
"client": [
"MixinGameRenderer",
"MixinMinecraft"
"MixinMinecraft",
"SplashManagerMixin"
]
}