A lot of refactoring related to block entities, capability helpers and save helpers

This commit is contained in:
DBotThePony 2023-02-16 13:12:29 +07:00
parent 370aca1e6a
commit 0ee5673ea9
Signed by: DBot
GPG Key ID: DCC23B5715498507
29 changed files with 923 additions and 536 deletions

View File

@ -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.predicates.LootItemBlockStatePropertyCondition
import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider import net.minecraft.world.level.storage.loot.providers.nbt.ContextNbtProvider
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue 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 import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
data class NbtCopy(val source: String, val destination: String, val strategy: CopyNbtFunction.MergeStrategy = CopyNbtFunction.MergeStrategy.REPLACE) 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( private val basicTags = arrayOf(
TileNbtCopy(MatteryBlockEntity.REDSTONE_CONTROL_KEY), TileNbtCopy(MatteryDeviceBlockEntity.REDSTONE_CONTROL_KEY),
) )
private val poweredTags = arrayOf( private val poweredTags = arrayOf(
*basicTags, *basicTags,
TileNbtCopy(MatteryBlockEntity.ENERGY_KEY), TileNbtCopy(MatteryDeviceBlockEntity.ENERGY_KEY),
TileNbtCopy(MatteryBlockEntity.BATTERY_KEY), TileNbtCopy(MatteryDeviceBlockEntity.BATTERY_KEY),
) )
private val workerTags = arrayOf( private val workerTags = arrayOf(
@ -53,7 +53,7 @@ private val workerTags = arrayOf(
private val poweredMatterWorker = arrayOf( private val poweredMatterWorker = arrayOf(
*workerTags, *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 */) { 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 */) {

View File

@ -8,9 +8,9 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition 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.ChemicalGeneratorBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.EnergyCounterBlockEntity 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.MatteryDeviceBlockEntity.Companion.ENERGY_KEY
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity.Companion.MATTER_STORAGE_KEY import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity.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.REDSTONE_CONTROL_KEY
import ru.dbotthepony.mc.otm.block.entity.decorative.HoloSignBlockEntity 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.matter.MatterBottlerBlockEntity
import ru.dbotthepony.mc.otm.block.entity.storage.AbstractStorageImportExport.Companion.FILTER_KEY import ru.dbotthepony.mc.otm.block.entity.storage.AbstractStorageImportExport.Companion.FILTER_KEY

View File

@ -17,7 +17,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import ru.dbotthepony.mc.otm.android.AndroidResearchManager; import ru.dbotthepony.mc.otm.android.AndroidResearchManager;
import ru.dbotthepony.mc.otm.android.feature.EnderTeleporterFeature; 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.block.entity.blackhole.ExplosionQueue;
import ru.dbotthepony.mc.otm.capability.MatteryCapability; import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.MatteryPlayerCapability; 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::onDataPackSync);
EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::addCommands); EVENT_BUS.addListener(EventPriority.NORMAL, MatterManager.INSTANCE::addCommands);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onServerStopping); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onServerStopping);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onLevelUnload); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onLevelUnload);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onWatch); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onWatch);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::onForget); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::onForget);
EVENT_BUS.addListener(EventPriority.NORMAL, SynchronizedBlockEntity.Companion::playerDisconnected); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryBlockEntity.Companion::playerDisconnected);
EVENT_BUS.addListener(EventPriority.LOWEST, SynchronizedBlockEntity.Companion::postLevelTick); EVENT_BUS.addListener(EventPriority.LOWEST, MatteryBlockEntity.Companion::postLevelTick);
EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath); EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath);
EVENT_BUS.addListener(EventPriority.HIGH, ItemTritaniumArmor.Companion::onHurt); EVENT_BUS.addListener(EventPriority.HIGH, ItemTritaniumArmor.Companion::onHurt);

View File

@ -1,7 +1,10 @@
package ru.dbotthepony.mc.otm.capability; package ru.dbotthepony.mc.otm.capability;
import mekanism.api.energy.IStrictEnergyHandler; 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 org.jetbrains.annotations.NotNull;
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive; import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive;
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage; import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage;

View File

@ -23,8 +23,8 @@ import net.minecraft.world.level.material.Material
import net.minecraft.world.level.material.MaterialColor import net.minecraft.world.level.material.MaterialColor
import net.minecraft.world.phys.BlockHitResult import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlProvider import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
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.WorkerState
import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom 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) { if (this is EntityBlock && itemStack.hasCustomHoverName() && !level.isClientSide) {
val tile = level.getBlockEntity(blockPos) val tile = level.getBlockEntity(blockPos)
if (tile is MatteryBlockEntity) if (tile is MatteryDeviceBlockEntity)
tile.customDisplayName = itemStack.displayName tile.customDisplayName = itemStack.displayName
} }
@ -172,7 +172,7 @@ abstract class MatteryBlock @JvmOverloads constructor(
if (this is EntityBlock && !level.isClientSide) { if (this is EntityBlock && !level.isClientSide) {
val tile = level.getBlockEntity(pos) val tile = level.getBlockEntity(pos)
if (tile is IRedstoneControlProvider) if (tile is IRedstoneControlled)
tile.redstoneControl.redstoneSignal = level.getBestNeighborSignal(pos) tile.redstoneControl.redstoneSignal = level.getBestNeighborSignal(pos)
} }
} }

View File

@ -1,43 +1,404 @@
package ru.dbotthepony.mc.otm.block.entity package ru.dbotthepony.mc.otm.block.entity
import net.minecraft.world.level.block.entity.BlockEntityType import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import net.minecraft.core.BlockPos import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import net.minecraft.world.level.block.state.BlockState import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap
import net.minecraft.world.MenuProvider import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import java.lang.Runnable
import net.minecraft.server.level.ServerLevel
import net.minecraft.client.multiplayer.ClientLevel import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.SectionPos 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.CompoundTag
import net.minecraft.nbt.StringTag import net.minecraft.nbt.Tag
import net.minecraft.network.chat.Component 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.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 net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity
import ru.dbotthepony.mc.otm.core.TextComponent import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.core.nbt.ifHas 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.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.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<*>, * Absolute barebone block entity class in Overdrive that Matters, providing bare minimum functionality
p_155229_: BlockPos, */
p_155230_: BlockState abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(p_155228_, p_155229_, p_155230_) {
) : SynchronizedBlockEntity(p_155228_, p_155229_, p_155230_), MenuProvider, IRedstoneControlProvider { private var isSynchronizing = false
var customDisplayName: Component? = null
override val redstoneControl: AbstractRedstoneControl = RedstoneControl { new, old -> /**
if (new != old) * "shortcut" for getting [BlockRotation]
redstoneStatusUpdated(new, old) *
else * if block has no rotation, returns [BlockRotation.NORTH]
setChangedLight() */
open val blockRotation: BlockRotation get() {
return blockState[(blockState.block as? RotatableMatteryBlock ?: return BlockRotation.NORTH).rotationProperty]
} }
protected open val defaultDisplayName: Component // Capabilities
get() = level?.getBlockState(blockPos)?.block?.name ?: TextComponent("null at $blockPos")
abstract override fun createMenu(containerID: Int, inventory: Inventory, ply: Player): AbstractContainerMenu? private val caps = Reference2ObjectArrayMap<Capability<*>, CapInstance<*, *>>()
fun <T : Any> getCapInstance(index: Capability<in T>): CapInstance<T, *>? {
return caps[index] as CapInstance<T, *>?
}
protected fun <T : IMatteryEnergyStorage> energy(capability: T): StaticCap<T> {
return StaticCap(MatteryCapability.ENERGY, "energy", capability).alias(ForgeCapabilities.ENERGY)
}
abstract inner class CapInstance<T : Any, S : CapInstance<T, S>>(type: Capability<in T>, val name: String) : INBTSerializable<CompoundTag> {
private val types = ReferenceArraySet<Capability<in T>>()
protected var valid = true
abstract val capability: T
protected var optional by object : ReadWriteProperty<Any?, LazyOptional<T>> {
private var value: LazyOptional<T>? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): LazyOptional<T> {
if (value == null)
value = if (valid) LazyOptional.of { capability } else LazyOptional.empty()
return value!!
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: LazyOptional<T>) {
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<in T>): 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<Tag>).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<Tag>).deserializeNBT(tag)
}
}
protected val exposedSides = Array<Pair<Boolean, LazyOptional<T>>>(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<T> {
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<T : Any>(type: Capability<in T>, name: String, override val capability: T) : CapInstance<T, StaticCap<T>>(type, name)
inner class DynamicCap<T : Any>(type: Capability<in T>, name: String, capability: T) : CapInstance<T, DynamicCap<T>>(type, name) {
override var capability: T = capability
private set(value) {
field = value
if (valid) {
val old = ArrayList<LazyOptional<T>>(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<T>::invalidate)
}
}
}
// /Capabilities
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
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<T : Tag, V : Any>(val type: Class<T>, val property: KProperty0<V>, val name: String, val serialize: (V) -> T, val deserialize: (V, T) -> Unit)
private data class Savetable1<T : Tag, V : Any>(val type: Class<T>, val property: KMutableProperty0<V>, val name: String, val serialize: (V) -> T, val deserialize: (T) -> V)
private val savetables0 = ArrayList<Savetable0<*, *>>()
private val savetables1 = ArrayList<Savetable1<*, *>>()
/**
* Save and load object state
*/
protected fun <T : Tag, V : Any> savetable(type: Class<T>, property: KProperty0<V>, 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 <T : Tag, V : Any> savetable(type: Class<T>, property: KMutableProperty0<V>, 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 <T : Tag, V : INBTSerializable<T?>> savetable(type: Class<T>, property: KProperty0<V>, name: String = property.name) {
savetable(type, property, name, { it.serializeNBT()!! }, { self, it -> self.deserializeNBT(it) })
}
/**
* Save and load object state
*/
protected inline fun <reified T : Tag, V : INBTSerializable<T?>> savetable(property: KProperty0<V>, name: String = property.name) {
savetable(T::class.java, property, name)
}
/**
* Save and load object state
*/
protected inline fun <reified T : Tag, V : Any> savetable(property: KProperty0<V>, 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 <reified T : Tag, V : Any> savetable(property: KMutableProperty0<V>, 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<Any>).set((data.deserialize as (Tag) -> Any).invoke(value))
}
}
}
protected fun tickOnce(func: Runnable) { protected fun tickOnce(func: Runnable) {
level?.oncePre { if (!isRemoved) func.run() } level?.oncePre { if (!isRemoved) func.run() }
@ -66,32 +427,6 @@ abstract class MatteryBlockEntity(
level.oncePre { if (!isRemoved) func.invoke(level) } 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 // Just to mark chunk unsaved
open fun setChangedLight() { open fun setChangedLight() {
val level = level 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<WeakReference<MatteryBlockEntity>> = ArrayList(0),
val players: ArrayList<ServerPlayer> = ArrayList(1),
val observingBlockEntities: ArrayList<WeakReference<MatteryBlockEntity>> = ArrayList(0),
val level: WeakReference<ServerLevel>,
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 { companion object {
const val REDSTONE_CONTROL_KEY = "redstoneControl" const val REDSTONE_CONTROL_KEY = "redstoneControl"
const val INVENTORY_KEY = "container" const val INVENTORY_KEY = "container"
@ -110,5 +694,105 @@ abstract class MatteryBlockEntity(
const val BATTERY_KEY = "batteryInventory" const val BATTERY_KEY = "batteryInventory"
const val LOOT_TABLE_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_TAG const val LOOT_TABLE_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_TAG
const val LOOT_TABLE_SEED_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_SEED_TAG const val LOOT_TABLE_SEED_KEY = RandomizableContainerBlockEntity.LOOT_TABLE_SEED_TAG
private val playerMap = WeakHashMap<ServerLevel, Long2ObjectOpenHashMap<ChunkSubscribers>>()
private val tickingMap = WeakHashMap<ServerLevel, ArrayList<WeakReference<ChunkSubscribers>>>()
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()
}
}
}
}
}
} }
} }

View File

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

View File

@ -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.map
import ru.dbotthepony.mc.otm.core.nbt.set 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? abstract val energy: BlockEnergyStorageImpl?
private var valid = true private var valid = true
val batteryContainer = MatteryContainer(this::setChangedLight, 1) val batteryContainer = MatteryContainer(this::setChangedLight, 1)

View File

@ -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.core.nbt.set
import ru.dbotthepony.mc.otm.network.FieldSynchronizer import ru.dbotthepony.mc.otm.network.FieldSynchronizer
interface IRedstoneControlProvider { interface IRedstoneControlled {
val redstoneControl: AbstractRedstoneControl val redstoneControl: AbstractRedstoneControl
} }

View File

@ -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<WeakReference<SynchronizedBlockEntity>> = ArrayList(0),
val players: ArrayList<ServerPlayer> = ArrayList(1),
val observingBlockEntities: ArrayList<WeakReference<SynchronizedBlockEntity>> = ArrayList(0),
val level: WeakReference<ServerLevel>,
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<ServerLevel, Long2ObjectOpenHashMap<ChunkSubscribers>>()
private val tickingMap = WeakHashMap<ServerLevel, ArrayList<WeakReference<ChunkSubscribers>>>()
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()
}
}
}
}
}
}
}

View File

@ -19,7 +19,7 @@ import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3 import net.minecraft.world.phys.Vec3
import ru.dbotthepony.mc.otm.block.BlackHoleBlock import ru.dbotthepony.mc.otm.block.BlackHoleBlock
import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity 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.block.entity.blackhole.ExplosionQueue.Companion.queueForLevel
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.core.math.plus
@ -36,7 +36,7 @@ import kotlin.math.pow
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sqrt 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 -> var mass by synchronizer.fraction(BASELINE_MASS, setter = setter@{ mass, field, setByRemote ->
if (mass <= Decimal.ZERO) { if (mass <= Decimal.ZERO) {
collapse() collapse()

View File

@ -30,7 +30,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock import ru.dbotthepony.mc.otm.block.decorative.CargoCrateBlock
import ru.dbotthepony.mc.otm.block.IDroppableContainer 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.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerHooks import ru.dbotthepony.mc.otm.container.MatteryContainerHooks
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
@ -43,7 +43,7 @@ import ru.dbotthepony.mc.otm.registry.MSoundEvents
class CargoCrateBlockEntity( class CargoCrateBlockEntity(
p_155229_: BlockPos, p_155229_: BlockPos,
p_155230_: BlockState 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) val container = MatteryContainer(this::setChanged, CAPACITY)
private var interactingPlayers = 0 private var interactingPlayers = 0
val handler = container.handler(object : MatteryContainerHooks { val handler = container.handler(object : MatteryContainerHooks {

View File

@ -8,15 +8,15 @@ import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlProvider import ru.dbotthepony.mc.otm.block.entity.IRedstoneControlled
import ru.dbotthepony.mc.otm.block.entity.SynchronizedBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.SynchronizedRedstoneControl import ru.dbotthepony.mc.otm.block.entity.SynchronizedRedstoneControl
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu import ru.dbotthepony.mc.otm.menu.decorative.HoloSignMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks 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() } override val redstoneControl = SynchronizedRedstoneControl(synchronizer) { _, _ -> setChanged() }
var text by synchronizer.string("", name = "text", setter = { value, access, remote -> var text by synchronizer.string("", name = "text", setter = { value, access, remote ->

View File

@ -11,14 +11,12 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.core.TranslatableComponent 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.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.block.entity.tech.BatteryBankBlockEntity
import ru.dbotthepony.mc.otm.capability.FlowDirection import ru.dbotthepony.mc.otm.capability.FlowDirection
import ru.dbotthepony.mc.otm.capability.MatteryCapability 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.map
import ru.dbotthepony.mc.otm.core.nbt.set 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() var gaugeLevel by synchronizer.float()
private set private set

View File

@ -19,7 +19,7 @@ import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import ru.dbotthepony.mc.otm.core.TranslatableComponent 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.capability.matter.*
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode import ru.dbotthepony.mc.otm.graph.matter.IMatterGraphNode
@ -29,7 +29,7 @@ import java.util.ArrayList
import java.util.stream.Stream import java.util.stream.Stream
class MatterPanelBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : 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<MatterPanelMenu>() private val listeners = ArrayList<MatterPanelMenu>()
override val matterNode = Graph6Node<IMatterGraphNode>(this) override val matterNode = Graph6Node<IMatterGraphNode>(this)

View File

@ -24,7 +24,7 @@ import net.minecraft.world.level.block.Block
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import ru.dbotthepony.mc.otm.block.IDroppableContainer 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.core.TranslatableComponent
import ru.dbotthepony.mc.otm.capability.matter.* import ru.dbotthepony.mc.otm.capability.matter.*
import ru.dbotthepony.mc.otm.core.collect.iterator import ru.dbotthepony.mc.otm.core.collect.iterator
@ -40,7 +40,7 @@ import java.util.stream.Stream
@MethodsReturnNonnullByDefault @MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
class PatternStorageBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : 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<IMatterGraphNode>(this) override val matterNode = Graph6Node<IMatterGraphNode>(this)
private val resolverPatterns = LazyOptional.of { this } private val resolverPatterns = LazyOptional.of { this }

View File

@ -10,17 +10,14 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import org.apache.logging.log4j.LogManager 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.IDroppableContainer
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper 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.*
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.facingOne 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.nbt.set
import ru.dbotthepony.mc.otm.core.util.BESubscribeList import ru.dbotthepony.mc.otm.core.util.BESubscribeList
import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu import ru.dbotthepony.mc.otm.menu.tech.BatteryBankMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities 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() var gaugeLevel by synchronizer.float()
private set private set

View File

@ -19,8 +19,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.block.IDroppableContainer import ru.dbotthepony.mc.otm.block.IDroppableContainer
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.capability.* import ru.dbotthepony.mc.otm.capability.*
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage 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.nbt.set
import ru.dbotthepony.mc.otm.core.util.BESubscribeList 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 override val defaultDisplayName: Component
get() = MACHINE_NAME get() = MACHINE_NAME

View File

@ -1,25 +1,17 @@
package ru.dbotthepony.mc.otm.block.entity.tech package ru.dbotthepony.mc.otm.block.entity.tech
import net.minecraft.core.BlockPos 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.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.BlockState
import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities 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.IDroppableContainer
import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryWorkerBlockEntity
import ru.dbotthepony.mc.otm.container.MatteryContainer import ru.dbotthepony.mc.otm.container.MatteryContainer
import ru.dbotthepony.mc.otm.container.MatteryContainerHooks 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.menu.tech.CobblerMenu
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities
@ -31,37 +23,15 @@ class CobblerBlockEntity(blockPos: BlockPos, blockState: BlockState)
override val energy = null override val energy = null
override val droppableContainer = MatteryContainer(this::itemContainerUpdated, CONTAINER_SIZE) 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 { override fun canInsert(slot: Int, stack: ItemStack): Boolean {
return false return false
} }
}) }))
override fun <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> { init {
if (cap === ForgeCapabilities.ITEM_HANDLER) savetable(::droppableContainer, INVENTORY_KEY)
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)
} }
override fun onJobFinish(job: ItemJob): Status { override fun onJobFinish(job: ItemJob): Status {

View File

@ -16,7 +16,7 @@ import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import net.minecraftforge.energy.IEnergyStorage import net.minecraftforge.energy.IEnergyStorage
import ru.dbotthepony.mc.otm.block.tech.EnergyCounterBlock 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.*
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.compat.mekanism.Mattery2MekanismEnergyWrapper 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 ru.dbotthepony.mc.otm.registry.MBlockEntities
import java.util.* 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() var passed by synchronizer.fraction()
private val history = Array(10 * 20) { Decimal.ZERO } private val history = Array(10 * 20) { Decimal.ZERO }

View File

@ -14,7 +14,7 @@ import net.minecraftforge.common.capabilities.Capability
import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.capabilities.ForgeCapabilities
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.block.IDroppableContainer 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.FlowDirection
import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.energy.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.capability.MatteryCapability 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.MBlockEntities
import ru.dbotthepony.mc.otm.registry.MBlocks 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 override val defaultDisplayName: Component
get() = MBlocks.ENERGY_SERVO.name get() = MBlocks.ENERGY_SERVO.name

View File

@ -11,8 +11,7 @@ import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.AABB import net.minecraft.world.phys.AABB
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizerLens import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizerLens
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock import ru.dbotthepony.mc.otm.block.entity.MatteryDeviceBlockEntity
import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity
import ru.dbotthepony.mc.otm.block.entity.WorkerState import ru.dbotthepony.mc.otm.block.entity.WorkerState
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleBlockEntity
import ru.dbotthepony.mc.otm.block.tech.BlockGravitationStabilizer 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.core.math.times
import ru.dbotthepony.mc.otm.registry.MBlockEntities 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_) { MBlockEntities.GRAVITATION_STABILIZER, p_155229_, p_155230_) {
override val defaultDisplayName: Component override val defaultDisplayName: Component

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.block.tech
import net.minecraft.ChatFormatting import net.minecraft.ChatFormatting
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component import net.minecraft.network.chat.Component
import net.minecraft.world.item.BlockItem 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.CollisionContext
import net.minecraft.world.phys.shapes.VoxelShape import net.minecraft.world.phys.shapes.VoxelShape
import ru.dbotthepony.mc.otm.block.RotatableMatteryBlock 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.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.entity.WorkerState
import ru.dbotthepony.mc.otm.block.getShapeForEachState import ru.dbotthepony.mc.otm.block.getShapeForEachState
import ru.dbotthepony.mc.otm.capability.energy.GeneratorEnergyStorage 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.container.MatteryContainer
import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.TranslatableComponent
import ru.dbotthepony.mc.otm.core.get 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.core.nbt.map
import ru.dbotthepony.mc.otm.oncePre import ru.dbotthepony.mc.otm.oncePre
import ru.dbotthepony.mc.otm.registry.MBlockEntities import ru.dbotthepony.mc.otm.registry.MBlockEntities

View File

@ -13,9 +13,10 @@ import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraftforge.common.ForgeConfigSpec import net.minecraftforge.common.ForgeConfigSpec
import net.minecraftforge.common.util.INBTSerializable import net.minecraftforge.common.util.INBTSerializable
import net.minecraftforge.common.util.LazyOptional 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.ConciseBalanceValues
import ru.dbotthepony.mc.otm.config.VerboseBalanceValues 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.capability.FlowDirection
import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.core.math.defineDecimal import ru.dbotthepony.mc.otm.core.math.defineDecimal

View File

@ -10,10 +10,8 @@ import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation import net.minecraft.resources.ResourceLocation
import net.minecraft.world.level.levelgen.XoroshiroRandomSource import net.minecraft.world.level.levelgen.XoroshiroRandomSource
import net.minecraftforge.client.event.ModelEvent 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.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.matter.MatterCapacitorBankBlockEntity
import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity import ru.dbotthepony.mc.otm.block.entity.tech.BatteryBankBlockEntity
import ru.dbotthepony.mc.otm.client.minecraft 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.ImmutableList
import ru.dbotthepony.mc.otm.core.get import ru.dbotthepony.mc.otm.core.get
import ru.dbotthepony.mc.otm.core.math.BlockRotationFreedom 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.facingOne
import ru.dbotthepony.mc.otm.core.math.rotate import ru.dbotthepony.mc.otm.core.math.rotate
import ru.dbotthepony.mc.otm.core.math.rotateY import ru.dbotthepony.mc.otm.core.math.rotateY
@ -32,7 +29,7 @@ import ru.dbotthepony.mc.otm.nanoTime
import java.util.function.Supplier import java.util.function.Supplier
import kotlin.math.PI import kotlin.math.PI
abstract class BankRenderer<T : MatteryBlockEntity>(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<T> { abstract class BankRenderer<T : MatteryDeviceBlockEntity>(private val context: BlockEntityRendererProvider.Context) : BlockEntityRenderer<T> {
protected abstract fun gaugeLevel(entity: T): Float protected abstract fun gaugeLevel(entity: T): Float
protected abstract val texture: AbstractMatterySprite protected abstract val texture: AbstractMatterySprite
protected abstract val models: List<BakedModel> protected abstract val models: List<BakedModel>

View File

@ -9,12 +9,13 @@ import net.minecraft.world.Container
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.level.block.entity.BlockEntity 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.ifHas
import ru.dbotthepony.mc.otm.core.nbt.set import ru.dbotthepony.mc.otm.core.nbt.set
import java.util.* import java.util.*
@Suppress("UNUSED") @Suppress("UNUSED")
open class MatteryContainer(val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack> { open class MatteryContainer(val watcher: Runnable, private val size: Int) : Container, Iterable<ItemStack>, INBTSerializable<Tag?> {
constructor(watcher: BlockEntity, size: Int) : this(watcher::setChanged, size) constructor(watcher: BlockEntity, size: Int) : this(watcher::setChanged, size)
constructor(size: Int) : this({}, size) constructor(size: Int) : this({}, size)
@ -107,7 +108,7 @@ open class MatteryContainer(val watcher: Runnable, private val size: Int) : Cont
setChanged() setChanged()
} }
fun deserializeNBT(tag: Tag?) { override fun deserializeNBT(tag: Tag?) {
when (tag) { when (tag) {
is CompoundTag -> deserializeNBT(tag) is CompoundTag -> deserializeNBT(tag)
is ListTag -> 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() if (ignoreChangeNotifications == 0) watcher.run()
} }
fun serializeNBT(): ListTag { override fun serializeNBT(): ListTag {
return ListTag().also { return ListTag().also {
for ((i, item) in slots.withIndex()) { for ((i, item) in slots.withIndex()) {
if (!item.isEmpty) { if (!item.isEmpty) {

View File

@ -1,5 +1,6 @@
package ru.dbotthepony.mc.otm.core.math package ru.dbotthepony.mc.otm.core.math
import com.google.common.collect.ImmutableMap
import net.minecraft.core.BlockPos import net.minecraft.core.BlockPos
import net.minecraft.core.Direction import net.minecraft.core.Direction
import net.minecraft.core.Vec3i 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.Capability
import net.minecraftforge.common.capabilities.ICapabilityProvider import net.minecraftforge.common.capabilities.ICapabilityProvider
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import java.util.Collections
import java.util.EnumMap import java.util.EnumMap
internal inline val Direction.blockRotation internal inline val Direction.blockRotation
@ -25,6 +27,10 @@ operator fun BlockPos.plus(other: BlockRotation): BlockPos {
return this + other.normal 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 * 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 bottom: Direction = top.opposite
val back: Direction = front.opposite val back: Direction = front.opposite
private val _dir2Side = EnumMap<Direction, RelativeSide>(Direction::class.java)
private val _side2Dir = EnumMap<RelativeSide, Direction>(RelativeSide::class.java)
val dir2Side: Map<Direction, RelativeSide> = Collections.unmodifiableMap(_dir2Side)
val side2Dir: Map<RelativeSide, Direction> = 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 component1() = front
operator fun component2() = top operator fun component2() = top
operator fun component3() = left operator fun component3() = left

View File

@ -1,5 +1,8 @@
package ru.dbotthepony.mc.otm.core.nbt 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.ByteArrayTag
import net.minecraft.nbt.ByteTag import net.minecraft.nbt.ByteTag
import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.CompoundTag
@ -10,11 +13,14 @@ import net.minecraft.nbt.IntTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.LongArrayTag import net.minecraft.nbt.LongArrayTag
import net.minecraft.nbt.LongTag import net.minecraft.nbt.LongTag
import net.minecraft.nbt.NbtAccounter
import net.minecraft.nbt.NbtUtils import net.minecraft.nbt.NbtUtils
import net.minecraft.nbt.ShortTag import net.minecraft.nbt.ShortTag
import net.minecraft.nbt.StringTag import net.minecraft.nbt.StringTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import net.minecraft.world.item.ItemStack 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 import java.util.UUID
operator fun CompoundTag.set(index: String, value: Tag) = put(index, value) 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 val value = this[key] as? IntArrayTag ?: return null
return NbtUtils.loadUUID(value) 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)
}

View File

@ -7,7 +7,7 @@ import net.minecraftforge.network.NetworkDirection
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import org.apache.logging.log4j.LogManager import org.apache.logging.log4j.LogManager
import ru.dbotthepony.mc.otm.android.feature.ItemEntityDataPacket 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 ru.dbotthepony.mc.otm.client.minecraft
import java.util.function.Supplier import java.util.function.Supplier
@ -22,7 +22,7 @@ class BlockEntitySyncPacket(val position: BlockPos, val buffer: ByteArray, val v
context.enqueueWork { context.enqueueWork {
val level = minecraft.player?.level ?: return@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 { try {
blockEntity.synchronizer.read(FastByteArrayInputStream(buffer, 0, validBytes)) blockEntity.synchronizer.read(FastByteArrayInputStream(buffer, 0, validBytes))