From 64557a77f5b08cacee36cfc110c51ea0df4d77f2 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sat, 1 Jan 2022 00:46:27 +0700 Subject: [PATCH] Functioning again storage network --- .../mc/otm/OverdriveThatMatters.java | 2 +- .../capability/drive/IItemMatteryDrive.java | 26 -- .../capability/drive/IItemViewListener.java | 15 - .../otm/capability/drive/IMatteryDrive.java | 28 -- .../item/ItemPortableCondensationDrive.java | 11 +- src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt | 2 + .../mc/otm/capability/drive/API.kt | 44 ++ .../capability/drive/AbstractMatteryDrive.kt | 324 +++++++-------- .../mc/otm/capability/drive/DrivePool.kt | 390 ++++++++++-------- .../otm/capability/drive/ItemMatteryDrive.kt | 156 +++---- .../graph/storage/BasicStorageGraphNode.kt | 8 +- .../mc/otm/graph/storage/IStorageGraphNode.kt | 17 +- .../otm/graph/storage/StorageNetworkGraph.kt | 34 +- .../mc/otm/menu/ItemMonitorMenu.kt | 2 +- .../ru/dbotthepony/mc/otm/storage/API.kt | 18 +- 15 files changed, 504 insertions(+), 573 deletions(-) delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemMatteryDrive.java delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemViewListener.java delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/capability/drive/IMatteryDrive.java create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index cbe1d0de0..3f8563157 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -141,7 +141,7 @@ public class OverdriveThatMatters { FMLJavaModLoadingContext.get().getModEventBus().register(Registry.AndroidResearch.class); FMLJavaModLoadingContext.get().getModEventBus().register(Registry.Stats.class); - MinecraftForge.EVENT_BUS.register(DrivePool.class); + MinecraftForge.EVENT_BUS.register(DrivePool.INSTANCE); MinecraftForge.EVENT_BUS.register(ItemPortableCondensationDrive.class); FMLJavaModLoadingContext.get().getModEventBus().addListener(AndroidCapability::registerEffects); diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemMatteryDrive.java b/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemMatteryDrive.java deleted file mode 100644 index c89ebde7b..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemMatteryDrive.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.dbotthepony.mc.otm.capability.drive; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import ru.dbotthepony.mc.otm.storage.IStorageTuple; -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.util.List; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public interface IItemMatteryDrive extends IMatteryDrive { - /** - * @param item - * @return all items belonging to passed class - */ - List> findItems(Item item); - - /** - * @param stack - * @return all items that match this itemstack - */ - List> findItems(ItemStack stack); -} diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemViewListener.java b/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemViewListener.java deleted file mode 100644 index 4dec37744..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IItemViewListener.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.dbotthepony.mc.otm.capability.drive; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.world.item.ItemStack; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.util.UUID; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public interface IItemViewListener { - void addViewItem(ItemStack stack, UUID id_upstream); - void changeViewItem(UUID id_upstream, int new_count); - void removeViewItem(UUID id_upstream); -} diff --git a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IMatteryDrive.java b/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IMatteryDrive.java deleted file mode 100644 index 21812366d..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/capability/drive/IMatteryDrive.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.dbotthepony.mc.otm.capability.drive; - -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import ru.dbotthepony.mc.otm.core.Fraction; -import ru.dbotthepony.mc.otm.storage.*; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.math.BigDecimal; -import java.util.List; -import java.util.UUID; - -@ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -public interface IMatteryDrive extends IStorageComponent { - boolean isDirty(); - void markDirty(); - void markClean(); - - Fraction getStoredCount(); - Fraction getCapacity(); - - // not extending INBTSerializable to avoid serializing it as forgecaps - CompoundTag serializeNBT(); - void deserializeNBT(CompoundTag nbt); -} diff --git a/src/main/java/ru/dbotthepony/mc/otm/item/ItemPortableCondensationDrive.java b/src/main/java/ru/dbotthepony/mc/otm/item/ItemPortableCondensationDrive.java index 7abaa082f..870b76770 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/item/ItemPortableCondensationDrive.java +++ b/src/main/java/ru/dbotthepony/mc/otm/item/ItemPortableCondensationDrive.java @@ -101,12 +101,19 @@ public class ItemPortableCondensationDrive extends Item { private class DriveCapability implements ICapabilityProvider { private UUID uuid; private final ItemStack stack; + private final LazyOptional resolver = LazyOptional.of(() -> { + if (!DrivePool.isLegalAccess()) { + return ItemMatteryDrive.DUMMY; + } + + final var uuid = this.uuid; + return DrivePool.get(uuid, (tag) -> { - var drive = new ItemMatteryDrive(capacity); + var drive = new ItemMatteryDrive(capacity, uuid); drive.deserializeNBT(tag); return drive; - }, () -> new ItemMatteryDrive(capacity)); + }, () -> new ItemMatteryDrive(capacity, uuid)); }); DriveCapability(ItemStack stack) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt index 7501043aa..c14adea09 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt @@ -28,6 +28,8 @@ operator fun CompoundTag.set(s: String, value: Short) = putShort(s, value) operator fun CompoundTag.set(s: String, value: Long) = putLong(s, value) operator fun CompoundTag.set(s: String, value: String) = putString(s, value) operator fun CompoundTag.set(s: String, value: Boolean) = putBoolean(s, value) +operator fun CompoundTag.set(s: String, value: ByteArray) = putByteArray(s, value) +operator fun CompoundTag.set(s: String, value: LongArray) = putLongArray(s, value) inline fun CompoundTag.ifHas(s: String, consumer: (Tag) -> Unit) { val tag = get(s) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt new file mode 100644 index 000000000..780c18281 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/API.kt @@ -0,0 +1,44 @@ +package ru.dbotthepony.mc.otm.capability.drive + +import net.minecraft.nbt.CompoundTag +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import ru.dbotthepony.mc.otm.core.Fraction +import ru.dbotthepony.mc.otm.storage.IStorageComponent +import ru.dbotthepony.mc.otm.storage.IStorageStack +import ru.dbotthepony.mc.otm.storage.IStorageTuple +import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import java.util.* + +interface IItemMatteryDrive : IMatteryDrive { + /** + * @param item + * @return all items belonging to passed class + */ + fun findItems(item: Item): Collection> + + /** + * @param stack + * @return all items that match this itemstack + */ + fun findItems(stack: ItemStack): Collection> +} + +interface IItemViewListener { + fun addViewItem(stack: ItemStack, id_upstream: UUID) + fun changeViewItem(id_upstream: UUID, new_count: Int) + fun removeViewItem(id_upstream: UUID) +} + +interface IMatteryDrive : IStorageComponent { + val uuid: UUID + + var isDirty: Boolean + + val storedCount: Fraction + val driveCapacity: Fraction + + // not extending INBTSerializable to avoid serializing it as forgecaps + fun serializeNBT(): CompoundTag + fun deserializeNBT(nbt: CompoundTag) +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt index 10017db73..ec9a0eb55 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/AbstractMatteryDrive.kt @@ -1,267 +1,233 @@ -package ru.dbotthepony.mc.otm.capability.drive; +package ru.dbotthepony.mc.otm.capability.drive -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; -import ru.dbotthepony.mc.otm.core.Fraction; -import ru.dbotthepony.mc.otm.storage.*; +import ru.dbotthepony.mc.otm.core.Fraction.Companion.deserializeNBT +import ru.dbotthepony.mc.otm.storage.IStorageStack +import kotlin.jvm.JvmOverloads +import java.util.HashMap +import ru.dbotthepony.mc.otm.storage.IStorageTuple +import java.util.UUID +import ru.dbotthepony.mc.otm.storage.StorageObjectTuple +import ru.dbotthepony.mc.otm.storage.IStorageListener +import ru.dbotthepony.mc.otm.storage.StorageTuple +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.ListTag +import net.minecraft.nbt.Tag +import ru.dbotthepony.mc.otm.core.Fraction +import ru.dbotthepony.mc.otm.ifHas +import ru.dbotthepony.mc.otm.set +import java.util.ArrayList +import java.util.HashSet -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; -import java.math.BigDecimal; -import java.util.*; -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -abstract public class AbstractMatteryDrive implements IMatteryDrive { - protected final HashMap>> items = new HashMap<>(); - protected final HashMap> items_by_id = new HashMap<>(); +abstract class AbstractMatteryDrive @JvmOverloads constructor( + override var driveCapacity: Fraction, + override val uuid: UUID = UUID.randomUUID(), + var maxDifferentStacks: Int = 0xFFFF +) : IMatteryDrive { - abstract public StorageObjectTuple identity(); + @JvmField + protected val storedStacks = HashMap>>() - protected boolean dirty = false; - protected int different_stacks = 0; - protected Fraction stored = Fraction.ZERO; - protected int max_different_stacks; - protected Fraction capacity; + @JvmField + protected val storedByID = HashMap>() + abstract fun identity(): StorageObjectTuple + override var isDirty = false + set(value) { + if (value != field && value && DrivePool.isLegalAccess()) { + DrivePool.markDirty(uuid) + } - public AbstractMatteryDrive(Fraction capacity, int max_different_stacks) { - this.capacity = capacity; - this.max_different_stacks = max_different_stacks; - } + field = value + } - public AbstractMatteryDrive(Fraction capacity) { - this(capacity, 0xFFFF); - } + var storedDifferentStacks = 0 - @Override - public boolean isDirty() { - return dirty; - } + override var storedCount = Fraction.ZERO + protected set - @Override - public void markDirty() { - dirty = true; - } + override fun insertStack(stack: T, simulate: Boolean): T { + val maxInsert = driveCapacity.minus(storedCount).min(stack.count) + if (maxInsert <= Fraction.ZERO) return stack - @Override - public void markClean() { - dirty = false; - } + val listing = storedStacks.computeIfAbsent(stack.partitionKey()) { ArrayList() } - @Override - public Fraction getStoredCount() { - return stored; - } - - @Override - public Fraction getCapacity() { - return capacity; - } - - @Nonnull - @Override - @SuppressWarnings("unchecked") - public T insertStack(T stack, boolean simulate) { - var max_insert = getCapacity().minus(getStoredCount()).min(stack.getCount()); - - if (max_insert.compareTo(Fraction.ZERO) <= 0) - return stack; - - final var listing = items.computeIfAbsent(stack.partitionKey(), (key) -> new ArrayList<>()); - - for (var state : listing) { + for (state in listing) { if (state.stack().sameItem(stack)) { if (!simulate) { - state.stack().grow(max_insert); - stored = stored.plus(max_insert); + state.stack().grow(maxInsert) + storedCount += maxInsert - for (var listener : listeners2) { - listener.changeObject(state.id(), state.stack().getCount()); + for (listener in listeners) { + listener.changeObject(state.id(), state.stack().count) } - markDirty(); + isDirty = true } - final var copy_item = (T) stack.copy(); - copy_item.shrink(max_insert); - return copy_item; + val copy = stack.copy() as T + copy.shrink(maxInsert) + return copy } } - if (different_stacks >= max_different_stacks) { - return stack; + if (storedDifferentStacks >= maxDifferentStacks) { + return stack } if (!simulate) { - different_stacks++; - stored = stored.plus(max_insert); + storedDifferentStacks++ + storedCount = storedCount.plus(maxInsert) - final var copy = (T) stack.copy(); - copy.setCount(max_insert); - final var state = new StorageTuple<>(UUID.randomUUID(), copy); - listing.add(state); - items_by_id.put(state.id(), state); + val copy = stack.copy() as T + copy.count = maxInsert - for (var listener : listeners2) { - listener.addObject(state.stack(), state.id(), this); + val state = StorageTuple(UUID.randomUUID(), copy) + listing.add(state) + storedByID[state.id()] = state + + for (listener in listeners) { + listener.addObject(state.stack(), state.id(), this) } - markDirty(); + isDirty = true } - final var copy_item = (T) stack.copy(); - copy_item.shrink(max_insert); - return copy_item; + val copy = stack.copy() as T + copy.shrink(maxInsert) + return copy } - @Nonnull - @Override - @SuppressWarnings("unchecked") - public T extractStack(UUID id, Fraction amount, boolean simulate) { - var get = items_by_id.get(id); + override fun extractStack(id: UUID, amount: Fraction, simulate: Boolean): T { + var amount = amount + val get = storedByID[id] ?: return identity().empty() - if (get == null) - return identity().empty(); + if (amount <= Fraction.ZERO) + amount = get.stack.getMaxStackSize().orElse(get.stack.count) - if (amount.compareTo(Fraction.ZERO) <= 0) - amount = get.stack().getMaxStackSize().orElse(get.stack().getCount()); + amount = amount.min(get.stack.count) - amount = amount.min(get.stack().getCount()); + if (amount <= Fraction.ZERO) + return identity().empty() - if (amount.compareTo(Fraction.ZERO) <= 0) - return identity().empty(); - - final var copy = (T) get.stack().copy(); - copy.setCount(amount); + val copy = get.stack.copy() as T + copy.count = amount if (!simulate) { - if (amount.compareTo(get.stack().getCount()) == 0) { - var listing = items.get(get.stack().partitionKey()); - listing.remove(get); - different_stacks--; + if (amount.compareTo(get.stack.count) == 0) { + val listing = storedStacks[get.stack.partitionKey()]!! + listing.remove(get) + storedDifferentStacks-- - for (var listener : listeners2) { - listener.removeObject(get.id()); + for (listener in listeners) { + listener.removeObject(get.id()) } - if (listing.size() == 0) { - items.remove(get.stack().partitionKey()); + if (listing.size == 0) { + storedStacks.remove(get.stack.partitionKey()) } } - stored = stored.minus(amount); - get.stack().shrink(amount); + storedCount = storedCount.minus(amount) + get.stack.shrink(amount) - if (get.stack().getCount().compareTo(Fraction.ZERO) != 0) { - for (var listener : listeners2) { - listener.changeObject(get.id(), get.stack().getCount()); + if (get.stack.count.compareTo(Fraction.ZERO) != 0) { + for (listener in listeners) { + listener.changeObject(get.id(), get.stack.count) } } - markDirty(); + isDirty = true } - return copy; + return copy } - @Nullable - abstract protected CompoundTag serializeStack(IStorageTuple item); + protected abstract fun serializeStack(item: IStorageTuple): CompoundTag? + protected abstract fun deserializeStack(tag: CompoundTag): T? - @Override - public CompoundTag serializeNBT() { - final var compound = new CompoundTag(); + override fun serializeNBT(): CompoundTag { + val compound = CompoundTag() - compound.putString("capacity", capacity.toString()); - compound.putInt("max_different_stacks", max_different_stacks); + compound["capacity"] = driveCapacity.serializeNBT() + compound["max_different_stacks"] = maxDifferentStacks - var list_items = new ListTag(); - compound.put("items", list_items); + val list = ListTag() + compound["items"] = list - for (var entry : items.entrySet()) { - for (var stack : entry.getValue()) { - CompoundTag entry_compound; + for (value in storedStacks.values) { + for (stack in value) { + val serialized = serializeStack(stack) - if ((entry_compound = serializeStack(stack)) != null) { - entry_compound.putLongArray("id", new long[] { stack.id().getMostSignificantBits(), stack.id().getLeastSignificantBits() }); - list_items.add(entry_compound); + if (serialized != null) { + serialized["id"] = longArrayOf(stack.id().mostSignificantBits, stack.id().leastSignificantBits) + list.add(serialized) } } } - - return compound; + return compound } - @Nullable - abstract protected T deserializeStack(CompoundTag tag); + override fun deserializeNBT(nbt: CompoundTag) { + storedStacks.clear() + storedByID.clear() + storedCount = Fraction.ZERO + storedDifferentStacks = 0 - @Override - public void deserializeNBT(CompoundTag nbt) { - items.clear(); - items_by_id.clear(); - stored = Fraction.ZERO; - different_stacks = 0; + nbt.ifHas("capacity") { + driveCapacity = deserializeNBT(it) + } - if (nbt.contains("capacity")) - capacity = Fraction.deserializeNBT(nbt.get("capacity")); + maxDifferentStacks = nbt.getInt("max_different_stacks") - max_different_stacks = nbt.getInt("max_different_stacks"); - - for (var _entry : nbt.getList("items", Tag.TAG_COMPOUND)) { - if (_entry instanceof CompoundTag entry) { - final var stack = deserializeStack(entry); + for (entry in nbt.getList("items", Tag.TAG_COMPOUND.toInt())) { + if (entry is CompoundTag) { + val stack = deserializeStack(entry) if (stack != null) { - stored = stored.plus(stack.getCount()); - different_stacks++; - final var id = entry.getLongArray("id"); + storedCount = storedCount.plus(stack.count) + storedDifferentStacks++ - final var tuple = new StorageTuple<>(id.length == 2 ? new UUID(id[0], id[1]) : UUID.randomUUID(), stack); - items.computeIfAbsent(stack.partitionKey(), (key) -> new ArrayList<>()).add(tuple); - items_by_id.put(tuple.id(), tuple); + val id = entry.getLongArray("id") + val tuple = StorageTuple(if (id.size == 2) UUID(id[0], id[1]) else UUID.randomUUID(), stack) + + storedStacks.computeIfAbsent(stack.partitionKey()) { ArrayList() }.add(tuple) + storedByID[tuple.id] = tuple } } } } - @Override - public Class storageIdentity() { - return identity().identity(); + override fun storageIdentity(): Class { + return identity().identity() } - @Override - public T getStack(UUID id) { - var get = items_by_id.get(id); - return get == null ? identity().empty() : get.stack(); + override fun getStack(id: UUID): T { + return storedByID[id]?.stack ?: identity().empty() } - @Override - public List> getStacks() { - int amount = 0; + override fun getStacks(): List> { + var amount = 0 - for (var listing : items.values()) - amount += listing.size(); + for (listing in storedStacks.values) + amount += listing.size - var list = new ArrayList>(amount); + val list = ArrayList>(amount) - for (var listing : items.values()) { - list.addAll(listing); + for (listing in storedStacks.values) { + list.addAll(listing) } - return list; + return list } - protected final HashSet> listeners2 = new HashSet<>(); + @JvmField + protected val listeners = HashSet>() - @Override - public boolean addListener(IStorageListener listener) { - return listeners2.add(listener); + override fun addListener(listener: IStorageListener): Boolean { + return listeners.add(listener) } - @Override - public boolean removeListener(IStorageListener listener) { - return listeners2.remove(listener); + override fun removeListener(listener: IStorageListener): Boolean { + return listeners.remove(listener) } -} +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt index 14af0abed..4046b7515 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/DrivePool.kt @@ -1,30 +1,24 @@ -package ru.dbotthepony.mc.otm.capability.drive; - -import net.minecraft.CrashReport; -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.ReportedException; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtIo; -import net.minecraft.server.MinecraftServer; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.world.WorldEvent; -import net.minecraftforge.eventbus.api.EventPriority; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.event.server.ServerStartingEvent; -import net.minecraftforge.event.server.ServerStoppingEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.UUID; -import java.util.concurrent.locks.LockSupport; -import java.util.function.Function; -import java.util.function.Supplier; +package ru.dbotthepony.mc.otm.capability.drive +import java.util.HashMap +import java.util.UUID +import net.minecraft.ReportedException +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.NbtIo +import net.minecraft.CrashReport +import net.minecraft.server.MinecraftServer +import java.util.concurrent.locks.LockSupport +import net.minecraftforge.eventbus.api.SubscribeEvent +import net.minecraftforge.eventbus.api.EventPriority +import net.minecraftforge.event.TickEvent.ServerTickEvent +import net.minecraftforge.event.TickEvent +import net.minecraftforge.event.server.ServerAboutToStartEvent +import net.minecraftforge.event.server.ServerStoppingEvent +import net.minecraftforge.event.world.WorldEvent +import org.apache.logging.log4j.LogManager +import java.io.File +import java.lang.ref.WeakReference +import java.util.ArrayList /** * Let me answer everyone's questions about: @@ -35,235 +29,279 @@ import java.util.function.Supplier; * 0. This data can get very large very quickly, even when playing singleplayer (and much quicker and bigger when hosting a dedicated server) * 1. This data can not be stored inside ItemStack.ForgeCaps due to it's size * 2. This data can not be stored inside unshared (server only) nbt tag because, again, mods prone to use and interact with - * it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large" - * network kicks, often locking players out of server + * it wrong, causing loss of stored data or mods exposing full content of a drive inside their own tag (which cause very real "NBT size too large" + * network kicks, often locking players out of server/singleplayer worlds * 3. net.minecraft.world.level.saveddata.SaveData is for storing everything inside one dat file, which - * is performance tanking, because we have to write *entire* NBT on each save, not the data of Drive which got changed + * is performance tanking, because we have to write *entire* NBT on each save, not the data of Drives that are dirty */ -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class DrivePool { - private static final Logger LOGGER = LogManager.getLogger(); +@Suppress("unused") +object DrivePool { + private val LOGGER = LogManager.getLogger() + private val pool = HashMap() + private var thread: Thread? = null + private var stopping = false + private var exception: ReportedException? = null + private const val DATA_PATH = "data/otm_drives" - public static class Entry { - public final UUID id; - public final IMatteryDrive drive; - public long last_access = System.currentTimeMillis(); + @JvmStatic + operator fun > get( + id: UUID, + factory_stored: (CompoundTag) -> T, + factory_empty: () -> T + ): T { + if (!isLegalAccess()) + throw IllegalAccessException("Can not access drive pool from outside of server thread.") - Entry(UUID id, IMatteryDrive drive) { - this.id = id; - this.drive = drive; - } - - @Nullable - public CompoundTag sync() { - if (!drive.isDirty()) { - return null; - } - - var tag = drive.serializeNBT(); - drive.markClean(); - last_access = System.currentTimeMillis(); - return tag; - } - - public IMatteryDrive access() { - last_access = System.currentTimeMillis(); - return drive; - } - } - - private static HashMap pool = new HashMap<>(); - private static Thread thread; - private static boolean stopping = false; - private static ReportedException exception; - public static final String DATA_PATH = "data/otm_drives"; - - public static > T get(UUID id, Function factory_stored, Supplier factory_empty) { - var get = pool.get(id); + val get = pool[id] if (get != null) { - return (T) get.access(); + val accessed = get.access() + + if (accessed != null) { + return accessed as T + } } - var file = new File(base_directory, id.toString() + ".dat"); + val file = File(baseDirectory, "$id.dat") if (file.exists()) { try { - var factorize = factory_stored.apply(NbtIo.readCompressed(file)); - pool.put(id, new Entry(id, factorize)); - return factorize; - } catch(Throwable error) { - var report = new CrashReport("Reading OTM Drive from disk", error); - var category = report.addCategory("Corrupt drive"); - category.setDetail("UUID", id); - throw new ReportedException(report); + val factorize = factory_stored(NbtIo.readCompressed(file)) + pool[id] = WeakDriveReference(factorize) + return factorize + } catch (error: Throwable) { + val report = CrashReport("Reading OTM Drive from disk", error) + val category = report.addCategory("Corrupt drive") + category.setDetail("UUID", id) + throw ReportedException(report) } } - var factorize = factory_empty.get(); - pool.put(id, new Entry(id, factorize)); - - return factorize; + val factorize = factory_empty() + pool[id] = WeakDriveReference(factorize) + return factorize } - public static void put(UUID id, IMatteryDrive drive) { - pool.put(id, new Entry(id, drive)); + @JvmStatic + fun put(id: UUID, drive: IMatteryDrive<*>) { + if (!isLegalAccess()) + throw IllegalAccessException("Can not access drive pool from outside of server thread.") + + pool[id] = WeakDriveReference(drive) } - private record BacklogLine(UUID file, CompoundTag tag, IMatteryDrive drive) { - @Override - public boolean equals(Object obj) { - return obj instanceof BacklogLine line && line.file.equals(file) || obj instanceof UUID uuid && uuid.equals(file); - } + @JvmStatic + fun markDirty(id: UUID) { + if (!isLegalAccess()) + throw IllegalAccessException("Can not access drive pool from outside of server thread.") + + pool[id]?.access() } - private static ArrayList backlog = new ArrayList<>(); - private static File base_directory; + private var backlog = ArrayList() + private var baseDirectory: File? = null + private var serverThread: Thread? = null - private static void thread(MinecraftServer server) { + private fun thread(server: MinecraftServer) { while (true) { - LockSupport.park(); + LockSupport.park() if (stopping) { - return; + return } try { // TODO: Syncing status with main thread, since server stop can occur during work. - sync(); - } catch (ReportedException error) { - exception = error; - return; + sync() + } catch (error: ReportedException) { + exception = error + return } } } @SubscribeEvent(priority = EventPriority.LOWEST) - public static void onServerPostTick(TickEvent.ServerTickEvent event) { + fun onServerPostTick(event: ServerTickEvent) { if (event.phase == TickEvent.Phase.END) { if (exception != null) { - throw exception; + throw exception!! } } } - @SubscribeEvent(priority = EventPriority.HIGHEST) - public static void serverStartEvent(ServerStartingEvent event) { - if (thread != null && thread.isAlive()) { - LOGGER.error("FMLServerStartedEvent fired twice."); - LOGGER.error("Attempting to start another DrivePool I/O thread when already running one!"); - return; - } - - pool.clear(); - - final var server = event.getServer(); - base_directory = new File(server.storageSource.getWorldDir().toFile(), DATA_PATH); - base_directory.mkdirs(); - - stopping = false; - thread = new Thread(null, () -> DrivePool.thread(server), "Overdrive That Matters DrivePool IO"); - thread.start(); + /** + * Returns whenever running on server thread. Calling [get], [put] or [markDirty] will throw an exception if this returns false. + * + * If you are making your own drive for your own storage stack, feed code outside of server thread (e.g. client code) dummy disk. + */ + @JvmStatic + fun isLegalAccess(): Boolean { + return serverThread == null || Thread.currentThread() === serverThread } @SubscribeEvent(priority = EventPriority.HIGHEST) - public static void serverStopEvent(ServerStoppingEvent event) { - if (thread != null && thread.isAlive()) { - stopping = true; - LockSupport.unpark(thread); + fun serverStartEvent(event: ServerAboutToStartEvent) { + if (thread != null && thread!!.isAlive) { + LOGGER.error("FMLServerStartedEvent fired twice.") + LOGGER.error("Attempting to start another DrivePool I/O thread when already running one!") + return + } + + pool.clear() + + val server = event.server + baseDirectory = File(server.storageSource.worldDir.toFile(), DATA_PATH) + baseDirectory!!.mkdirs() + + serverThread = Thread.currentThread() + + stopping = false + thread = Thread(null, { thread(server) }, "Overdrive That Matters DrivePool IO") + thread!!.start() + } + + @SubscribeEvent(priority = EventPriority.HIGHEST) + fun serverStopEvent(event: ServerStoppingEvent) { + val thread = thread + + if (thread != null && thread.isAlive) { + stopping = true + LockSupport.unpark(thread) } if (exception == null) { - writeBacklog(); - sync(); + writeBacklog() + sync() } - pool.clear(); + pool.clear() } @SubscribeEvent - public static void onWorldSave(WorldEvent.Save event) { - writeBacklog(); + fun onWorldSave(event: WorldEvent.Save) { + writeBacklog() } - public static void writeBacklog() { - var sync_required = false; - ArrayList remove_keys = null; + private fun writeBacklog() { + var needsSync = false + var removeKeys: ArrayList? = null - for (var entry : pool.entrySet()) { - var tag = entry.getValue().sync(); + for ((key, value) in pool) { + val tag = value.sync() if (tag != null) { - LOGGER.info("Serializing OTM Drive {}", entry.getKey()); - - var index = backlog.indexOf(entry.getKey()); + LOGGER.info("Serializing OTM Drive {}", key) + val index = backlog.indexOf(key) if (index != -1) { - backlog.set(index, new BacklogLine(entry.getKey(), tag, entry.getValue().drive)); + backlog[index] = BacklogLine(key, tag, value.drive()!!) } else { - backlog.add(new BacklogLine(entry.getKey(), tag, entry.getValue().drive)); + backlog.add(BacklogLine(key, tag, value.drive()!!)) } - sync_required = true; - } else if (entry.getValue().last_access < System.currentTimeMillis() - 600_000) { - // no access or changes in 600 seconds - unload - if (remove_keys == null) - remove_keys = new ArrayList<>(); + needsSync = true + } else if (!value.valid()) { + if (removeKeys == null) + removeKeys = ArrayList() - remove_keys.add(entry.getKey()); + removeKeys.add(key) } } - if (remove_keys != null) { - for (var key : remove_keys) { - pool.remove(key); + if (removeKeys != null) { + for (key in removeKeys) { + pool.remove(key) } } - if (sync_required) { - LockSupport.unpark(thread); + if (needsSync) { + LockSupport.unpark(thread) } } - public static void sync() { - var copy = backlog; - backlog = new ArrayList<>(); + private fun sync() { + val copy = backlog + backlog = ArrayList() - for (var entry : copy) { + for (entry in copy) { try { - LOGGER.info("Syncing OTM Drive {}", entry.file); - var old_file = new File(base_directory, entry.file + ".dat_old"); + LOGGER.info("Syncing OTM Drive {}", entry.file) - if (old_file.exists()) { - if (!old_file.delete()) { - throw new IllegalStateException("Unable to delete old dat file"); - } + val oldFile = File(baseDirectory, entry.file.toString() + ".dat_old") + + if (oldFile.exists()) { + check(oldFile.delete()) { "Unable to delete old dat file" } } - var file = new File(base_directory, entry.file + ".dat"); + val newFile = File(baseDirectory, entry.file.toString() + ".dat") - if (file.exists()) { - if (!file.renameTo(old_file)) { - throw new IllegalStateException("Unable to move old dat file"); - } + if (newFile.exists()) { + check(newFile.renameTo(oldFile)) { "Unable to move old dat file" } } - NbtIo.writeCompressed(entry.tag, file); - } catch(Throwable error) { - var report = new CrashReport("Syncing OTM Drives state to disk", error); - var category = report.addCategory("Corrupt drive"); - category.setDetail("UUID", entry.file.toString()); - category.setDetail("Stored item count", entry.drive.getStoredCount()); - category.setDetail("Capacity", entry.drive.getCapacity()); - - if (entry.drive instanceof AbstractMatteryDrive drive) { - category.setDetail("Amount of different stacks", drive.different_stacks); - category.setDetail("Max amount of different stacks", drive.max_different_stacks); - } - - throw new ReportedException(report); + NbtIo.writeCompressed(entry.tag, newFile) + } catch (error: Throwable) { + val report = CrashReport("Syncing OTM Drives state to disk", error) + val category = report.addCategory("Corrupt drive") + category.setDetail("UUID", entry.file.toString()) + category.setDetail("Stored item count", entry.drive.storedCount) + category.setDetail("Capacity", entry.drive.driveCapacity) + throw ReportedException(report) } } } } + +private class WeakDriveReference(drive: IMatteryDrive<*>) { + private var drive: IMatteryDrive<*>? = drive + private val weak = WeakReference(drive) + + fun valid(): Boolean { + return drive != null || weak.get() != null + } + + fun drive(): IMatteryDrive<*>? { + return drive ?: weak.get() + } + + fun sync(): CompoundTag? { + var drive = drive + + if (drive == null) { + drive = weak.get() + + if (drive == null) + return null + } + + if (!drive.isDirty) { + this.drive = null + return null + } + + val tag = drive.serializeNBT() + drive.isDirty = false + this.drive = null + + return tag + } + + fun access(): IMatteryDrive<*>? { + this.drive = weak.get() + return drive + } +} + +private data class BacklogLine(val file: UUID, val tag: CompoundTag, val drive: IMatteryDrive<*>) { + override fun equals(other: Any?): Boolean { + if (other is BacklogLine) { + return other.file == file + } + + if (other is UUID) { + return other == file + } + + return false + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt index 0f7abe61c..ec1be24e2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/drive/ItemMatteryDrive.kt @@ -1,124 +1,98 @@ -package ru.dbotthepony.mc.otm.capability.drive; +package ru.dbotthepony.mc.otm.capability.drive -import net.minecraft.MethodsReturnNonnullByDefault; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; -import net.minecraftforge.registries.RegistryManager; -import ru.dbotthepony.mc.otm.core.Fraction; -import ru.dbotthepony.mc.otm.storage.IStorageTuple; -import ru.dbotthepony.mc.otm.storage.ItemStackWrapper; -import ru.dbotthepony.mc.otm.storage.StorageObjectRegistry; -import ru.dbotthepony.mc.otm.storage.StorageObjectTuple; +import net.minecraft.nbt.CompoundTag +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.Items +import net.minecraftforge.registries.RegistryManager +import ru.dbotthepony.mc.otm.core.Fraction +import ru.dbotthepony.mc.otm.set +import ru.dbotthepony.mc.otm.storage.IStorageTuple +import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import ru.dbotthepony.mc.otm.storage.StorageObjectRegistry +import ru.dbotthepony.mc.otm.storage.StorageObjectTuple +import java.util.* -import javax.annotation.Nullable; -import javax.annotation.ParametersAreNonnullByDefault; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; +class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDrive { + constructor(capacity: Fraction, max_different_stacks: Int) : super(capacity, maxDifferentStacks = max_different_stacks) + constructor(capacity: Fraction, uuid: UUID, max_different_stacks: Int) : super(capacity, uuid, max_different_stacks) + constructor(capacity: Fraction, uuid: UUID) : super(capacity, uuid) + constructor(capacity: Fraction) : super(capacity) -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class ItemMatteryDrive extends AbstractMatteryDrive implements IItemMatteryDrive { - private static StorageObjectTuple identity; - - public ItemMatteryDrive(Fraction capacity, int max_different_stacks) { - super(capacity, max_different_stacks); - } - - public ItemMatteryDrive(Fraction capacity) { - super(capacity); - } - - @Override - public StorageObjectTuple identity() { + override fun identity(): StorageObjectTuple { if (identity == null) - identity = StorageObjectRegistry.getOrError(ItemStackWrapper.class); + identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java) - return identity; + return identity!! } - public ItemStack insertObject(ItemStack item, boolean simulate) { - return insertStack(new ItemStackWrapper(item), simulate).stack(); + fun insertObject(item: ItemStack?, simulate: Boolean): ItemStack { + return insertStack(ItemStackWrapper(item!!), simulate).stack } - @Override - protected CompoundTag serializeStack(IStorageTuple item) { - final var tag = new CompoundTag(); - final var location = Objects.requireNonNull(item.stack().stack().getItem().getRegistryName(), "Missing registry name for stored Item").toString(); + override fun serializeStack(item: IStorageTuple): CompoundTag? { + val tag = CompoundTag() + val location = item.stack().stack.item.registryName!!.toString() - tag.putString("item", location); - tag.putInt("count", item.stack().stack().getCount()); + tag["item"] = location + tag["count"] = item.stack.stack.count - CompoundTag item_tag; + val itag = item.stack.stack.tag - if ((item_tag = item.stack().stack().getTag()) != null) { - tag.put("data", item_tag); + if (itag != null) { + tag["data"] = itag } - return tag; + return tag } - @Nullable - @Override - protected ItemStackWrapper deserializeStack(CompoundTag tag) { - final var item = RegistryManager.ACTIVE.getRegistry(Item.class).getValue(new ResourceLocation(tag.getString("item"))); + override fun deserializeStack(tag: CompoundTag): ItemStackWrapper? { + val item = RegistryManager.ACTIVE.getRegistry(Item::class.java).getValue(ResourceLocation(tag.getString("item"))) - if (item != null && item != Items.AIR) { - final var count = tag.getInt("count"); - final var data = tag.get("data"); - - final var itemstack = new ItemStack(item, count); - - if (data != null) { - itemstack.setTag((CompoundTag) data); - } - - return new ItemStackWrapper(itemstack); + if (item != null && item !== Items.AIR) { + val count = tag.getInt("count") + val itemstack = ItemStack(item, count) + itemstack.tag = tag["data"] as? CompoundTag? + return ItemStackWrapper(itemstack) } - return null; + return null } - - @Override - public List> findItems(Item item) { - var list = items.get(item); - - if (list != null) - return List.copyOf(list); - - return List.of(); + override fun findItems(item: Item): List> { + val list = storedStacks[item] + return if (list != null) java.util.List.copyOf(list) else emptyList() } - @Override - public List> findItems(ItemStack stack) { - var list = items.get(stack.getItem()); + override fun findItems(stack: ItemStack): List> { + val list = storedStacks[stack.item] ?: return emptyList() - if (list == null) - return List.of(); + var amount = 0 - var amount = 0; - - for (var _stack : list) { - if (ItemStack.tagMatches(_stack.stack().stack(), stack)) { - amount++; + for (_stack in list) { + if (ItemStack.tagMatches(_stack.stack.stack, stack)) { + amount++ } } - var build_list = new ArrayList>(amount); - var i = 0; + val build_list = ArrayList>(amount) + var i = 0 - for (var _stack : list) { - if (ItemStack.tagMatches(_stack.stack().stack(), stack)) { - build_list.set(i, _stack); - i++; + for (_stack in list) { + if (ItemStack.tagMatches(_stack.stack.stack, stack)) { + build_list[i] = _stack + i++ } } - return build_list; + return build_list } -} + + companion object { + private var identity: StorageObjectTuple? = null + + @JvmField + val DUMMY = ItemMatteryDrive(Fraction(0), UUID(0L, 0L), 0) + } +} \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt index 3cadfc6e6..652feb7c5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/BasicStorageGraphNode.kt @@ -52,17 +52,15 @@ open class BasicStorageGraphNode : IStorageGraphNode { } } - detachStorage() components.add(component) - attachStorage() + getStorageGraph()?.add(component) } fun removeStorageComponent(component: IStorageIdentity<*>) { for (component1 in components) { if (component === component1 || component1.storageIdentity() == component.storageIdentity()) { - detachStorage() components.remove(component1) - attachStorage() + getStorageGraph()?.remove(component) return } } @@ -85,6 +83,4 @@ open class BasicStorageGraphNode : IStorageGraphNode { fun get(): LazyOptional { return if (valid) resolver else LazyOptional.empty() } - - override var storageGrid: StorageNetworkGraph? = null } \ No newline at end of file diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/IStorageGraphNode.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/IStorageGraphNode.kt index fc705d1d4..49f8eefef 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/IStorageGraphNode.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/IStorageGraphNode.kt @@ -11,23 +11,12 @@ interface IStorageGraphNode { /** * Return all providers that this cell can store. Or nothing, if it is not a storage. * - * If you don't know what you are going to store, return empty list, then, when you make up your - * mind by either player tweaking settings or something, - * call helper method detachStorage(); - * add whatever going to end up in getComponents(); - * add helper method attachStorage(); + * If you don't know what you are going to store, you may return nothing. * - * Failing to do it in this order will cause IllegalStateException - * - * @return a list of all providers, inheriting IStorageIdentity, or an empty list + * If something pops up, call [getStorageGraph], and then, call [StorageNetworkGraph.add]. */ fun getComponents(): Collection> - fun getComponentsForView(): Collection> = getComponents() - fun getComponentsForInterfaces(): Collection> = getComponents() - fun getComponentsForExporters(): Collection> = getComponents() - fun detachStorage() = storageGrid?.detach(getAsStorageNode()) - fun attachStorage() = storageGrid?.attach(getAsStorageNode()) - var storageGrid: StorageNetworkGraph? fun getAsStorageNode(): Graph6Node + fun getStorageGraph(): StorageNetworkGraph? = getAsStorageNode().graph as StorageNetworkGraph? } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt index 22822088b..04a9c0fe6 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/storage/StorageNetworkGraph.kt @@ -1,6 +1,5 @@ package ru.dbotthepony.mc.otm.graph.storage -import net.minecraft.MethodsReturnNonnullByDefault import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.block.entity.BlockEntity import ru.dbotthepony.mc.otm.capability.MatteryCapability @@ -9,10 +8,7 @@ import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.storage.IStorageIdentity import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.VirtualComponent -import javax.annotation.ParametersAreNonnullByDefault -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault class StorageNetworkGraph : Abstract6Graph() { private val virtualComponents = HashMap, VirtualComponent>() @@ -21,9 +17,7 @@ class StorageNetworkGraph : Abstract6Graph() { } /** - * @param type Class, representing identity of stored object - * @param The identity itself - * @return a virtual IStorageComponent + * Returns a [VirtualComponent] representing [type] storage */ fun getVirtualComponent(type: Class): VirtualComponent { return virtualComponents.computeIfAbsent(type) { VirtualComponent(type) } as VirtualComponent @@ -37,35 +31,15 @@ class StorageNetworkGraph : Abstract6Graph() { getVirtualComponent(identity.storageIdentity()).remove(identity) } - override fun onNodeRemoved(node: Graph6Node) { - node.value.storageGrid = null - - for (identity in node.value.getComponents()) { - remove(identity) - } - } - override fun onNodeAdded(node: Graph6Node) { - node.value.storageGrid = this - for (identity in node.value.getComponents()) { add(identity) } } - fun detach(node: Graph6Node) { - if (_nodes.contains(node)) { - for (identity in node.value.getComponents()) { - remove(identity) - } - } - } - - fun attach(node: Graph6Node) { - if (_nodes.contains(node)) { - for (identity in node.value.getComponents()) { - add(identity) - } + override fun onNodeRemoved(node: Graph6Node) { + for (identity in node.value.getComponents()) { + remove(identity) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt index ab0c2249f..d8e6d1f2f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt @@ -21,7 +21,7 @@ class ItemMonitorMenu @JvmOverloads constructor( init { if (tile != null) { - view.setComponent(tile.cell.storageGrid!!.getVirtualComponent(ItemStackWrapper::class.java)) + view.setComponent(tile.cell.getStorageGraph()!!.getVirtualComponent(ItemStackWrapper::class.java)) } addBatterySlot() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt index 4a53eeeae..d521b5891 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt @@ -143,14 +143,24 @@ interface IStorageListener { } interface IStorageTuple { + val id: UUID + get() = id() + + val stack: T + get() = stack() + fun id(): UUID fun stack(): T } -@JvmRecord -data class StorageTuple(private val id: UUID, private val stack: T) : IStorageTuple { - override fun id(): UUID = id - override fun stack(): T = stack +data class StorageTuple(override val id: UUID, override val stack: T) : IStorageTuple { + override fun id(): UUID { + return id + } + + override fun stack(): T { + return stack + } } interface IStorageComponent : IStorageView, IStorageConsumer