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.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 */) {

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

View File

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

View File

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

View File

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

View File

@ -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<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) {
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<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 {
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<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.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)

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.network.FieldSynchronizer
interface IRedstoneControlProvider {
interface IRedstoneControlled {
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 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()

View File

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

View File

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

View File

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

View File

@ -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<MatterPanelMenu>()
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.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<IMatterGraphNode>(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.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

View File

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

View File

@ -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 <T> getCapability(cap: Capability<T>, side: Direction?): LazyOptional<T> {
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 {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<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 val texture: AbstractMatterySprite
protected abstract val models: List<BakedModel>

View File

@ -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<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(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) {

View File

@ -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, 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 component2() = top
operator fun component3() = left

View File

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

View File

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