diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTables.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTables.kt index bc9d7b1a0..a57b06bd1 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTables.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTables.kt @@ -26,7 +26,7 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider import net.minecraft.world.level.storage.loot.providers.number.ConstantValue -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity data class NbtCopy(val source: String, val destination: String, val strategy: CopyNbtFunction.MergeStrategy = CopyNbtFunction.MergeStrategy.REPLACE) @@ -36,13 +36,13 @@ fun TileNbtCopy(source: String, strategy: CopyNbtFunction.MergeStrategy = CopyNb } private val basicTags = arrayOf( - TileNbtCopy(MatteryBlockEntity.REDSTONE_CONTROL_KEY), + TileNbtCopy(MatteryDeviceBlockEntity.REDSTONE_CONTROL_KEY), ) private val poweredTags = arrayOf( *basicTags, - TileNbtCopy(MatteryBlockEntity.ENERGY_KEY), - TileNbtCopy(MatteryBlockEntity.BATTERY_KEY), + TileNbtCopy(MatteryDeviceBlockEntity.ENERGY_KEY), + TileNbtCopy(MatteryDeviceBlockEntity.BATTERY_KEY), ) private val workerTags = arrayOf( @@ -53,7 +53,7 @@ private val workerTags = arrayOf( private val poweredMatterWorker = arrayOf( *workerTags, - TileNbtCopy(MatteryBlockEntity.MATTER_STORAGE_KEY), + TileNbtCopy(MatteryDeviceBlockEntity.MATTER_STORAGE_KEY), ) class LootTables(generator: DataGenerator) : LootTableProvider(generator.packOutput, setOf() /* because we don't fucking validate you fuck */, listOf() /* because we attach everything after class is constructed duh */) { diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt index a6321136b..5b5548bb8 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/loot/LootTablesData.kt @@ -8,9 +8,9 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition import ru.dbotthepony.mc.otm.block.entity.tech.ChemicalGeneratorBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.ENERGY_KEY -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.MATTER_STORAGE_KEY -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.REDSTONE_CONTROL_KEY +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion.ENERGY_KEY +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion.MATTER_STORAGE_KEY +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.Companion.REDSTONE_CONTROL_KEY import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterBottlerBlockEntity import ru.dbotthepony.mc.otm.block.entity.storage.AbstractStorageImportExport.Companion.FILTER_KEY diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 6e92cacc6..e4d26df10 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -17,7 +17,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import ru.dbotthepony.mc.otm.android.AndroidResearchManager; import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature; -import ru.dbotthepony.mc.otm.block.entity.SynchronizedBlockEntity; +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity; import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue; import ru.dbotthepony.mc.otm.capability.MatteryCapability; import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability; @@ -170,12 +170,12 @@ public final class OverdriveThatMatters { EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::onDataPackSync); EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::addCommands); - EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStopping); - EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onLevelUnload); - EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch); - EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onForget); - EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::playerDisconnected); - EVENT_BUS.addListener(EventPriority.LOWEST, SynchronizedBlockEntity.Companion::postLevelTick); + EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onServerStopping); + EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onLevelUnload); + EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onWatch); + EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onForget); + EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::playerDisconnected); + EVENT_BUS.addListener(EventPriority.LOWEST, MatteryBlockEntity.Companion::postLevelTick); EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath); EVENT_BUS.addListener(EventPriority.HIGH, ItemTritaniumArmor.Companion::onHurt); diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java index 958ece5b3..4b182e74a 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java +++ b/src/main/java/ru/dbotthepony/mc/otm/capability/MatteryCapability.java @@ -1,7 +1,10 @@ package ru.dbotthepony.mc.otm.capability; import mekanism.api.energy.IStrictEnergyHandler; -import net.minecraftforge.common.capabilities.*; +import net.minecraftforge.common.capabilities.CapabilityManager; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityToken; +import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent; import org.jetbrains.annotations.NotNull; import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive; import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage; diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt index a6843199c..1c271cb0b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/MatteryBlock.kt @@ -23,8 +23,8 @@ import net.minecraft.world.level.material.Material import net.minecraft.world.level.material.MaterialColor import net.minecraft.world.phys.BlockHitResult import net.minecraft.world.phys.shapes.VoxelShape -import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlProvider -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom @@ -69,7 +69,7 @@ abstract class MatteryBlock @JvmOverloads constructor( if (this is EntityBlock && itemStack.hasCustomHoverName() && !level.isClientSide) { val tile = level.getBlockEntity(blockPos) - if (tile is MatteryBlockEntity) + if (tile is MatteryDeviceBlockEntity) tile.customDisplayName = itemStack.displayName } @@ -172,7 +172,7 @@ abstract class MatteryBlock @JvmOverloads constructor( if (this is EntityBlock && !level.isClientSide) { val tile = level.getBlockEntity(pos) - if (tile is IRedstoneControlProvider) + if (tile is IRedstoneControlled) tile.redstoneControl.redstoneSignal = level.getBestNeighborSignal(pos) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index 31fe08a0a..10e7117ba 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -1,43 +1,404 @@ package ru.dbotthepony.mc.otm.block.entity -import net.minecraft.world.level.block.entity.BlockEntityType -import net.minecraft.core.BlockPos -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.MenuProvider -import java.lang.Runnable -import net.minecraft.server.level.ServerLevel +import it.unimi.dsi.fastutil.longs.Long2ObjectFunction +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap +import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap +import it.unimi.dsi.fastutil.objects.ReferenceArraySet import net.minecraft.client.multiplayer.ClientLevel +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction import net.minecraft.core.SectionPos -import net.minecraft.world.entity.player.Inventory -import net.minecraft.world.entity.player.Player -import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.StringTag -import net.minecraft.network.chat.Component +import net.minecraft.nbt.Tag +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.level.ChunkPos import net.minecraft.world.level.Level +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity -import ru.dbotthepony.mc.otm.core.TextComponent -import ru.dbotthepony.mc.otm.core.nbt.ifHas +import net.minecraft.world.level.block.state.BlockState +import net.minecraftforge.common.capabilities.Capability +import net.minecraftforge.common.capabilities.ForgeCapabilities +import net.minecraftforge.common.util.INBTSerializable +import net.minecraftforge.common.util.LazyOptional +import net.minecraftforge.event.TickEvent +import net.minecraftforge.event.TickEvent.LevelTickEvent +import net.minecraftforge.event.entity.player.PlayerEvent +import net.minecraftforge.event.level.ChunkWatchEvent +import net.minecraftforge.event.level.LevelEvent +import net.minecraftforge.event.server.ServerStoppingEvent +import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock +import ru.dbotthepony.mc.otm.capability.MatteryCapability +import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.core.forValidRefs +import ru.dbotthepony.mc.otm.core.get +import ru.dbotthepony.mc.otm.core.math.BlockRotation +import ru.dbotthepony.mc.otm.core.math.RelativeSide import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket +import ru.dbotthepony.mc.otm.network.FieldSynchronizer +import ru.dbotthepony.mc.otm.network.WorldNetworkChannel import ru.dbotthepony.mc.otm.oncePre +import ru.dbotthepony.mc.otm.onceServer +import java.lang.ref.WeakReference +import java.util.WeakHashMap +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KMutableProperty0 +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty0 -abstract class MatteryBlockEntity( - p_155228_: BlockEntityType<*>, - p_155229_: BlockPos, - p_155230_: BlockState -) : SynchronizedBlockEntity(p_155228_, p_155229_, p_155230_), MenuProvider, IRedstoneControlProvider { - var customDisplayName: Component? = null - override val redstoneControl: AbstractRedstoneControl = RedstoneControl { new, old -> - if (new != old) - redstoneStatusUpdated(new, old) - else - setChangedLight() +/** + * Absolute barebone block entity class in Overdrive that Matters, providing bare minimum functionality + */ +abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) { + private var isSynchronizing = false + + /** + * "shortcut" for getting [BlockRotation] + * + * if block has no rotation, returns [BlockRotation.NORTH] + */ + open val blockRotation: BlockRotation get() { + return blockState[(blockState.block as? RotatableMatteryBlock ?: return BlockRotation.NORTH).rotationProperty] } - protected open val defaultDisplayName: Component - get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos") + // Capabilities - abstract override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? + private val caps = Reference2ObjectArrayMap, CapInstance<*, *>>() + + fun getCapInstance(index: Capability): CapInstance? { + return caps[index] as CapInstance? + } + + protected fun energy(capability: T): StaticCap { + return StaticCap(MatteryCapability.ENERGY, "energy", capability).alias(ForgeCapabilities.ENERGY) + } + + abstract inner class CapInstance>(type: Capability, val name: String) : INBTSerializable { + private val types = ReferenceArraySet>() + protected var valid = true + abstract val capability: T + + protected var optional by object : ReadWriteProperty> { + private var value: LazyOptional? = null + + override fun getValue(thisRef: Any?, property: KProperty<*>): LazyOptional { + if (value == null) + value = if (valid) LazyOptional.of { capability } else LazyOptional.empty() + + return value!! + } + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: LazyOptional) { + this.value = value + } + } + + init { + check(!caps.values.any { it.name == name }) { "Already has capability with name $name!" } + check(!caps.containsKey(type)) { "Already has capability with type $type!" } + caps[type] = this + types.add(type) + } + + fun alias(type: Capability): S { + if (type !in types) { + check(!caps.containsKey(type)) { "Already has capability with type $type!" } + caps[type] = this + types.add(type) + } + + return this as S + } + + override fun toString(): String { + return "${this@MatteryBlockEntity} at $blockPos CapInstance[name = $name, capability = $capability]" + } + + override fun serializeNBT(): CompoundTag { + return CompoundTag().also { + var sides = 0 + + for (i in 0 .. 5) + if (exposedSides[i].first) + sides = sides.or(1 shl (i + 1)) + + it["sides"] = sides.toShort() + + if (capability is INBTSerializable<*>) + it["capability"] = (capability as INBTSerializable).serializeNBT() + } + } + + override fun deserializeNBT(nbt: CompoundTag) { + var sides = 0 + + if (nbt.contains("sides")) { + sides = nbt.getInt("sides") + } + + for (i in 0 .. 5) { + val isExposed = sides and (1 shl (1 + i)) == 1 + + if (isExposed && valid) { + exposedSides[i] = true to LazyOptional.of { capability } + } else { + exposedSides[i] = isExposed to LazyOptional.empty() + } + } + + val tag = nbt["capability"] + + if (tag != null && capability is INBTSerializable<*>) { + // will throw classcastexception if serialized type is wrong + (capability as INBTSerializable).deserializeNBT(tag) + } + } + + protected val exposedSides = Array>>(6) { true to LazyOptional.of { capability } } + + fun exposeAt(side: RelativeSide): S { + if (!exposedSides[side.ordinal].first) { + if (valid) { + setChanged() + exposedSides[side.ordinal] = true to LazyOptional.of { capability } + } else { + exposedSides[side.ordinal] = true to LazyOptional.empty() + } + } + + return this as S + } + + fun closeAt(side: RelativeSide): S { + if (exposedSides[side.ordinal].first) { + exposedSides[side.ordinal].second.invalidate() + exposedSides[side.ordinal] = false to LazyOptional.empty() + } + + return this as S + } + + fun closeAll(): S { + for (side in RelativeSide.values()) + closeAt(side) + + return this as S + } + + fun exposeAll(): S { + for (side in RelativeSide.values()) + exposeAt(side) + + return this as S + } + + fun exposeFront() = exposeAt(RelativeSide.FRONT) + fun exposeBack() = exposeAt(RelativeSide.BACK) + fun exposeLeft() = exposeAt(RelativeSide.LEFT) + fun exposeRight() = exposeAt(RelativeSide.RIGHT) + fun exposeTop() = exposeAt(RelativeSide.TOP) + fun exposeBottom() = exposeAt(RelativeSide.BOTTOM) + + fun closeFront() = closeAt(RelativeSide.FRONT) + fun closeBack() = closeAt(RelativeSide.BACK) + fun closeLeft() = closeAt(RelativeSide.LEFT) + fun closeRight() = closeAt(RelativeSide.RIGHT) + fun closeTop() = closeAt(RelativeSide.TOP) + fun closeBottom() = closeAt(RelativeSide.BOTTOM) + + fun isExposedAt(side: RelativeSide): Boolean { + return exposedSides[side.ordinal].first + } + + fun isExposedAt(side: Direction): Boolean { + return isExposedAt(blockRotation.dir2Side(side)) + } + + fun get(side: Direction? = null): LazyOptional { + if (!valid) { + return LazyOptional.empty() + } + + if (side == null) { + return optional + } else { + return exposedSides[blockRotation.dir2Side(side).ordinal].second + } + } + + fun invalidate() { + if (valid) { + for ((_, v) in exposedSides) + v.invalidate() + + optional.invalidate() + valid = false + } + } + + fun revive() { + if (!valid) { + for ((i, pair) in exposedSides.withIndex()) + if (pair.first) + exposedSides[i] = true to LazyOptional.of { capability } + + optional = LazyOptional.of { capability } + valid = true + } + } + } + + inner class StaticCap(type: Capability, name: String, override val capability: T) : CapInstance>(type, name) + + inner class DynamicCap(type: Capability, name: String, capability: T) : CapInstance>(type, name) { + override var capability: T = capability + private set(value) { + field = value + + if (valid) { + val old = ArrayList>(7) + old.add(optional) + val new = LazyOptional.of { value } + optional = new + + for ((i, pair) in exposedSides.withIndex()) { + if (pair.first) { + old.add(pair.second) + exposedSides[i] = true to LazyOptional.of { value } + } + } + + old.forEach(LazyOptional::invalidate) + } + } + } + + // /Capabilities + + override fun getCapability(cap: Capability, side: Direction?): LazyOptional { + return caps[cap]?.get(side)?.cast() ?: super.getCapability(cap, side) + } + + override fun invalidateCaps() { + super.invalidateCaps() + + for (cap in caps.values) + cap.invalidate() + } + + override fun reviveCaps() { + super.reviveCaps() + + for (cap in caps.values) + cap.revive() + } + + private data class Savetable0(val type: Class, val property: KProperty0, val name: String, val serialize: (V) -> T, val deserialize: (V, T) -> Unit) + private data class Savetable1(val type: Class, val property: KMutableProperty0, val name: String, val serialize: (V) -> T, val deserialize: (T) -> V) + + private val savetables0 = ArrayList>() + private val savetables1 = ArrayList>() + + /** + * Save and load object state + */ + protected fun savetable(type: Class, property: KProperty0, name: String = property.name, serialize: (V) -> T, deserialize: (V, T?) -> Unit) { + check(!savetables0.any { it.name == name }) { "Already has save field with name $name" } + check(!savetables1.any { it.name == name }) { "Already has save field with name $name" } + savetables0.add(Savetable0(type, property, name, serialize, deserialize)) + } + + /** + * Save and load value + */ + protected fun savetable(type: Class, property: KMutableProperty0, name: String = property.name, serialize: (V) -> T, deserialize: (T?) -> V) { + check(!savetables0.any { it.name == name }) { "Already has save field with name $name" } + check(!savetables1.any { it.name == name }) { "Already has save field with name $name" } + savetables1.add(Savetable1(type, property, name, serialize, deserialize)) + } + + /** + * Save and load object state + */ + protected fun > savetable(type: Class, property: KProperty0, name: String = property.name) { + savetable(type, property, name, { it.serializeNBT()!! }, { self, it -> self.deserializeNBT(it) }) + } + + /** + * Save and load object state + */ + protected inline fun > savetable(property: KProperty0, name: String = property.name) { + savetable(T::class.java, property, name) + } + + /** + * Save and load object state + */ + protected inline fun savetable(property: KProperty0, name: String = property.name, noinline serialize: (V) -> T, noinline deserialize: (V, T?) -> Unit) { + savetable(T::class.java, property, name, serialize, deserialize) + } + + /** + * Save and load value + */ + protected inline fun savetable(property: KMutableProperty0, name: String = property.name, noinline serialize: (V) -> T, noinline deserialize: (T?) -> V) { + savetable(T::class.java, property, name, serialize, deserialize) + } + + override fun saveAdditional(nbt: CompoundTag) { + super.saveAdditional(nbt) + + if (caps.isNotEmpty()) { + nbt["Capabilities"] = CompoundTag().also { + for (cap in caps.values) { + it[cap.name] = cap.serializeNBT() + } + } + } + + for (data in savetables0) { + nbt[data.name] = (data.serialize as (Any) -> Tag).invoke(data.property.get()) + } + + for (data in savetables1) { + nbt[data.name] = (data.serialize as (Any) -> Tag).invoke(data.property.get()) + } + } + + override fun load(nbt: CompoundTag) { + super.load(nbt) + + if (nbt.contains("Capabilities")) { + @Suppress("name_shadowing") + val nbt = nbt.getCompound("Capabilities") + + for (cap in caps.values) { + if (nbt.contains(cap.name)) { + cap.deserializeNBT(nbt.getCompound(cap.name)) + } + } + } + + for (data in savetables0) { + val value = nbt[data.name] + + if (value != null && data.type.isAssignableFrom(value.javaClass)) { + (data.deserialize as (Any, Tag) -> Unit).invoke(data.property.get(), value) + } else if (value != null) { + throw ClassCastException("Expected ") + } + } + + for (data in savetables1) { + val value = nbt[data.name] + + if (value != null && data.type.isAssignableFrom(value.javaClass)) { + (data.property as KMutableProperty0).set((data.deserialize as (Tag) -> Any).invoke(value)) + } + } + } protected fun tickOnce(func: Runnable) { level?.oncePre { if (!isRemoved) func.run() } @@ -66,32 +427,6 @@ abstract class MatteryBlockEntity( level.oncePre { if (!isRemoved) func.invoke(level) } } - override fun getDisplayName(): Component { - return customDisplayName ?: defaultDisplayName - } - - protected open fun redstoneStatusUpdated(newBlocked: Boolean, oldBlocked: Boolean) {} - - override fun saveAdditional(nbt: CompoundTag) { - super.saveAdditional(nbt) - - val customDisplayName = customDisplayName - if (customDisplayName != null) - nbt.putString("Name", Component.Serializer.toJson(customDisplayName)) - - nbt[REDSTONE_CONTROL_KEY] = redstoneControl.serializeNBT() - } - - override fun load(nbt: CompoundTag) { - super.load(nbt) - - nbt.ifHas("Name", StringTag::class.java) { - customDisplayName = Component.Serializer.fromJson(it.asString) - } - - redstoneControl.deserializeNBT(nbt[REDSTONE_CONTROL_KEY] as? CompoundTag) - } - // Just to mark chunk unsaved open fun setChangedLight() { val level = level @@ -102,6 +437,255 @@ abstract class MatteryBlockEntity( } } + val synchronizer = FieldSynchronizer { + if (isSynchronizing) + return@FieldSynchronizer + + if (!isRemoved && level?.isClientSide == false && (_subCache == null || (_subCache ?: throw ConcurrentModificationException()).players.isNotEmpty())) { + onceServer { + synchronizeToPlayers(true) + } + } else { + markSynchronizerClean() + } + } + + private fun markSynchronizerClean() { + synchronizer.markClean() + } + + init { + synchronizer.defaultEndpoint.markUnused() + } + + override fun setLevel(p_155231_: Level) { + super.setLevel(p_155231_) + unsubscribe() + _subCache = null + + if (!p_155231_.isClientSide) { + subscribe() + } + } + + override fun setRemoved() { + super.setRemoved() + unsubscribe() + } + + override fun clearRemoved() { + super.clearRemoved() + + if (level?.isClientSide == false) { + subscribe() + } + } + + private var _subCache: ChunkSubscribers? = null + + private fun unsubscribe() { + val subCache = _subCache ?: return + + if (subCache.unsubscribe(this)) { + val level = subCache.level.get() + + if (level != null) { + playerMap.get(level)?.remove(subCache.chunkPos) + } + } + + lastSubscriptionChangeset = -1 + } + + private fun subscribe(): ChunkSubscribers { + val level = level + check(level is ServerLevel) { "Invalid realm" } + unsubscribe() + + val subs = playerMap + .computeIfAbsent(level) { Long2ObjectOpenHashMap() } + .computeIfAbsent(ChunkPos(blockPos).toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(level), chunkPos = ChunkPos(blockPos).toLong()) }) + + subs.subscribe(this) + _subCache = subs + return subs + } + + private val subscription get() = _subCache ?: subscribe() + private var lastSubscriptionChangeset = -1 + + fun synchronizeToPlayers() { + synchronizeToPlayers(false) + } + + protected fun synchronizeToPlayers(calledBySynchronizer: Boolean) { + isSynchronizing = true + + try { + if (synchronizer.isEmpty || isRemoved) { + if (calledBySynchronizer) synchronizer.markClean() + return + } + + check(level is ServerLevel) { "Invalid realm or Level is null" } + + synchronizer.observe() + + val subscription = subscription + + if (subscription.players.isEmpty() || lastSubscriptionChangeset == subscription.changeset && !synchronizer.hasChanges) { + if (calledBySynchronizer) synchronizer.markClean() + return + } + + lastSubscriptionChangeset = subscription.changeset + + for (player in subscription.players) { + val payload = synchronizer.computeEndpointFor(player).collectNetworkPayload() + + if (payload != null) { + WorldNetworkChannel.send(player, BlockEntitySyncPacket(blockPos, payload.array, payload.length)) + } + } + + synchronizer.markClean() + } finally { + isSynchronizing = false + } + } + + private data class ChunkSubscribers( + val blockEntities: ArrayList> = ArrayList(0), + val players: ArrayList = ArrayList(1), + val observingBlockEntities: ArrayList> = ArrayList(0), + val level: WeakReference, + val chunkPos: Long, + var changeset: Int = 0 + ) { + fun cleanUpBlocks() { + val listIterator = blockEntities.listIterator() + + for (block in listIterator) { + if (block.get() == null) { + listIterator.remove() + } + } + } + + val hasObservers: Boolean get() { + val listIterator = blockEntities.listIterator() + + for (value in listIterator) { + val ref = value.get() + + if (ref == null) { + listIterator.remove() + } else if (ref.synchronizer.hasObservers) { + return true + } + } + + return false + } + + fun subscribe(blockEntity: MatteryBlockEntity) { + val iterator = blockEntities.listIterator() + + for (value in iterator) { + val ref = value.get() + + if (ref === blockEntity) { + return + } else if (ref == null) { + iterator.remove() + } + } + + val bref = WeakReference(blockEntity) + blockEntities.add(bref) + changeset++ + + onceServer { + blockEntity.synchronizeToPlayers() + } + + if (blockEntity.synchronizer.hasObservers) { + observingBlockEntities.add(bref) + val listing = tickingMap.computeIfAbsent(level.get() ?: throw NullPointerException("Level got GCd!")) { ArrayList(1) } + val tickerIterator = listing.listIterator() + + for (value in tickerIterator) { + val ref = value.get() + + if (ref === this) { + return + } else if (ref == null) { + tickerIterator.remove() + } + } + + listing.add(WeakReference(this)) + } + } + + fun unsubscribe(blockEntity: MatteryBlockEntity): Boolean { + val listIterator = blockEntities.listIterator() + + for (value in listIterator) { + if (value.get() === blockEntity) { + listIterator.remove() + changeset++ + break + } + } + + if (blockEntity.synchronizer.hasObservers) { + val tickerIterator = observingBlockEntities.listIterator() + + for (value in tickerIterator) { + val ref = value.get() + + if (ref === blockEntity) { + tickerIterator.remove() + break + } else if (ref == null) { + tickerIterator.remove() + } + } + } + + if (players.isNotEmpty()) { + return false + } + + cleanUpBlocks() + return blockEntities.isEmpty() + } + + val isEmpty: Boolean get() { + if (players.isNotEmpty()) { + return false + } + + cleanUpBlocks() + return blockEntities.isEmpty() + } + } + + /** + * Why track player-tracked chunks? + * + * because minecraft itself doesn't track them, well, + * in "section partitioning" way. + * + * just look at [net.minecraft.server.level.PlayerMap] + * + * the [net.minecraft.server.level.PlayerMap.getPlayers] straight ignores chunk position + * and just fetches all players + * + * even if they did not ignore that argument, you still have to fetch player *list* though + * [net.minecraft.server.level.ChunkMap.getPlayers], which is not something we want + */ companion object { const val REDSTONE_CONTROL_KEY = "redstoneControl" const val INVENTORY_KEY = "container" @@ -110,5 +694,105 @@ abstract class MatteryBlockEntity( const val BATTERY_KEY = "batteryInventory" const val LOOT_TABLE_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_TAG const val LOOT_TABLE_SEED_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_SEED_TAG + + private val playerMap = WeakHashMap>() + private val tickingMap = WeakHashMap>>() + + fun onLevelUnload(event: LevelEvent.Unload) { + val level = event.level as? ServerLevel ?: return + playerMap.remove(level) + tickingMap.remove(level) + } + + fun onServerStopping(event: ServerStoppingEvent) { + playerMap.clear() + tickingMap.clear() + } + + fun postLevelTick(event: LevelTickEvent) { + if (event.phase == TickEvent.Phase.END) { + val level = event.level as? ServerLevel ?: return + val listing = tickingMap[level] ?: return + val listIterator = listing.listIterator() + var hitAnyObservers = false + + for (value in listIterator) { + val ref = value.get() + + if (ref == null) { + listIterator.remove() + } else if (ref.players.isNotEmpty()) { + var hitObservers = false + + ref.observingBlockEntities.forValidRefs { + hitObservers = true + hitAnyObservers = true + it.synchronizeToPlayers() + } + + if (!hitObservers) { + listIterator.remove() + } + } + } + + if (!hitAnyObservers) { + tickingMap.remove(level) + } + } + } + + fun onWatch(event: ChunkWatchEvent.Watch) { + playerMap + .computeIfAbsent(event.level) { Long2ObjectOpenHashMap() } + .computeIfAbsent(event.pos.toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(event.level), chunkPos = event.pos.toLong()) }) + .let { + val (blocks, players) = it + + if (event.player !in players) { + players.add(event.player) + it.changeset++ + + onceServer(10) { + if (!event.player.hasDisconnected() && event.player in players) { + blocks.forValidRefs { + it.synchronizeToPlayers(false) + } + } + } + } + } + } + + fun onForget(event: ChunkWatchEvent.UnWatch) { + val subs = playerMap.get(event.level)?.get(event.pos.toLong()) ?: return + + if (subs.players.remove(event.player)) { + if (subs.isEmpty) { + playerMap.get(event.level)?.remove(event.pos.toLong()) + } else { + subs.changeset++ + subs.blockEntities.forValidRefs { + it.synchronizer.removeEndpointFor(event.player) + } + } + } + } + + fun playerDisconnected(event: PlayerEvent.PlayerLoggedOutEvent) { + for (tree in playerMap.values) { + val iterator = tree.iterator() + + for (entry in iterator) { + if (entry.value.players.remove(event.entity)) { + entry.value.changeset++ + + if (entry.value.isEmpty) { + iterator.remove() + } + } + } + } + } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt new file mode 100644 index 000000000..b2e43f7db --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryDeviceBlockEntity.kt @@ -0,0 +1,64 @@ +package ru.dbotthepony.mc.otm.block.entity + +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.core.BlockPos +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.MenuProvider +import java.lang.Runnable +import net.minecraft.server.level.ServerLevel +import net.minecraft.client.multiplayer.ClientLevel +import net.minecraft.core.SectionPos +import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.Player +import net.minecraft.world.inventory.AbstractContainerMenu +import net.minecraft.nbt.CompoundTag +import net.minecraft.network.chat.Component +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity +import ru.dbotthepony.mc.otm.core.TextComponent +import ru.dbotthepony.mc.otm.core.nbt.getJson +import ru.dbotthepony.mc.otm.core.nbt.putJson +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.oncePre + +/** + * Device block entity base, implementing [MenuProvider] and [IRedstoneControlled], and also tracks custom display name + */ +abstract class MatteryDeviceBlockEntity(blockEntityType: BlockEntityType<*>, blockPos: BlockPos, blockState: BlockState) + : MatteryBlockEntity(blockEntityType, blockPos, blockState), MenuProvider, IRedstoneControlled { + var customDisplayName: Component? = null + + override val redstoneControl: AbstractRedstoneControl = RedstoneControl { new, old -> + setChangedLight() + + if (new != old) + redstoneStatusUpdated(new, old) + } + + protected open val defaultDisplayName: Component + get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos") + + abstract override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? + + override fun getDisplayName(): Component { + return customDisplayName ?: defaultDisplayName + } + + protected open fun redstoneStatusUpdated(newBlocked: Boolean, oldBlocked: Boolean) {} + + override fun saveAdditional(nbt: CompoundTag) { + super.saveAdditional(nbt) + + if (customDisplayName != null) + nbt.putJson("Name", Component.Serializer.toJsonTree(customDisplayName!!)) + + nbt[REDSTONE_CONTROL_KEY] = redstoneControl.serializeNBT() + } + + override fun load(nbt: CompoundTag) { + super.load(nbt) + + customDisplayName = nbt.getJson("Name")?.let(Component.Serializer::fromJson) + redstoneControl.deserializeNBT(nbt[REDSTONE_CONTROL_KEY] as? CompoundTag) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryPoweredBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryPoweredBlockEntity.kt index 791544ebd..89d265c9c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryPoweredBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryPoweredBlockEntity.kt @@ -25,7 +25,7 @@ import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set -abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(p_155228_, p_155229_, p_155230_) { +abstract class MatteryPoweredBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(p_155228_, p_155229_, p_155230_) { abstract val energy: BlockEnergyStorageImpl? private var valid = true val batteryContainer = MatteryContainer(this::setChangedLight, 1) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/RedstoneControl.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/RedstoneControl.kt index 5e112c7ae..a022d1918 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/RedstoneControl.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/RedstoneControl.kt @@ -6,7 +6,7 @@ import ru.dbotthepony.mc.otm.core.nbt.getEnum import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.network.FieldSynchronizer -interface IRedstoneControlProvider { +interface IRedstoneControlled { val redstoneControl: AbstractRedstoneControl } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/SynchronizedBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/SynchronizedBlockEntity.kt deleted file mode 100644 index 920821fb4..000000000 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/SynchronizedBlockEntity.kt +++ /dev/null @@ -1,380 +0,0 @@ -package ru.dbotthepony.mc.otm.block.entity - -import it.unimi.dsi.fastutil.longs.Long2ObjectFunction -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap -import net.minecraft.core.BlockPos -import net.minecraft.server.level.ServerLevel -import net.minecraft.server.level.ServerPlayer -import net.minecraft.world.level.ChunkPos -import net.minecraft.world.level.Level -import net.minecraft.world.level.block.entity.BlockEntity -import net.minecraft.world.level.block.entity.BlockEntityType -import net.minecraft.world.level.block.state.BlockState -import net.minecraftforge.event.TickEvent -import net.minecraftforge.event.TickEvent.LevelTickEvent -import net.minecraftforge.event.entity.player.PlayerEvent -import net.minecraftforge.event.level.ChunkWatchEvent -import net.minecraftforge.event.level.LevelEvent -import net.minecraftforge.event.server.ServerStoppingEvent -import ru.dbotthepony.mc.otm.core.forValidRefs -import ru.dbotthepony.mc.otm.network.BlockEntitySyncPacket -import ru.dbotthepony.mc.otm.network.FieldSynchronizer -import ru.dbotthepony.mc.otm.network.WorldNetworkChannel -import ru.dbotthepony.mc.otm.onceServer -import java.lang.ref.WeakReference -import java.util.WeakHashMap - -abstract class SynchronizedBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) { - private var isSynchronizing = false - - val synchronizer = FieldSynchronizer { - if (isSynchronizing) - return@FieldSynchronizer - - if (!isRemoved && level?.isClientSide == false && (_subCache == null || (_subCache ?: throw ConcurrentModificationException()).players.isNotEmpty())) { - onceServer { - synchronizeToPlayers(true) - } - } else { - markSynchronizerClean() - } - } - - private fun markSynchronizerClean() { - synchronizer.markClean() - } - - init { - synchronizer.defaultEndpoint.markUnused() - } - - override fun setLevel(p_155231_: Level) { - super.setLevel(p_155231_) - unsubscribe() - _subCache = null - - if (!p_155231_.isClientSide) { - subscribe() - } - } - - override fun setRemoved() { - super.setRemoved() - unsubscribe() - } - - override fun clearRemoved() { - super.clearRemoved() - - if (level?.isClientSide == false) { - subscribe() - } - } - - private var _subCache: ChunkSubscribers? = null - - private fun unsubscribe() { - val subCache = _subCache ?: return - - if (subCache.unsubscribe(this)) { - val level = subCache.level.get() - - if (level != null) { - playerMap.get(level)?.remove(subCache.chunkPos) - } - } - - lastSubscriptionChangeset = -1 - } - - private fun subscribe(): ChunkSubscribers { - val level = level - check(level is ServerLevel) { "Invalid realm" } - unsubscribe() - - val subs = playerMap - .computeIfAbsent(level) { Long2ObjectOpenHashMap() } - .computeIfAbsent(ChunkPos(blockPos).toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(level), chunkPos = ChunkPos(blockPos).toLong()) }) - - subs.subscribe(this) - _subCache = subs - return subs - } - - private val subscription get() = _subCache ?: subscribe() - private var lastSubscriptionChangeset = -1 - - fun synchronizeToPlayers() { - synchronizeToPlayers(false) - } - - protected fun synchronizeToPlayers(calledBySynchronizer: Boolean) { - isSynchronizing = true - - try { - if (synchronizer.isEmpty || isRemoved) { - if (calledBySynchronizer) synchronizer.markClean() - return - } - - check(level is ServerLevel) { "Invalid realm or Level is null" } - - synchronizer.observe() - - val subscription = subscription - - if (subscription.players.isEmpty() || lastSubscriptionChangeset == subscription.changeset && !synchronizer.hasChanges) { - if (calledBySynchronizer) synchronizer.markClean() - return - } - - lastSubscriptionChangeset = subscription.changeset - - for (player in subscription.players) { - val payload = synchronizer.computeEndpointFor(player).collectNetworkPayload() - - if (payload != null) { - WorldNetworkChannel.send(player, BlockEntitySyncPacket(blockPos, payload.array, payload.length)) - } - } - - synchronizer.markClean() - } finally { - isSynchronizing = false - } - } - - private data class ChunkSubscribers( - val blockEntities: ArrayList> = ArrayList(0), - val players: ArrayList = ArrayList(1), - val observingBlockEntities: ArrayList> = ArrayList(0), - val level: WeakReference, - val chunkPos: Long, - var changeset: Int = 0 - ) { - fun cleanUpBlocks() { - val listIterator = blockEntities.listIterator() - - for (block in listIterator) { - if (block.get() == null) { - listIterator.remove() - } - } - } - - val hasObservers: Boolean get() { - val listIterator = blockEntities.listIterator() - - for (value in listIterator) { - val ref = value.get() - - if (ref == null) { - listIterator.remove() - } else if (ref.synchronizer.hasObservers) { - return true - } - } - - return false - } - - fun subscribe(blockEntity: SynchronizedBlockEntity) { - val iterator = blockEntities.listIterator() - - for (value in iterator) { - val ref = value.get() - - if (ref === blockEntity) { - return - } else if (ref == null) { - iterator.remove() - } - } - - val bref = WeakReference(blockEntity) - blockEntities.add(bref) - changeset++ - - onceServer { - blockEntity.synchronizeToPlayers() - } - - if (blockEntity.synchronizer.hasObservers) { - observingBlockEntities.add(bref) - val listing = tickingMap.computeIfAbsent(level.get() ?: throw NullPointerException("Level got GCd!")) { ArrayList(1) } - val tickerIterator = listing.listIterator() - - for (value in tickerIterator) { - val ref = value.get() - - if (ref === this) { - return - } else if (ref == null) { - tickerIterator.remove() - } - } - - listing.add(WeakReference(this)) - } - } - - fun unsubscribe(blockEntity: SynchronizedBlockEntity): Boolean { - val listIterator = blockEntities.listIterator() - - for (value in listIterator) { - if (value.get() === blockEntity) { - listIterator.remove() - changeset++ - break - } - } - - if (blockEntity.synchronizer.hasObservers) { - val tickerIterator = observingBlockEntities.listIterator() - - for (value in tickerIterator) { - val ref = value.get() - - if (ref === blockEntity) { - tickerIterator.remove() - break - } else if (ref == null) { - tickerIterator.remove() - } - } - } - - if (players.isNotEmpty()) { - return false - } - - cleanUpBlocks() - return blockEntities.isEmpty() - } - - val isEmpty: Boolean get() { - if (players.isNotEmpty()) { - return false - } - - cleanUpBlocks() - return blockEntities.isEmpty() - } - } - - /** - * Why track player-tracked chunks? - * - * because minecraft itself doesn't track them, well, - * in "section partitioning" way. - * - * just look at [net.minecraft.server.level.PlayerMap] - * - * the [net.minecraft.server.level.PlayerMap.getPlayers] straight ignores chunk position - * and just fetches all players - * - * even if they did not ignore that argument, you still have to fetch player *list* though - * [net.minecraft.server.level.ChunkMap.getPlayers], which is not something we want - */ - companion object { - private val playerMap = WeakHashMap>() - private val tickingMap = WeakHashMap>>() - - fun onLevelUnload(event: LevelEvent.Unload) { - val level = event.level as? ServerLevel ?: return - playerMap.remove(level) - tickingMap.remove(level) - } - - fun onServerStopping(event: ServerStoppingEvent) { - playerMap.clear() - tickingMap.clear() - } - - fun postLevelTick(event: LevelTickEvent) { - if (event.phase == TickEvent.Phase.END) { - val level = event.level as? ServerLevel ?: return - val listing = tickingMap[level] ?: return - val listIterator = listing.listIterator() - var hitAnyObservers = false - - for (value in listIterator) { - val ref = value.get() - - if (ref == null) { - listIterator.remove() - } else if (ref.players.isNotEmpty()) { - var hitObservers = false - - ref.observingBlockEntities.forValidRefs { - hitObservers = true - hitAnyObservers = true - it.synchronizeToPlayers() - } - - if (!hitObservers) { - listIterator.remove() - } - } - } - - if (!hitAnyObservers) { - tickingMap.remove(level) - } - } - } - - fun onWatch(event: ChunkWatchEvent.Watch) { - playerMap - .computeIfAbsent(event.level) { Long2ObjectOpenHashMap() } - .computeIfAbsent(event.pos.toLong(), Long2ObjectFunction { ChunkSubscribers(level = WeakReference(event.level), chunkPos = event.pos.toLong()) }) - .let { - val (blocks, players) = it - - if (event.player !in players) { - players.add(event.player) - it.changeset++ - - onceServer(10) { - if (!event.player.hasDisconnected() && event.player in players) { - blocks.forValidRefs { - it.synchronizeToPlayers(false) - } - } - } - } - } - } - - fun onForget(event: ChunkWatchEvent.UnWatch) { - val subs = playerMap.get(event.level)?.get(event.pos.toLong()) ?: return - - if (subs.players.remove(event.player)) { - if (subs.isEmpty) { - playerMap.get(event.level)?.remove(event.pos.toLong()) - } else { - subs.changeset++ - subs.blockEntities.forValidRefs { - it.synchronizer.removeEndpointFor(event.player) - } - } - } - } - - fun playerDisconnected(event: PlayerEvent.PlayerLoggedOutEvent) { - for (tree in playerMap.values) { - val iterator = tree.iterator() - - for (entry in iterator) { - if (entry.value.players.remove(event.entity)) { - entry.value.changeset++ - - if (entry.value.isEmpty) { - iterator.remove() - } - } - } - } - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt index 3c3782657..eb7e64e18 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt @@ -19,7 +19,7 @@ import net.minecraft.world.phys.AABB import net.minecraft.world.phys.Vec3 import ru.dbotthepony.mc.otm.block.BlackHoleBlock import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity -import ru.dbotthepony.mc.otm.block.entity.SynchronizedBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue.Companion.queueForLevel import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.plus @@ -36,7 +36,7 @@ import kotlin.math.pow import kotlin.math.roundToInt import kotlin.math.sqrt -class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : SynchronizedBlockEntity(MBlockEntities.BLACK_HOLE, p_155229_, p_155230_) { +class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.BLACK_HOLE, p_155229_, p_155230_) { var mass by synchronizer.fraction(BASELINE_MASS, setter = setter@{ mass, field, setByRemote -> if (mass <= Decimal.ZERO) { collapse() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt index aa762442c..b345ac238 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/CargoCrateBlockEntity.kt @@ -30,7 +30,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainerHooks import ru.dbotthepony.mc.otm.core.TranslatableComponent @@ -43,7 +43,7 @@ import ru.dbotthepony.mc.otm.registry.MSoundEvents class CargoCrateBlockEntity( p_155229_: BlockPos, p_155230_: BlockState -) : MatteryBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_), IDroppableContainer { +) : MatteryDeviceBlockEntity(MBlockEntities.CARGO_CRATE, p_155229_, p_155230_), IDroppableContainer { val container = MatteryContainer(this::setChanged, CAPACITY) private var interactingPlayers = 0 val handler = container.handler(object : MatteryContainerHooks { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/HoloSignBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/HoloSignBlockEntity.kt index 9429d3251..a4b1b36ff 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/HoloSignBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/decorative/HoloSignBlockEntity.kt @@ -8,15 +8,15 @@ import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.level.block.state.BlockState -import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlProvider -import ru.dbotthepony.mc.otm.block.entity.SynchronizedBlockEntity +import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.SynchronizedRedstoneControl import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlocks -class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : SynchronizedBlockEntity(MBlockEntities.HOLO_SIGN, blockPos, blockState), MenuProvider, IRedstoneControlProvider { +class HoloSignBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.HOLO_SIGN, blockPos, blockState), MenuProvider, IRedstoneControlled { override val redstoneControl = SynchronizedRedstoneControl(synchronizer) { _, _ -> setChanged() } var text by synchronizer.string("", name = "text", setter = { value, access, remote -> diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterCapacitorBankBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterCapacitorBankBlockEntity.kt index a8e9e404b..61496ebed 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterCapacitorBankBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterCapacitorBankBlockEntity.kt @@ -11,14 +11,12 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.item.ItemStack import net.minecraft.world.level.Level -import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.util.LazyOptional import ru.dbotthepony.mc.otm.core.TranslatableComponent -import ru.dbotthepony.mc.otm.block.tech.BatteryBankBlock import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.MatteryCapability @@ -35,7 +33,7 @@ import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set -class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterGraphNode, IMatterStorage, IDroppableContainer { +class MatterCapacitorBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.MATTER_CAPACITOR_BANK, p_155229_, p_155230_), IMatterGraphNode, IMatterStorage, IDroppableContainer { var gaugeLevel by synchronizer.float() private set diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt index 2fe317f8d..28c09daeb 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/MatterPanelBlockEntity.kt @@ -19,7 +19,7 @@ import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.Level import net.minecraftforge.common.capabilities.Capability import ru.dbotthepony.mc.otm.core.TranslatableComponent -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode @@ -29,7 +29,7 @@ import java.util.ArrayList import java.util.stream.Stream class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : - MatteryBlockEntity(MBlockEntities.MATTER_PANEL, p_155229_, p_155230_), IMatterGraphNode, IReplicationTaskProvider { + MatteryDeviceBlockEntity(MBlockEntities.MATTER_PANEL, p_155229_, p_155230_), IMatterGraphNode, IReplicationTaskProvider { private val listeners = ArrayList() override val matterNode = Graph6Node(this) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/PatternStorageBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/PatternStorageBlockEntity.kt index 759d29f70..378c5ac21 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/PatternStorageBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/matter/PatternStorageBlockEntity.kt @@ -24,7 +24,7 @@ import net.minecraft.world.level.block.Block import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.core.collect.iterator @@ -40,7 +40,7 @@ import java.util.stream.Stream @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : - MatteryBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IMatterGraphNode, IPatternStorage, IDroppableContainer { + MatteryDeviceBlockEntity(MBlockEntities.PATTERN_STORAGE, p_155229_, p_155230_), IMatterGraphNode, IPatternStorage, IDroppableContainer { override val matterNode = Graph6Node(this) private val resolverPatterns = LazyOptional.of { this } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/BatteryBankBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/BatteryBankBlockEntity.kt index f348fdd9b..a92b3eb3a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/BatteryBankBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/BatteryBankBlockEntity.kt @@ -10,17 +10,14 @@ import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.item.ItemStack import net.minecraft.world.level.Level -import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.IEnergyStorage import org.apache.logging.log4j.LogManager -import ru.dbotthepony.mc.otm.block.tech.BatteryBankBlock import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper @@ -29,14 +26,12 @@ import ru.dbotthepony.mc.otm.container.MatteryContainerHooks import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.facingOne -import ru.dbotthepony.mc.otm.core.math.rotationOne -import ru.dbotthepony.mc.otm.core.math.unaryMinus import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.util.BESubscribeList import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities -class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.BATTERY_BANK, p_155229_, p_155230_), IDroppableContainer { +class BatteryBankBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.BATTERY_BANK, p_155229_, p_155230_), IDroppableContainer { var gaugeLevel by synchronizer.float() private set diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt index 73e6e6cc1..e78090317 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/ChemicalGeneratorBlockEntity.kt @@ -19,8 +19,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.IEnergyStorage import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage @@ -39,7 +38,7 @@ import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.util.BESubscribeList -class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state), IDroppableContainer { +class ChemicalGeneratorBlockEntity(pos: BlockPos, state: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.CHEMICAL_GENERATOR, pos, state), IDroppableContainer { override val defaultDisplayName: Component get() = MACHINE_NAME diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt index 07a2c7b29..56947d59a 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/CobblerBlockEntity.kt @@ -1,25 +1,17 @@ package ru.dbotthepony.mc.otm.block.entity.tech import net.minecraft.core.BlockPos -import net.minecraft.core.Direction -import net.minecraft.nbt.CompoundTag -import net.minecraft.network.chat.Component -import net.minecraft.world.Container import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Player import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.level.block.state.BlockState -import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities -import net.minecraftforge.common.util.LazyOptional import ru.dbotthepony.mc.otm.block.IDroppableContainer import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainerHooks -import ru.dbotthepony.mc.otm.core.nbt.map -import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.menu.tech.CobblerMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities @@ -31,37 +23,15 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState) override val energy = null override val droppableContainer = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE) - val handler = droppableContainer.handler(object : MatteryContainerHooks { + + val handler = StaticCap(ForgeCapabilities.ITEM_HANDLER, INVENTORY_KEY, droppableContainer.handler(object : MatteryContainerHooks { override fun canInsert(slot: Int, stack: ItemStack): Boolean { return false } - }) + })) - override fun getCapability(cap: Capability, side: Direction?): LazyOptional { - if (cap === ForgeCapabilities.ITEM_HANDLER) - return handler.get().cast() - - return super.getCapability(cap, side) - } - - override fun invalidateCaps() { - super.invalidateCaps() - handler.invalidate() - } - - override fun reviveCaps() { - super.reviveCaps() - handler.revive() - } - - override fun saveAdditional(nbt: CompoundTag) { - super.saveAdditional(nbt) - nbt[INVENTORY_KEY] = droppableContainer.serializeNBT() - } - - override fun load(nbt: CompoundTag) { - super.load(nbt) - nbt.map(INVENTORY_KEY, droppableContainer::deserializeNBT) + init { + savetable(::droppableContainer, INVENTORY_KEY) } override fun onJobFinish(job: ItemJob): Status { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt index 982838158..e31e5aa5c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyCounterBlockEntity.kt @@ -16,7 +16,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.energy.IEnergyStorage import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper @@ -33,7 +33,7 @@ import ru.dbotthepony.mc.otm.menu.tech.EnergyCounterMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import java.util.* -class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { +class EnergyCounterBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_COUNTER, p_155229_, p_155230_) { var passed by synchronizer.fraction() private val history = Array(10 * 20) { Decimal.ZERO } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyServoBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyServoBlockEntity.kt index aa6fcbdd5..17403b223 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyServoBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/EnergyServoBlockEntity.kt @@ -14,7 +14,7 @@ import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.LazyOptional import ru.dbotthepony.mc.otm.block.IDroppableContainer -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.MatteryCapability @@ -33,7 +33,7 @@ import ru.dbotthepony.mc.otm.menu.tech.EnergyServoMenu import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlocks -class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState), IDroppableContainer { +class EnergyServoBlockEntity(blockPos: BlockPos, blockState: BlockState) : MatteryDeviceBlockEntity(MBlockEntities.ENERGY_SERVO, blockPos, blockState), IDroppableContainer { override val defaultDisplayName: Component get() = MBlocks.ENERGY_SERVO.name diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/GravitationStabilizerBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/GravitationStabilizerBlockEntity.kt index 0ae35a55c..06eb3b533 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/GravitationStabilizerBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/tech/GravitationStabilizerBlockEntity.kt @@ -11,8 +11,7 @@ import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.AABB import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizerLens -import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizer @@ -21,7 +20,7 @@ import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.times import ru.dbotthepony.mc.otm.registry.MBlockEntities -class GravitationStabilizerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryBlockEntity( +class GravitationStabilizerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : MatteryDeviceBlockEntity( MBlockEntities.GRAVITATION_STABILIZER, p_155229_, p_155230_) { override val defaultDisplayName: Component diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/ChemicalGeneratorBlock.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/ChemicalGeneratorBlock.kt index c6a2a7517..4728e76e2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/ChemicalGeneratorBlock.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/tech/ChemicalGeneratorBlock.kt @@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.block.tech import net.minecraft.ChatFormatting import net.minecraft.core.BlockPos -import net.minecraft.core.Direction import net.minecraft.nbt.CompoundTag import net.minecraft.network.chat.Component import net.minecraft.world.item.BlockItem @@ -20,8 +19,9 @@ import net.minecraft.world.level.block.state.StateDefinition import net.minecraft.world.phys.shapes.CollisionContext import net.minecraft.world.phys.shapes.VoxelShape import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock -import ru.dbotthepony.mc.otm.block.entity.tech.ChemicalGeneratorBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.tech.ChemicalGeneratorBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.block.getShapeForEachState import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage @@ -29,7 +29,6 @@ import ru.dbotthepony.mc.otm.capability.energy.ItemEnergyStorageImpl import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.get -import ru.dbotthepony.mc.otm.core.math.facingOne import ru.dbotthepony.mc.otm.core.nbt.map import ru.dbotthepony.mc.otm.oncePre import ru.dbotthepony.mc.otm.registry.MBlockEntities diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/BlockEnergyStorageImpl.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/BlockEnergyStorageImpl.kt index 0ec7c4e1f..627c997bf 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/BlockEnergyStorageImpl.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/energy/BlockEnergyStorageImpl.kt @@ -13,9 +13,10 @@ import net.minecraft.world.level.block.entity.BlockEntity import net.minecraftforge.common.ForgeConfigSpec import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.LazyOptional +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.config.ConciseBalanceValues import ru.dbotthepony.mc.otm.config.VerboseBalanceValues -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.defineDecimal diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/BankRenderer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/BankRenderer.kt index 5d62e3ff0..ae1c202e6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/BankRenderer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/blockentity/BankRenderer.kt @@ -10,10 +10,8 @@ import net.minecraft.core.Direction import net.minecraft.resources.ResourceLocation import net.minecraft.world.level.levelgen.XoroshiroRandomSource import net.minecraftforge.client.event.ModelEvent -import org.joml.AxisAngle4f -import org.joml.Quaternionf import ru.dbotthepony.mc.otm.OverdriveThatMatters -import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity import ru.dbotthepony.mc.otm.block.entity.matter.MatterCapacitorBankBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity import ru.dbotthepony.mc.otm.client.minecraft @@ -24,7 +22,6 @@ import ru.dbotthepony.mc.otm.client.screen.widget.PowerGaugePanel import ru.dbotthepony.mc.otm.core.ImmutableList import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom -import ru.dbotthepony.mc.otm.core.math.PIf import ru.dbotthepony.mc.otm.core.math.facingOne import ru.dbotthepony.mc.otm.core.math.rotate import ru.dbotthepony.mc.otm.core.math.rotateY @@ -32,7 +29,7 @@ import ru.dbotthepony.mc.otm.nanoTime import java.util.function.Supplier import kotlin.math.PI -abstract class BankRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer { +abstract class BankRenderer(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer { protected abstract fun gaugeLevel(entity: T): Float protected abstract val texture: AbstractMatterySprite protected abstract val models: List diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt index 97cdad1b9..c91c77904 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/container/MatteryContainer.kt @@ -9,12 +9,13 @@ import net.minecraft.world.Container import kotlin.jvm.JvmOverloads import net.minecraft.world.entity.player.Player import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraftforge.common.util.INBTSerializable import ru.dbotthepony.mc.otm.core.nbt.ifHas import ru.dbotthepony.mc.otm.core.nbt.set import java.util.* @Suppress("UNUSED") -open class MatteryContainer(val watcher: Runnable, private val size: Int) : Container, Iterable { +open class MatteryContainer(val watcher: Runnable, private val size: Int) : Container, Iterable, INBTSerializable { constructor(watcher: BlockEntity, size: Int) : this(watcher::setChanged, size) constructor(size: Int) : this({}, size) @@ -107,7 +108,7 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont setChanged() } - fun deserializeNBT(tag: Tag?) { + override fun deserializeNBT(tag: Tag?) { when (tag) { is CompoundTag -> deserializeNBT(tag) is ListTag -> deserializeNBT(tag) @@ -122,7 +123,7 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont if (ignoreChangeNotifications == 0) watcher.run() } - fun serializeNBT(): ListTag { + override fun serializeNBT(): ListTag { return ListTag().also { for ((i, item) in slots.withIndex()) { if (!item.isEmpty) { 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 cefba788a..496ce6645 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,5 +1,6 @@ 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 @@ -8,6 +9,7 @@ import net.minecraft.world.level.block.Rotation import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ICapabilityProvider import net.minecraftforge.common.util.LazyOptional +import java.util.Collections import java.util.EnumMap internal inline val Direction.blockRotation @@ -25,6 +27,10 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos { return this + other.normal } +enum class RelativeSide { + FRONT, BACK, LEFT, RIGHT, TOP, BOTTOM +} + /** * Represents unique block orientation in space, by providing [front] and [top] directions * @@ -83,6 +89,36 @@ enum class BlockRotation( val bottom: Direction = top.opposite val back: Direction = front.opposite + private val _dir2Side = EnumMap(Direction::class.java) + private val _side2Dir = EnumMap(RelativeSide::class.java) + + val dir2Side: Map = Collections.unmodifiableMap(_dir2Side) + val side2Dir: Map = Collections.unmodifiableMap(_side2Dir) + + init { + _side2Dir[RelativeSide.FRONT] = front + _side2Dir[RelativeSide.BACK] = back + _side2Dir[RelativeSide.LEFT] = left + _side2Dir[RelativeSide.RIGHT] = right + _side2Dir[RelativeSide.TOP] = top + _side2Dir[RelativeSide.BOTTOM] = bottom + + _dir2Side[front] = RelativeSide.FRONT + _dir2Side[back] = RelativeSide.BACK + _dir2Side[left] = RelativeSide.LEFT + _dir2Side[right] = RelativeSide.RIGHT + _dir2Side[top] = RelativeSide.TOP + _dir2Side[bottom] = RelativeSide.BOTTOM + } + + fun dir2Side(direction: Direction): RelativeSide { + return _dir2Side[direction]!! + } + + fun side2Dir(side: RelativeSide): Direction { + return _side2Dir[side]!! + } + operator fun component1() = front operator fun component2() = top operator fun component3() = left diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/nbt/CompoundTagExt.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/nbt/CompoundTagExt.kt index 4073e87f5..ae8e344da 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/nbt/CompoundTagExt.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/nbt/CompoundTagExt.kt @@ -1,5 +1,8 @@ package ru.dbotthepony.mc.otm.core.nbt +import com.google.gson.JsonElement +import it.unimi.dsi.fastutil.io.FastByteArrayInputStream +import it.unimi.dsi.fastutil.io.FastByteArrayOutputStream import net.minecraft.nbt.ByteArrayTag import net.minecraft.nbt.ByteTag import net.minecraft.nbt.CompoundTag @@ -10,11 +13,14 @@ import net.minecraft.nbt.IntTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.LongArrayTag import net.minecraft.nbt.LongTag +import net.minecraft.nbt.NbtAccounter import net.minecraft.nbt.NbtUtils import net.minecraft.nbt.ShortTag import net.minecraft.nbt.StringTag import net.minecraft.nbt.Tag import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.core.util.readJson +import ru.dbotthepony.mc.otm.core.util.writeJson import java.util.UUID operator fun CompoundTag.set(index: String, value: Tag) = put(index, value) @@ -132,3 +138,18 @@ fun CompoundTag.getUUIDSafe(key: String): UUID? { val value = this[key] as? IntArrayTag ?: return null return NbtUtils.loadUUID(value) } + +fun CompoundTag.putJson(key: String, json: JsonElement) { + val bytes = FastByteArrayOutputStream() + bytes.writeJson(json) + putByteArray(key, bytes.array.copyOfRange(0, bytes.length)) +} + +fun CompoundTag.getJson(key: String, sizeLimit: NbtAccounter = NbtAccounter.UNLIMITED): JsonElement? { + val bytes = getByteArray(key) + + if (bytes.isEmpty()) + return null + + return FastByteArrayInputStream(bytes).readJson(sizeLimit) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt index 9e8cfc865..b135c3f94 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/network/WorldNetworkChannel.kt @@ -7,7 +7,7 @@ import net.minecraftforge.network.NetworkDirection import net.minecraftforge.network.NetworkEvent import org.apache.logging.log4j.LogManager import ru.dbotthepony.mc.otm.android.feature.ItemEntityDataPacket -import ru.dbotthepony.mc.otm.block.entity.SynchronizedBlockEntity +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.client.minecraft import java.util.function.Supplier @@ -22,7 +22,7 @@ class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val v context.enqueueWork { val level = minecraft.player?.level ?: return@enqueueWork - val blockEntity = level.getBlockEntity(position) as? SynchronizedBlockEntity ?: return@enqueueWork + val blockEntity = level.getBlockEntity(position) as? MatteryBlockEntity ?: return@enqueueWork try { blockEntity.synchronizer.read(FastByteArrayInputStream(buffer, 0, validBytes))