Iterate storage system design once again

This commit is contained in:
DBotThePony 2022-02-26 12:08:17 +07:00
parent da7be875d5
commit 398105f067
Signed by: DBot
GPG Key ID: DCC23B5715498507
19 changed files with 255 additions and 289 deletions

View File

@ -27,8 +27,8 @@ import ru.dbotthepony.mc.otm.matter.MatterRegistryKt;
import ru.dbotthepony.mc.otm.network.MatteryNetworking; import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.registry.*; import ru.dbotthepony.mc.otm.registry.*;
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper; import ru.dbotthepony.mc.otm.storage.ItemStackWrapper;
import ru.dbotthepony.mc.otm.storage.StorageObjectRegistry; import ru.dbotthepony.mc.otm.storage.StorageRegistry;
import ru.dbotthepony.mc.otm.storage.StorageObjectTuple; import ru.dbotthepony.mc.otm.storage.StorageStackType;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
@ -43,7 +43,11 @@ public final class OverdriveThatMatters {
public static OverdriveThatMatters INSTANCE; public static OverdriveThatMatters INSTANCE;
public AndroidGui ANDROID_GUI; public AndroidGui ANDROID_GUI;
public StorageObjectTuple<ItemStackWrapper> ITEM_STORAGE; private StorageStackType<ItemStackWrapper> ITEM_STORAGE;
public StorageStackType<ItemStackWrapper> ITEM_STORAGE() {
return ITEM_STORAGE;
}
public static ResourceLocation loc(String path) { public static ResourceLocation loc(String path) {
return new ResourceLocation(MOD_ID, path); return new ResourceLocation(MOD_ID, path);
@ -89,9 +93,7 @@ public final class OverdriveThatMatters {
private void setup(final FMLCommonSetupEvent event) { private void setup(final FMLCommonSetupEvent event) {
MatteryNetworking.register(); MatteryNetworking.register();
// LOGGER.info("Registered network"); ITEM_STORAGE = StorageRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"));
ITEM_STORAGE = StorageObjectRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new ImpreciseFraction("3.125"));
} }
private void setupClient(final FMLClientSetupEvent event) { private void setupClient(final FMLClientSetupEvent event) {

View File

@ -1,34 +0,0 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import ru.dbotthepony.mc.otm.core.Fraction;
import ru.dbotthepony.mc.otm.core.ImpreciseFraction;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.HashMap;
import java.util.Objects;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public class StorageObjectRegistry {
private static final HashMap<Class<? extends IStorageStack>, StorageObjectTuple<? extends IStorageStack>> REGISTRY = new HashMap<>();
public static <T extends IStorageStack> StorageObjectTuple<T> register(Class<T> identity, T empty, ImpreciseFraction energyPerOperation) {
final var tuple = new StorageObjectTuple<>(identity, empty, energyPerOperation);
REGISTRY.put(identity, tuple);
return tuple;
}
@SuppressWarnings("unchecked")
@Nullable
public static <T extends IStorageStack> StorageObjectTuple<T> get(Class<T> identity) {
return (StorageObjectTuple<T>) REGISTRY.get(identity);
}
@Nonnull
public static <T extends IStorageStack> StorageObjectTuple<T> getOrError(Class<T> identity) {
return Objects.requireNonNull(get(identity), "No storage mapping present for " + identity);
}
}

View File

@ -1,21 +0,0 @@
package ru.dbotthepony.mc.otm.storage;
import ru.dbotthepony.mc.otm.core.Fraction;
import ru.dbotthepony.mc.otm.core.ImpreciseFraction;
import javax.annotation.Nonnull;
public record StorageObjectTuple<T extends IStorageStack>(@Nonnull Class<T> identity, @Nonnull T empty, @Nonnull ImpreciseFraction energyPerOperation) {
@Override
public boolean equals(Object obj) {
if (obj instanceof StorageObjectTuple tuple)
return tuple.identity == identity;
return false;
}
@Override
public int hashCode() {
return identity.hashCode();
}
}

View File

@ -36,11 +36,11 @@ class DriveRackBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) :
super.setChanged(slot, new, old) super.setChanged(slot, new, old)
old.getCapability(MatteryCapability.DRIVE).ifPresent { old.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.computeIfAbsent(it.storageIdentity()) {c -> PoweredVirtualComponent(c, energy)}.remove(it) cell.computeIfAbsent(it.storageType) {PoweredVirtualComponent(it, energy)}.remove(it)
} }
new.getCapability(MatteryCapability.DRIVE).ifPresent { new.getCapability(MatteryCapability.DRIVE).ifPresent {
cell.computeIfAbsent(it.storageIdentity()) {c -> PoweredVirtualComponent(c, energy)}.add(it) cell.computeIfAbsent(it.storageType) {PoweredVirtualComponent(it, energy)}.add(it)
} }
} }
} }

View File

@ -31,7 +31,7 @@ class DriveViewerBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Matte
tickOnceServer { tickOnceServer {
var state = blockState var state = blockState
if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent && energy.batteryLevel >= OverdriveThatMatters.INSTANCE.ITEM_STORAGE.energyPerOperation()) { if (container.getItem(0).getCapability(MatteryCapability.DRIVE).isPresent && energy.batteryLevel >= OverdriveThatMatters.INSTANCE.ITEM_STORAGE().energyPerOperation) {
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING) state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.WORKING)
} else { } else {
state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE) state = state.setValue(WorkerState.SEMI_WORKER_STATE, WorkerState.IDLE)

View File

@ -1,37 +1,28 @@
package ru.dbotthepony.mc.otm.capability.drive package ru.dbotthepony.mc.otm.capability.drive
import ru.dbotthepony.mc.otm.core.Fraction.Companion.deserializeNBT import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
import ru.dbotthepony.mc.otm.storage.IStorageStack import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmOverloads
import java.util.HashMap
import ru.dbotthepony.mc.otm.storage.IStorageTuple
import java.util.UUID 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.CompoundTag
import net.minecraft.nbt.ListTag import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag import net.minecraft.nbt.Tag
import ru.dbotthepony.mc.otm.core.Fraction
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.ifHas
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.storage.*
import java.util.ArrayList import java.util.ArrayList
import java.util.HashSet import java.util.HashSet
abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor( abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor(
override var driveCapacity: ImpreciseFraction, override var driveCapacity: ImpreciseFraction,
override val uuid: UUID = UUID.randomUUID(), override val uuid: UUID = UUID.randomUUID(),
var maxDifferentStacks: Int = 0xFFFF var maxDifferentStacks: Int = 0xFFFF
) : IMatteryDrive<T> { ) : IMatteryDrive<T> {
protected val storedStacks = Long2ObjectAVLTreeMap<MutableList<IStorageTuple<T>>>()
protected val storedByID = Object2ObjectAVLTreeMap<UUID, IStorageTuple<T>>()
@JvmField
protected val storedStacks = HashMap<Any, MutableList<IStorageTuple<T>>>()
@JvmField
protected val storedByID = HashMap<UUID, IStorageTuple<T>>()
abstract fun identity(): StorageObjectTuple<T>
override var isDirty = false override var isDirty = false
set(value) { set(value) {
if (value != field && value && DrivePool.isLegalAccess()) { if (value != field && value && DrivePool.isLegalAccess()) {
@ -42,24 +33,26 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
} }
var storedDifferentStacks = 0 var storedDifferentStacks = 0
protected set
override var storedCount = ImpreciseFraction.ZERO override var storedCount = ImpreciseFraction.ZERO
protected set protected set
@Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T { override fun insertStack(stack: T, simulate: Boolean): T {
val maxInsert = driveCapacity.minus(storedCount).min(stack.count) val maxInsert = driveCapacity.minus(storedCount).min(stack.count)
if (maxInsert <= ImpreciseFraction.ZERO) return stack if (maxInsert <= ImpreciseFraction.ZERO) return stack
val listing = storedStacks.computeIfAbsent(stack.partitionKey()) { ArrayList() } val listing = storedStacks.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() })
for (state in listing) { for (state in listing) {
if (state.stack().sameItem(stack)) { if (state.stack.sameItem(stack)) {
if (!simulate) { if (!simulate) {
state.stack().grow(maxInsert) state.stack.grow(maxInsert)
storedCount += maxInsert storedCount += maxInsert
for (listener in listeners) { for (listener in listeners) {
listener.changeObject(state.id(), state.stack().count) listener.changeStack(state.id, state.stack.count)
} }
isDirty = true isDirty = true
@ -84,10 +77,10 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
val state = StorageTuple(UUID.randomUUID(), copy) val state = StorageTuple(UUID.randomUUID(), copy)
listing.add(state) listing.add(state)
storedByID[state.id()] = state storedByID[state.id] = state
for (listener in listeners) { for (listener in listeners) {
listener.addObject(state.stack(), state.id(), this) listener.addStack(state.stack, state.id, this)
} }
isDirty = true isDirty = true
@ -98,33 +91,35 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
return copy return copy
} }
@Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T {
@Suppress("NAME_SHADOWING")
var amount = amount var amount = amount
val get = storedByID[id] ?: return identity().empty() val get = storedByID[id] ?: return storageType.empty
if (amount <= ImpreciseFraction.ZERO) if (amount <= ImpreciseFraction.ZERO)
amount = get.stack.getMaxStackSize().orElse(get.stack.count) amount = get.stack.maxStackSize ?: get.stack.count
amount = amount.min(get.stack.count) amount = amount.min(get.stack.count)
if (amount <= ImpreciseFraction.ZERO) if (amount <= ImpreciseFraction.ZERO)
return identity().empty() return storageType.empty
val copy = get.stack.copy() as T val copy = get.stack.copy() as T
copy.count = amount copy.count = amount
if (!simulate) { if (!simulate) {
if (amount.compareTo(get.stack.count) == 0) { if (amount.compareTo(get.stack.count) == 0) {
val listing = storedStacks[get.stack.partitionKey()]!! val listing = storedStacks[get.stack.partitionKey]!!
listing.remove(get) listing.remove(get)
storedDifferentStacks-- storedDifferentStacks--
for (listener in listeners) { for (listener in listeners) {
listener.removeObject(get.id()) listener.removeStack(get.id)
} }
if (listing.size == 0) { if (listing.size == 0) {
storedStacks.remove(get.stack.partitionKey()) storedStacks.remove(get.stack.partitionKey)
} }
} }
@ -133,7 +128,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
if (!get.stack.count.isZero) { if (!get.stack.count.isZero) {
for (listener in listeners) { for (listener in listeners) {
listener.changeObject(get.id(), get.stack.count) listener.changeStack(get.id, get.stack.count)
} }
} }
@ -160,7 +155,7 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
val serialized = serializeStack(stack) val serialized = serializeStack(stack)
if (serialized != null) { if (serialized != null) {
serialized["id"] = longArrayOf(stack.id().mostSignificantBits, stack.id().leastSignificantBits) serialized["id"] = longArrayOf(stack.id.mostSignificantBits, stack.id.leastSignificantBits)
list.add(serialized) list.add(serialized)
} }
} }
@ -191,19 +186,15 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
val id = entry.getLongArray("id") val id = entry.getLongArray("id")
val tuple = StorageTuple(if (id.size == 2) UUID(id[0], id[1]) else UUID.randomUUID(), stack) val tuple = StorageTuple(if (id.size == 2) UUID(id[0], id[1]) else UUID.randomUUID(), stack)
storedStacks.computeIfAbsent(stack.partitionKey()) { ArrayList() }.add(tuple) storedStacks.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() }).add(tuple)
storedByID[tuple.id] = tuple storedByID[tuple.id] = tuple
} }
} }
} }
} }
override fun storageIdentity(): Class<T> {
return identity().identity()
}
override fun getStack(id: UUID): T { override fun getStack(id: UUID): T {
return storedByID[id]?.stack ?: identity().empty() return storedByID[id]?.stack ?: storageType.empty
} }
override fun getStacks(): List<IStorageTuple<T>> { override fun getStacks(): List<IStorageTuple<T>> {
@ -221,7 +212,6 @@ abstract class AbstractMatteryDrive<T : IStorageStack> @JvmOverloads constructor
return list return list
} }
@JvmField
protected val listeners = HashSet<IStorageListener<T>>() protected val listeners = HashSet<IStorageListener<T>>()
override fun addListener(listener: IStorageListener<T>): Boolean { override fun addListener(listener: IStorageListener<T>): Boolean {

View File

@ -6,12 +6,12 @@ import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items import net.minecraft.world.item.Items
import net.minecraftforge.registries.RegistryManager import net.minecraftforge.registries.RegistryManager
import ru.dbotthepony.mc.otm.core.Fraction import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.set
import ru.dbotthepony.mc.otm.storage.IStorageTuple import ru.dbotthepony.mc.otm.storage.IStorageTuple
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
import ru.dbotthepony.mc.otm.storage.StorageObjectRegistry import ru.dbotthepony.mc.otm.storage.StorageStackType
import java.util.* import java.util.*
class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive { class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDrive {
@ -20,16 +20,15 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
constructor(capacity: ImpreciseFraction, uuid: UUID) : super(capacity, uuid) constructor(capacity: ImpreciseFraction, uuid: UUID) : super(capacity, uuid)
constructor(capacity: ImpreciseFraction) : super(capacity) constructor(capacity: ImpreciseFraction) : super(capacity)
private val identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java) override val storageType: StorageStackType<ItemStackWrapper> = OverdriveThatMatters.INSTANCE.ITEM_STORAGE()
override fun identity() = identity
fun insertObject(item: ItemStack, simulate: Boolean): ItemStack { fun insertStack(item: ItemStack, simulate: Boolean): ItemStack {
return insertStack(ItemStackWrapper(item), simulate).stack return insertStack(ItemStackWrapper(item), simulate).stack
} }
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? { override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
val tag = CompoundTag() val tag = CompoundTag()
val location = item.stack().stack.item.registryName!!.toString() val location = item.stack.stack.item.registryName!!.toString()
tag["item"] = location tag["item"] = location
tag["count"] = item.stack.stack.count tag["count"] = item.stack.stack.count
@ -57,12 +56,12 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
} }
override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> { override fun findItems(item: Item): List<IStorageTuple<ItemStackWrapper>> {
val list = storedStacks[item] val list = storedStacks[ItemStackWrapper.partitionKey(item)]
return if (list != null) java.util.List.copyOf(list) else emptyList() return if (list != null) java.util.List.copyOf(list) else emptyList()
} }
override fun findItems(stack: ItemStack): List<IStorageTuple<ItemStackWrapper>> { override fun findItems(stack: ItemStack): List<IStorageTuple<ItemStackWrapper>> {
val list = storedStacks[stack.item] ?: return emptyList() val list = storedStacks[ItemStackWrapper.partitionKey(stack.item)] ?: return emptyList()
var amount = 0 var amount = 0

View File

@ -2,14 +2,15 @@ package ru.dbotthepony.mc.otm.graph.storage
import net.minecraftforge.common.util.LazyOptional import net.minecraftforge.common.util.LazyOptional
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.storage.IStorageIdentity import ru.dbotthepony.mc.otm.storage.IStorage
import ru.dbotthepony.mc.otm.storage.IStorageStack import ru.dbotthepony.mc.otm.storage.IStorageStack
import ru.dbotthepony.mc.otm.storage.StorageStackType
import java.util.* import java.util.*
open class BasicStorageGraphNode : IStorageGraphNode { open class BasicStorageGraphNode : IStorageGraphNode {
private var resolver = LazyOptional.of<IStorageGraphNode> { this } private var resolver = LazyOptional.of<IStorageGraphNode> { this }
private var valid = true private var valid = true
protected val components = ArrayList<IStorageIdentity<*>>() protected val components = ArrayList<IStorage<*>>()
private val node = Graph6Node<IStorageGraphNode>(this) private val node = Graph6Node<IStorageGraphNode>(this)
@ -17,25 +18,14 @@ open class BasicStorageGraphNode : IStorageGraphNode {
return node return node
} }
override fun getComponents(): List<IStorageIdentity<*>> { override fun fetchComponents(): List<IStorage<*>> {
return Collections.unmodifiableList(components) return Collections.unmodifiableList(components)
} }
fun <T : IStorageStack, U : IStorageIdentity<T>> computeIfAbsent(identity: Class<T>, provider: () -> U): U { @Suppress("unchecked_cast")
fun <T : IStorageStack, U : IStorage<T>> computeIfAbsent(identity: StorageStackType<T>, provider: (StorageStackType<T>) -> U): U {
for (component in components) { for (component in components) {
if (component.storageIdentity() == identity) { if (component.storageType === identity) {
return component as U
}
}
val factory = provider()
addStorageComponent(factory)
return factory
}
fun <T : IStorageStack, U : IStorageIdentity<T>> computeIfAbsent(identity: Class<T>, provider: (Class<T>) -> U): U {
for (component in components) {
if (component.storageIdentity() == identity) {
return component as U return component as U
} }
} }
@ -45,25 +35,15 @@ open class BasicStorageGraphNode : IStorageGraphNode {
return factory return factory
} }
fun addStorageComponent(component: IStorageIdentity<*>) { fun addStorageComponent(component: IStorage<*>) {
for (component1 in components) { for (component1 in components) {
if (component === component1 || component1.storageIdentity() == component.storageIdentity()) { if (component === component1 || component1.storageType === component.storageType) {
return return
} }
} }
components.add(component) components.add(component)
getStorageGraph()?.add(component) storageGraph?.add(component)
}
fun removeStorageComponent(component: IStorageIdentity<*>) {
for (component1 in components) {
if (component === component1 || component1.storageIdentity() == component.storageIdentity()) {
components.remove(component1)
getStorageGraph()?.remove(component)
return
}
}
} }
fun invalidate() { fun invalidate() {

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.mc.otm.graph.storage
import net.minecraft.MethodsReturnNonnullByDefault import net.minecraft.MethodsReturnNonnullByDefault
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.storage.IStorageIdentity import ru.dbotthepony.mc.otm.storage.IStorage
import javax.annotation.ParametersAreNonnullByDefault import javax.annotation.ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault @MethodsReturnNonnullByDefault
@ -13,10 +13,10 @@ interface IStorageGraphNode {
* *
* If you don't know what you are going to store, you may return nothing. * If you don't know what you are going to store, you may return nothing.
* *
* If something pops up, call [getStorageGraph], and then, call [StorageNetworkGraph.add]. * If something pops up, call [storageGraph], and then, call [StorageNetworkGraph.add].
*/ */
fun getComponents(): Collection<IStorageIdentity<*>> fun fetchComponents(): Collection<IStorage<*>>
fun getAsStorageNode(): Graph6Node<IStorageGraphNode> fun getAsStorageNode(): Graph6Node<IStorageGraphNode>
fun getStorageGraph(): StorageNetworkGraph? = getAsStorageNode().graph as StorageNetworkGraph? val storageGraph: StorageNetworkGraph? get() = getAsStorageNode().graph as StorageNetworkGraph?
} }

View File

@ -1,44 +1,51 @@
package ru.dbotthepony.mc.otm.graph.storage package ru.dbotthepony.mc.otm.graph.storage
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.graph.Abstract6Graph import ru.dbotthepony.mc.otm.graph.Abstract6Graph
import ru.dbotthepony.mc.otm.graph.Graph6Node import ru.dbotthepony.mc.otm.graph.Graph6Node
import ru.dbotthepony.mc.otm.storage.IStorageIdentity import ru.dbotthepony.mc.otm.storage.*
import ru.dbotthepony.mc.otm.storage.IStorageStack
import ru.dbotthepony.mc.otm.storage.VirtualComponent
class StorageNetworkGraph : Abstract6Graph<IStorageGraphNode>() { class StorageNetworkGraph : Abstract6Graph<IStorageGraphNode>() {
private val virtualComponents = HashMap<Class<out IStorageStack>, VirtualComponent<out IStorageStack>>() private val virtualComponents = Object2ObjectArrayMap<StorageStackType<*>, VirtualComponent<*>>()
fun <T : IStorageStack> insertObject(type: Class<T>, obj: T, simulate: Boolean): T { fun <T : IStorageStack> insertStack(obj: T, simulate: Boolean): T {
return getVirtualComponent(type).insertStack(obj, simulate) return (getVirtualComponent(StorageRegistry.get(obj::class.java)) as VirtualComponent<T>).insertStack(obj, simulate)
}
/**
* Returns a [VirtualComponent] representing [type] storage
*/
fun <T : IStorageStack> getVirtualComponent(type: StorageStackType<T>): VirtualComponent<T> {
return virtualComponents.computeIfAbsent(type, Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
} }
/** /**
* Returns a [VirtualComponent] representing [type] storage * Returns a [VirtualComponent] representing [type] storage
*/ */
fun <T : IStorageStack> getVirtualComponent(type: Class<T>): VirtualComponent<T> { fun <T : IStorageStack> getVirtualComponent(type: Class<T>): VirtualComponent<T> {
return virtualComponents.computeIfAbsent(type) { VirtualComponent(type) } as VirtualComponent<T> return virtualComponents.computeIfAbsent(StorageRegistry.get(type), Object2ObjectFunction { VirtualComponent(type) }) as VirtualComponent<T>
} }
fun <T : IStorageStack> add(identity: IStorageIdentity<T>) { fun <T : IStorageStack> add(storage: IStorage<T>) {
getVirtualComponent(identity.storageIdentity()).add(identity) getVirtualComponent(storage.storageType).add(storage)
} }
fun <T : IStorageStack> remove(identity: IStorageIdentity<T>) { fun <T : IStorageStack> remove(storage: IStorage<T>) {
getVirtualComponent(identity.storageIdentity()).remove(identity) getVirtualComponent(storage.storageType).remove(storage)
} }
override fun onNodeAdded(node: Graph6Node<IStorageGraphNode>) { override fun onNodeAdded(node: Graph6Node<IStorageGraphNode>) {
for (identity in node.value.getComponents()) { for (identity in node.value.fetchComponents()) {
add(identity) add(identity)
} }
} }
override fun onNodeRemoved(node: Graph6Node<IStorageGraphNode>) { override fun onNodeRemoved(node: Graph6Node<IStorageGraphNode>) {
for (identity in node.value.getComponents()) { for (identity in node.value.fetchComponents()) {
remove(identity) remove(identity)
} }
} }

View File

@ -223,7 +223,7 @@ class PortableCondensationDriveItem(capacity: Int) :
if (filter.matches(event.item.item)) { if (filter.matches(event.item.item)) {
val copy = event.item.item.copy() val copy = event.item.item.copy()
val remaining = (it as ItemMatteryDrive).insertObject(event.item.item, false) val remaining = (it as ItemMatteryDrive).insertStack(event.item.item, false)
if (remaining.count == event.item.item.count) { if (remaining.count == event.item.item.count) {
return@ifPresent return@ifPresent

View File

@ -16,6 +16,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import net.minecraftforge.network.PacketDistributor import net.minecraftforge.network.PacketDistributor
import org.lwjgl.glfw.GLFW import org.lwjgl.glfw.GLFW
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
@ -201,7 +202,7 @@ private fun getMatterValue(stack: ItemStack, level: Int): MatterTuple {
val drive = stack.getCapability(MatteryCapability.DRIVE).orNull() val drive = stack.getCapability(MatteryCapability.DRIVE).orNull()
if (drive != null && drive.storageIdentity() === ItemStackWrapper.javaClass) { if (drive != null && drive.storageType === OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) {
for (item in (drive as IMatteryDrive<ItemStackWrapper>).getStacks()) { for (item in (drive as IMatteryDrive<ItemStackWrapper>).getStacks()) {
val tuple = getMatterValue(item.stack.stack, level + 1) val tuple = getMatterValue(item.stack.stack, level + 1)

View File

@ -7,6 +7,7 @@ import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import net.minecraftforge.energy.CapabilityEnergy import net.minecraftforge.energy.CapabilityEnergy
import net.minecraftforge.network.NetworkEvent import net.minecraftforge.network.NetworkEvent
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.entity.DriveViewerBlockEntity import ru.dbotthepony.mc.otm.block.entity.DriveViewerBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive
@ -70,7 +71,7 @@ class DriveViewerMenu @JvmOverloads constructor(
if (!itemStack.isEmpty) { if (!itemStack.isEmpty) {
itemStack.getCapability(MatteryCapability.DRIVE).ifPresent { itemStack.getCapability(MatteryCapability.DRIVE).ifPresent {
if (it.storageIdentity() == ItemStackWrapper::class.java) { if (it.storageType === OverdriveThatMatters.INSTANCE.ITEM_STORAGE()) {
lastDrive = it as IMatteryDrive<ItemStackWrapper> lastDrive = it as IMatteryDrive<ItemStackWrapper>
} }
} }
@ -123,7 +124,7 @@ class DriveViewerMenu @JvmOverloads constructor(
if (remaining.count.toInt() == item.count) if (remaining.count.toInt() == item.count)
return ItemStack.EMPTY return ItemStack.EMPTY
if (remaining.isEmpty()) { if (remaining.isEmpty) {
val copy = item.copy() val copy = item.copy()
slot.set(ItemStack.EMPTY) slot.set(ItemStack.EMPTY)
return copy return copy

View File

@ -3,6 +3,7 @@ package ru.dbotthepony.mc.otm.menu
import net.minecraft.world.entity.player.Inventory import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.OverdriveThatMatters
import ru.dbotthepony.mc.otm.block.entity.ItemMonitorBlockEntity import ru.dbotthepony.mc.otm.block.entity.ItemMonitorBlockEntity
import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.capability.MatteryCapability
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewSupplier import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewSupplier
@ -28,7 +29,7 @@ class ItemMonitorMenu @JvmOverloads constructor(
init { init {
if (tile != null) { if (tile != null) {
subscribed = tile.cell.getStorageGraph()!!.getVirtualComponent(ItemStackWrapper::class.java) subscribed = tile.cell.storageGraph!!.getVirtualComponent(OverdriveThatMatters.INSTANCE.ITEM_STORAGE())
local = PoweredVirtualComponent(subscribed, tile.getCapability(MatteryCapability.ENERGY).resolve().get()) local = PoweredVirtualComponent(subscribed, tile.getCapability(MatteryCapability.ENERGY).resolve().get())
view.setComponent(local) view.setComponent(local)
} else { } else {

View File

@ -77,8 +77,8 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
return state.values.size return state.values.size
} }
override fun addObject(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) = addObject(stack.stack, id) override fun addStack(stack: ItemStackWrapper, id: UUID, provider: IStorageView<ItemStackWrapper>) = addObject(stack.stack, id)
override fun changeObject(id: UUID, newCount: ImpreciseFraction) = changeObject(id, newCount.toInt()) override fun changeStack(id: UUID, newCount: ImpreciseFraction) = changeObject(id, newCount.toInt())
protected fun network(fn: () -> Any) { protected fun network(fn: () -> Any) {
if (!remote) { if (!remote) {
@ -86,7 +86,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
} }
} }
override fun removeObject(id: UUID) { override fun removeStack(id: UUID) {
val get = upstream_state[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!") val get = upstream_state[id] ?: throw IllegalStateException("Unknown ItemStack with upstream id $id!")
upstream_state.remove(id) upstream_state.remove(id)
state.remove(get.id) state.remove(get.id)
@ -162,7 +162,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val extracted = provider.extractStack(state.upstreamId!!, amount, true) val extracted = provider.extractStack(state.upstreamId!!, amount, true)
if (!extracted.isEmpty()) { if (!extracted.isEmpty) {
val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false) val (_, remaining) = menu.quickMoveToInventory(extracted.stack, false)
if (remaining.count != extracted.stack.count) { if (remaining.count != extracted.stack.count) {
@ -189,7 +189,7 @@ open class NetworkedItemView(val ply: Player, val menu: MatteryMenu, val remote:
val copy = menu.carried.copy() val copy = menu.carried.copy()
copy.count = 1 copy.count = 1
if (provider.insertStack(ItemStackWrapper(copy), false).isEmpty()) { if (provider.insertStack(ItemStackWrapper(copy), false).isEmpty) {
menu.carried.shrink(1) menu.carried.shrink(1)
MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried)) MatteryNetworking.send(ply as ServerPlayer, SetCarriedPacket(menu.carried))
menu.setRemoteCarried(menu.carried.copy()) menu.setRemoteCarried(menu.carried.copy())

View File

@ -2,46 +2,53 @@ package ru.dbotthepony.mc.otm.storage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.util.* import java.util.*
import net.minecraftforge.registries.ForgeRegistry
interface IStorageStack { interface IStorageStack {
fun copy(): IStorageStack fun copy(): IStorageStack
var count: ImpreciseFraction var count: ImpreciseFraction
fun isEmpty(): Boolean val isEmpty: Boolean
/** /**
* @return max stack size for this stack object * @return max stack size for this stack object,
* [Optional.empty()] if unlimited (default) * null if unlimited (default)
*/ */
fun getMaxStackSize(): Optional<ImpreciseFraction> = Optional.empty() val maxStackSize: ImpreciseFraction? get() = null
/** /**
* Returns Identity utilized to partition view table (if it has any). * This property represent (almost) unique key used to quickly look up for similar stacks.
* Defaults to no partitioning; meaning performance will degrade much quicker than it should. * Semantic is very similar, but not equal, to [hashCode] of object. This property **must** be equal for two
* Good object is an object that will influence on [sameItem] return result, making it return true. * entries if [sameItem] returns true. This value should not change.
* It is strictly considered that if !this.itemIdentity().equals(other.itemIdentity);
* then this.sameItem(other) will never return true.
* *
* If implemented, and storage is also partitioned properly, then performance will be flat equally to view table's performance. * A good example of key is [ForgeRegistry.getID].
* *
* Example: ItemStack#item * Collisions are solved as in open hash map, with linear scan for required item.
*/ */
fun partitionKey(): Any { val partitionKey: Long
return Any::class.java
}
/** /**
* [other] other object to compare; if not the same of this object type just return false * [other] is the stack to compare.
* Returns boolean representing whenever internal state of [this] equals to [other]; *
* this include tags, mapping IDs, capabilities, **excluding** amount; * If not the same of this object type just return false
* behavior is pretty much the same as ItemStack.isSameItemSameTags * Returns boolean representing whenever internal state of *this* equals to [other];
*
* This includes (nbt) tags, mapping IDs, capabilities, **excluding** amount;
*
* Example of this behavior is demonstrated by ItemStack.isSameItemSameTags.
*/ */
fun sameItem(other: IStorageStack): Boolean fun sameItem(other: IStorageStack): Boolean
/**
* Increase [count] by [amount]
*/
fun grow(amount: ImpreciseFraction) { fun grow(amount: ImpreciseFraction) {
count += amount count += amount
} }
/**
* Decrease [count] by [amount]
*/
fun shrink(amount: ImpreciseFraction) { fun shrink(amount: ImpreciseFraction) {
count -= amount count -= amount
} }
@ -50,11 +57,14 @@ interface IStorageStack {
/** /**
* Storage system root, along IStorageStack interface * Storage system root, along IStorageStack interface
*/ */
interface IStorageIdentity<T : IStorageStack> { interface IStorage<T : IStorageStack> {
fun storageIdentity(): Class<T> /**
* @return Identity of this virtual component
*/
val storageType: StorageStackType<T>
} }
interface IStorageTrigger<T : IStorageStack> : IStorageIdentity<T> { interface IStorageTrigger<T : IStorageStack> : IStorage<T> {
/** /**
* [listener] is [IStorageListener] which want to subscribe to our events * [listener] is [IStorageListener] which want to subscribe to our events
*/ */
@ -66,19 +76,23 @@ interface IStorageTrigger<T : IStorageStack> : IStorageIdentity<T> {
fun removeListener(listener: IStorageListener<T>): Boolean fun removeListener(listener: IStorageListener<T>): Boolean
} }
interface IStorageConsumer<T : IStorageStack> : IStorageIdentity<T> { interface IStorageConsumer<T : IStorageStack> : IStorage<T> {
/**
* Inserts an item into system.
* @return leftover, might equal to [stack] if no items were inserted
*/
fun insertStack(stack: T, simulate: Boolean): T fun insertStack(stack: T, simulate: Boolean): T
} }
interface IStorageView<T : IStorageStack> : IStorageTrigger<T> { interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
/** /**
* @param id identifier of object * @param id identifier of stack
* @return stored object (not a copy). Do not edit it. * @return stored object (not a copy). Do not edit it.
*/ */
fun getStack(id: UUID): T fun getStack(id: UUID): T
/** /**
* @param id identifier of object to extract * @param id identifier of stack to extract
* @param amount amount of units to extract * @param amount amount of units to extract
* @param simulate whenever to simulate the action or not * @param simulate whenever to simulate the action or not
* @return copy of object, with amount of units actually extracted * @return copy of object, with amount of units actually extracted
@ -89,9 +103,9 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
/** /**
* Designed for views, for extraction with less computation overhead caused by * Designed for views, for extraction with less computation overhead caused by
* copying object extracted * copying stack extracted
* *
* @param id identifier of object to extract * @param id identifier of stack to extract
* @param amount desired amount to extract * @param amount desired amount to extract
* @param simulate whenever to simulate the action or not * @param simulate whenever to simulate the action or not
* @return amount extracted * @return amount extracted
@ -108,7 +122,7 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
fun addListenerAuto(listener: IStorageListener<T>): Boolean { fun addListenerAuto(listener: IStorageListener<T>): Boolean {
if (addListener(listener)) { if (addListener(listener)) {
for (stack in getStacks()) { for (stack in getStacks()) {
listener.addObject(stack.stack(), stack.id(), this) listener.addStack(stack.stack, stack.id, this)
} }
return true return true
@ -120,7 +134,7 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
fun removeListenerAuto(listener: IStorageListener<T>): Boolean { fun removeListenerAuto(listener: IStorageListener<T>): Boolean {
if (removeListener(listener)) { if (removeListener(listener)) {
for (stack in getStacks()) { for (stack in getStacks()) {
listener.removeObject(stack.id()) listener.removeStack(stack.id)
} }
return true return true
@ -134,38 +148,23 @@ interface IStorageListener<T : IStorageStack> {
/** /**
* Fired on whenever an object is added (to listener) we subscribed to * Fired on whenever an object is added (to listener) we subscribed to
*/ */
fun addObject(stack: T, id: UUID, provider: IStorageView<T>) fun addStack(stack: T, id: UUID, provider: IStorageView<T>)
/** /**
* Fired on whenever an object is changes on listener we subscribed to * Fired on whenever an object is changes on listener we subscribed to
*/ */
fun changeObject(id: UUID, newCount: ImpreciseFraction) fun changeStack(id: UUID, newCount: ImpreciseFraction)
/** /**
* Fired on whenever an object is removed from listener we subscribed to * Fired on whenever an object is removed from listener we subscribed to
*/ */
fun removeObject(id: UUID) fun removeStack(id: UUID)
} }
interface IStorageTuple<T : IStorageStack> { interface IStorageTuple<T : IStorageStack> {
val id: UUID val id: UUID
get() = id()
val stack: T val stack: T
get() = stack()
fun id(): UUID
fun stack(): T
}
data class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T> {
override fun id(): UUID {
return id
}
override fun stack(): T {
return stack
}
} }
class StorageTuple<T : IStorageStack>(override val id: UUID, override val stack: T) : IStorageTuple<T>
interface IStorageComponent<T : IStorageStack> : IStorageView<T>, IStorageConsumer<T> interface IStorageComponent<T : IStorageStack> : IStorageView<T>, IStorageConsumer<T>

View File

@ -1,39 +1,37 @@
package ru.dbotthepony.mc.otm.storage package ru.dbotthepony.mc.otm.storage
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack import net.minecraft.world.item.ItemStack
import ru.dbotthepony.mc.otm.core.Fraction import net.minecraftforge.registries.ForgeRegistries
import net.minecraftforge.registries.ForgeRegistry
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.util.*
@JvmRecord class ItemStackWrapper(val stack: ItemStack) : IStorageStack {
data class ItemStackWrapper(val stack: ItemStack) : IStorageStack { operator fun component1() = stack
override fun copy(): IStorageStack { override fun copy(): IStorageStack {
return ItemStackWrapper(stack.copy()) return ItemStackWrapper(stack.copy())
} }
fun setCount(value: Int) { fun setCount(value: Int) {
stack.count = Math.max(value, 0) stack.count = value.coerceAtLeast(0)
} }
override var count: ImpreciseFraction override var count: ImpreciseFraction
get() = ImpreciseFraction(stack.count) get() = ImpreciseFraction(stack.count)
set(value) = setCount(value.toInt()) set(value) = setCount(value.toInt())
fun getCountInt() = stack.count override val maxStackSize get() = ImpreciseFraction(stack.maxStackSize)
override fun getMaxStackSize(): Optional<ImpreciseFraction> { override val isEmpty: Boolean = stack.isEmpty
return Optional.of(ImpreciseFraction(stack.maxStackSize)) override val partitionKey: Long = partitionKey(stack.item)
}
override fun isEmpty(): Boolean = stack.isEmpty
override fun partitionKey(): Any = stack.item
override fun sameItem(other: IStorageStack): Boolean { override fun sameItem(other: IStorageStack): Boolean {
if (this === other) if (this === other)
return true return true
if (other is ItemStackWrapper) if (other is ItemStackWrapper)
return ItemStack.isSameItemSameTags(stack, other.stack); return ItemStack.isSameItemSameTags(stack, other.stack)
return false return false
} }
@ -41,5 +39,7 @@ data class ItemStackWrapper(val stack: ItemStack) : IStorageStack {
companion object { companion object {
@JvmField @JvmField
val EMPTY = ItemStackWrapper(ItemStack.EMPTY) val EMPTY = ItemStackWrapper(ItemStack.EMPTY)
fun partitionKey(item: Item) = (ForgeRegistries.ITEMS as ForgeRegistry<Item>?)?.getID(item)?.toLong() ?: System.identityHashCode(item).toLong()
} }
} }

View File

@ -0,0 +1,50 @@
package ru.dbotthepony.mc.otm.storage
import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.util.IdentityHashMap
open class StorageStackType<T : IStorageStack>(
val identity: Class<T>,
open val empty: T,
/**
* Speculated energy required per operation on stack with size of 1
*/
open val energyPerOperation: ImpreciseFraction
) {
open fun energyPerOperation(stack: T): ImpreciseFraction {
return stack.count * energyPerOperation
}
open fun energyPerStorage(stack: T) = energyPerOperation(stack)
open fun energyPerExtraction(stack: T) = energyPerOperation(stack)
open fun energyForUpkeep(stack: T) = ImpreciseFraction.ZERO
}
@Suppress("unchecked_cast")
object StorageRegistry {
private val REGISTRY = IdentityHashMap<Class<*>, StorageStackType<*>>()
@JvmStatic
fun register(type: StorageStackType<*>): StorageStackType<*> {
check(!REGISTRY.containsKey(type.identity)) { "Already have storage stack type for ${type.identity}" }
REGISTRY[type.identity] = type
return type
}
@JvmStatic
fun <T : IStorageStack> register(identity: Class<T>, empty: T, energyPerOperation: ImpreciseFraction): StorageStackType<T> {
return register(StorageStackType(identity, empty, energyPerOperation)) as StorageStackType<T>
}
@JvmStatic
fun <T : IStorageStack> get(identity: Class<T>): StorageStackType<T> {
val get = REGISTRY[identity] ?: throw NoSuchElementException("Registry does not contain $identity")
return get as StorageStackType<T>
}
@JvmStatic
fun <T : IStorageStack> getOrNull(identity: Class<T>): StorageStackType<T>? {
return REGISTRY[identity] as StorageStackType<T>?
}
}

View File

@ -1,7 +1,9 @@
package ru.dbotthepony.mc.otm.storage package ru.dbotthepony.mc.otm.storage
import it.unimi.dsi.fastutil.longs.Long2ObjectAVLTreeMap
import it.unimi.dsi.fastutil.longs.Long2ObjectFunction
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage
import ru.dbotthepony.mc.otm.core.Fraction
import ru.dbotthepony.mc.otm.core.ImpreciseFraction import ru.dbotthepony.mc.otm.core.ImpreciseFraction
import java.util.* import java.util.*
@ -23,45 +25,27 @@ class RemoteTuple<T : IStorageStack>(val obj: T, val remote_id: UUID, val provid
} }
} }
class LocalTuple<T : IStorageStack>(override val stack: T, override val id: UUID, val tuples: ArrayList<RemoteTuple<T>>) : IStorageTuple<T> { class LocalTuple<T : IStorageStack>(override val stack: T, override val id: UUID, val tuples: ArrayList<RemoteTuple<T>>) : IStorageTuple<T>
override fun id(): UUID {
return id
}
override fun stack(): T { open class VirtualComponent<T : IStorageStack>(type: StorageStackType<T>) : IStorageComponent<T>, IStorageListener<T> {
return stack constructor(type: Class<T>) : this(StorageRegistry.get(type))
}
}
open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageComponent<T>, IStorageListener<T> { override val storageType: StorageStackType<T> = type
@JvmField
protected val identity: StorageObjectTuple<T>
final override fun storageIdentity() = identity.identity()
init {
this.identity = StorageObjectRegistry.getOrError(identity)
}
// удаленный UUID -> Кортеж // удаленный UUID -> Кортеж
@JvmField protected val remoteByUUID = Object2ObjectAVLTreeMap<UUID, RemoteTuple<T>>()
protected val remoteByUUID = HashMap<UUID, RemoteTuple<T>>()
// локальный UUID -> Локальный кортеж // локальный UUID -> Локальный кортеж
@JvmField protected val localByUUID = Object2ObjectAVLTreeMap<UUID, LocalTuple<T>>()
protected val localByUUID = HashMap<UUID, LocalTuple<T>>()
// Хеш ключ -> Список Локальных кортежей // ключ -> Список Локальных кортежей
@JvmField protected val partitions = Long2ObjectAVLTreeMap<ArrayList<LocalTuple<T>>>()
protected val partitions = HashMap<Any, ArrayList<LocalTuple<T>>>()
// ArrayList для скорости работы // ArrayList для скорости работы
@JvmField
protected val listeners = ArrayList<IStorageListener<T>>() protected val listeners = ArrayList<IStorageListener<T>>()
@JvmField
protected val consumers = ArrayList<IStorageConsumer<T>>() protected val consumers = ArrayList<IStorageConsumer<T>>()
open fun add(identity: IStorageIdentity<T>) { open fun add(identity: IStorage<T>) {
if (identity is IStorageView<T>) { if (identity is IStorageView<T>) {
identity.addListenerAuto(this) identity.addListenerAuto(this)
} else if (identity is IStorageTrigger<T>) { } else if (identity is IStorageTrigger<T>) {
@ -69,11 +53,11 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
} }
if (identity is IStorageConsumer<T> && !consumers.contains(identity)) { if (identity is IStorageConsumer<T> && !consumers.contains(identity)) {
consumers.add(identity); consumers.add(identity)
} }
} }
open fun remove(identity: IStorageIdentity<T>) { open fun remove(identity: IStorage<T>) {
if (identity is IStorageView<T>) { if (identity is IStorageView<T>) {
identity.removeListenerAuto(this) identity.removeListenerAuto(this)
} else if (identity is IStorageTrigger<T>) { } else if (identity is IStorageTrigger<T>) {
@ -99,7 +83,7 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
} }
override fun getStack(id: UUID): T { override fun getStack(id: UUID): T {
return localByUUID[id]?.stack ?: identity.empty return localByUUID[id]?.stack ?: this.storageType.empty
} }
override fun getStacks(): List<IStorageTuple<T>> { override fun getStacks(): List<IStorageTuple<T>> {
@ -110,9 +94,10 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
return output return output
} }
override fun addObject(stack: T, id: UUID, provider: IStorageView<T>) { @Suppress("unchecked_cast")
override fun addStack(stack: T, id: UUID, provider: IStorageView<T>) {
check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" } check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" }
val items = partitions.computeIfAbsent(stack.partitionKey()) { ArrayList() } val items = partitions.computeIfAbsent(stack.partitionKey, Long2ObjectFunction { ArrayList() })
var local: LocalTuple<T>? = null var local: LocalTuple<T>? = null
for (item in items) { for (item in items) {
@ -137,16 +122,16 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
if (added) { if (added) {
for (listener in listeners) { for (listener in listeners) {
listener.addObject(local.stack, local.id, this) listener.addStack(local.stack, local.id, this)
} }
} else { } else {
for (listener in listeners) { for (listener in listeners) {
listener.changeObject(local.id, local.stack.count) listener.changeStack(local.id, local.stack.count)
} }
} }
} }
override fun changeObject(id: UUID, newCount: ImpreciseFraction) { override fun changeStack(id: UUID, newCount: ImpreciseFraction) {
assert(newCount > ImpreciseFraction.ZERO) assert(newCount > ImpreciseFraction.ZERO)
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id")
@ -155,13 +140,13 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
tuple.local.stack.grow(diff) tuple.local.stack.grow(diff)
for (listener in listeners) { for (listener in listeners) {
listener.changeObject(tuple.local.id, tuple.local.stack.count) listener.changeStack(tuple.local.id, tuple.local.stack.count)
} }
} }
override fun removeObject(id: UUID) { override fun removeStack(id: UUID) {
val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id")
val key = tuple.local.stack.partitionKey() val key = tuple.local.stack.partitionKey
tuple.local.stack.shrink(tuple.obj.count) tuple.local.stack.shrink(tuple.obj.count)
tuple.local.tuples.remove(tuple) tuple.local.tuples.remove(tuple)
@ -177,7 +162,7 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
partitions[key]!!.remove(tuple.local) partitions[key]!!.remove(tuple.local)
for (listener in listeners) { for (listener in listeners) {
listener.removeObject(tuple.local.id) listener.removeStack(tuple.local.id)
} }
} }
} }
@ -188,7 +173,7 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
for (consumer in consumers) { for (consumer in consumers) {
leftover = consumer.insertStack(leftover, simulate) leftover = consumer.insertStack(leftover, simulate)
if (leftover.isEmpty()) { if (leftover.isEmpty) {
return leftover return leftover
} }
} }
@ -196,18 +181,20 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
return leftover return leftover
} }
@Suppress("unchecked_cast")
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T {
if (amount.isZero) if (amount.isZero)
return identity.empty return this.storageType.empty
@Suppress("name_shadowing")
var amount = amount var amount = amount
val tuple: LocalTuple<T>? = localByUUID[id] val tuple: LocalTuple<T>? = localByUUID[id]
if (tuple == null || amount.isZero) if (tuple == null || amount.isZero)
return identity.empty return this.storageType.empty
if (amount <= ImpreciseFraction.MINUS_ONE) if (amount <= ImpreciseFraction.MINUS_ONE)
amount = tuple.stack.getMaxStackSize().orElse(tuple.stack.count) amount = tuple.stack.maxStackSize ?: tuple.stack.count
val toExtract = tuple.stack.count.min(amount) val toExtract = tuple.stack.count.min(amount)
var extracted = ImpreciseFraction.ZERO var extracted = ImpreciseFraction.ZERO
@ -225,19 +212,21 @@ open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageCom
return copy return copy
} }
return identity.empty() return this.storageType.empty
} }
} }
open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmField val energyProvider: () -> IMatteryEnergyStorage) : VirtualComponent<T>(identity) { open class PoweredVirtualComponent<T : IStorageStack>(type: StorageStackType<T>, val energyProvider: () -> IMatteryEnergyStorage) : VirtualComponent<T>(type) {
constructor(identity: Class<T>, energyStorage: IMatteryEnergyStorage) : this(identity, {energyStorage}) constructor(type: Class<T>, energyStorage: IMatteryEnergyStorage) : this(StorageRegistry.get(type), {energyStorage})
constructor(other: VirtualComponent<T>, energyStorage: IMatteryEnergyStorage) : this(other.storageIdentity(), energyStorage) { constructor(type: StorageStackType<T>, energyStorage: IMatteryEnergyStorage) : this(type, {energyStorage})
constructor(other: VirtualComponent<T>, energyStorage: IMatteryEnergyStorage) : this(other.storageType.identity, energyStorage) {
add(other) add(other)
} }
@Suppress("unchecked_cast")
override fun insertStack(stack: T, simulate: Boolean): T { override fun insertStack(stack: T, simulate: Boolean): T {
val required = identity.energyPerOperation * stack.count val required = storageType.energyPerOperation * stack.count
val energy = energyProvider() val energy = energyProvider.invoke()
val extracted = energy.extractEnergyInner(required, true) val extracted = energy.extractEnergyInner(required, true)
if (extracted.isZero) { if (extracted.isZero) {
@ -247,7 +236,7 @@ open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmFi
if (extracted == required) { if (extracted == required) {
val leftover = super.insertStack(stack, simulate) val leftover = super.insertStack(stack, simulate)
if (leftover.isEmpty()) { if (leftover.isEmpty) {
if (!simulate) { if (!simulate) {
energy.extractEnergyInner(required, false) energy.extractEnergyInner(required, false)
} }
@ -256,24 +245,25 @@ open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmFi
} }
if (!simulate) { if (!simulate) {
val requiredNew = identity.energyPerOperation * stack.count - leftover.count val requiredNew = storageType.energyPerOperation * stack.count - leftover.count
energy.extractEnergyInner(requiredNew, false) energy.extractEnergyInner(requiredNew, false)
} }
return leftover return leftover
} }
@Suppress("name_shadowing")
val stack = stack.copy() as T val stack = stack.copy() as T
val oldCount = stack.count val oldCount = stack.count
stack.count = extracted / identity.energyPerOperation stack.count = extracted / storageType.energyPerOperation
val diff = oldCount - stack.count val diff = oldCount - stack.count
val newRequired = identity.energyPerOperation * stack.count val newRequired = storageType.energyPerOperation * stack.count
val newExtracted = energy.extractEnergyInner(newRequired, true) val newExtracted = energy.extractEnergyInner(newRequired, true)
if (newExtracted == newRequired) { if (newExtracted == newRequired) {
val leftover = super.insertStack(stack, simulate) val leftover = super.insertStack(stack, simulate)
if (leftover.isEmpty()) { if (leftover.isEmpty) {
if (!simulate) { if (!simulate) {
energy.extractEnergyInner(newRequired, false) energy.extractEnergyInner(newRequired, false)
} }
@ -283,7 +273,7 @@ open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmFi
} }
if (!simulate) { if (!simulate) {
val requiredNew = (stack.count - leftover.count) * identity.energyPerOperation val requiredNew = (stack.count - leftover.count) * storageType.energyPerOperation
energy.extractEnergyInner(requiredNew, false) energy.extractEnergyInner(requiredNew, false)
} }
@ -295,18 +285,18 @@ open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmFi
} }
override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T { override fun extractStack(id: UUID, amount: ImpreciseFraction, simulate: Boolean): T {
val required = identity.energyPerOperation * amount val required = storageType.energyPerOperation * amount
val energy = energyProvider() val energy = energyProvider.invoke()
val extracted = energy.extractEnergyInner(required, true) val extracted = energy.extractEnergyInner(required, true)
if (extracted.isZero) { if (extracted.isZero) {
return identity.empty return storageType.empty
} }
if (extracted == required) { if (extracted == required) {
val extractedStack = super.extractStack(id, amount, simulate) val extractedStack = super.extractStack(id, amount, simulate)
if (extractedStack.isEmpty()) { if (extractedStack.isEmpty) {
return extractedStack return extractedStack
} }
@ -314,22 +304,23 @@ open class PoweredVirtualComponent<T : IStorageStack>(identity: Class<T>, @JvmFi
if (extractedStack.count == amount) { if (extractedStack.count == amount) {
energy.extractEnergyInner(required, false) energy.extractEnergyInner(required, false)
} else { } else {
energy.extractEnergyInner(extractedStack.count * identity.energyPerOperation, false) energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false)
} }
} }
return extractedStack return extractedStack
} }
val amount = required / identity.energyPerOperation @Suppress("name_shadowing")
val amount = required / storageType.energyPerOperation
val extractedStack = super.extractStack(id, amount, simulate) val extractedStack = super.extractStack(id, amount, simulate)
if (extractedStack.isEmpty()) { if (extractedStack.isEmpty) {
return extractedStack return extractedStack
} }
if (!simulate) { if (!simulate) {
energy.extractEnergyInner(extractedStack.count * identity.energyPerOperation, false) energy.extractEnergyInner(extractedStack.count * storageType.energyPerOperation, false)
} }
return extractedStack return extractedStack