From 0ed841ab2136de09b0f69a7f3b8f20ca281c2051 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Sun, 2 Jan 2022 12:55:35 +0700 Subject: [PATCH] VirtualComponent on kotlin, PoweredVirtualComponent, powered storage system --- .../mc/otm/OverdriveThatMatters.java | 3 +- .../otm/client/screen/ItemMonitorScreen.java | 8 +- .../mc/otm/menu/data/NetworkedItemView.java | 10 +- .../mc/otm/storage/StorageObjectRegistry.java | 5 +- .../mc/otm/storage/StorageObjectTuple.java | 6 +- .../mc/otm/storage/VirtualComponent.java | 270 -------------- .../mc/otm/block/BlockDriveRack.kt | 15 + .../mc/otm/block/BlockItemMonitor.kt | 16 + .../otm/block/entity/BlockEntityDriveRack.kt | 9 +- .../block/entity/BlockEntityItemMonitor.kt | 6 +- .../otm/capability/drive/ItemMatteryDrive.kt | 14 +- .../ru/dbotthepony/mc/otm/core/Fraction.kt | 25 ++ .../mc/otm/menu/ItemMonitorMenu.kt | 22 +- .../ru/dbotthepony/mc/otm/storage/API.kt | 4 +- .../mc/otm/storage/VirtualComponent.kt | 337 ++++++++++++++++++ 15 files changed, 452 insertions(+), 298 deletions(-) delete mode 100644 src/main/java/ru/dbotthepony/mc/otm/storage/VirtualComponent.java create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 839c1c3e6..6fbedd9a2 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -27,6 +27,7 @@ import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer; import ru.dbotthepony.mc.otm.capability.drive.DrivePool; import ru.dbotthepony.mc.otm.client.AndroidGui; import ru.dbotthepony.mc.otm.client.EventHandler; +import ru.dbotthepony.mc.otm.core.Fraction; import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive; import ru.dbotthepony.mc.otm.matter.MatterRegistry; import ru.dbotthepony.mc.otm.network.MatteryNetworking; @@ -165,7 +166,7 @@ public class OverdriveThatMatters { MatterRegistry.registerInitialItems(); - StorageObjectRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY); + StorageObjectRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Fraction("3.125")); } private void setupClient(final FMLClientSetupEvent event) { diff --git a/src/main/java/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.java b/src/main/java/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.java index c501188cb..e26b8bbf4 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.java +++ b/src/main/java/ru/dbotthepony/mc/otm/client/screen/ItemMonitorScreen.java @@ -25,16 +25,16 @@ public class ItemMonitorScreen extends MatteryScreen { protected FramePanel makeMainFrame() { var frame = new FramePanel(this, null, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, getTitle()); - var grid = new GridPanel(this, frame, 0, 0, 0, 0, GRID_WIDTH, GRID_HEIGHT); - grid.setDock(Dock.FILL); - grid.setDockMargin(2, 2, 2, 2); - var scroll_bar = new ScrollBarPanel(this, frame, 0, 0, 0); scroll_bar.setDock(Dock.RIGHT); scroll_bar.setupRowMultiplier(() -> { return menu.view.getItems().size() / GRID_WIDTH; }); + var grid = new GridPanel(this, frame, 0, 0, GRID_WIDTH * 18, 0, GRID_WIDTH, GRID_HEIGHT); + grid.setDock(Dock.RIGHT); + grid.setDockMargin(2, 2, 2, 2); + for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; i++) { final int index = i; diff --git a/src/main/java/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.java b/src/main/java/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.java index 181f9984f..a4fbd666e 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.java +++ b/src/main/java/ru/dbotthepony/mc/otm/menu/data/NetworkedItemView.java @@ -16,7 +16,6 @@ import ru.dbotthepony.mc.otm.storage.*; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -77,6 +76,11 @@ public class NetworkedItemView implements IStorageListener { provider.addListenerAuto(this); } + public void removed() { + if (this.provider != null) + this.provider.removeListenerAuto(this); + } + public record NetworkedItem(int id, ItemStack stack, @Nullable UUID id_upstream) { public NetworkedItem(int id, ItemStack stack) { this(id, stack, null); @@ -100,8 +104,8 @@ public class NetworkedItemView implements IStorageListener { } @Override - public void changeObject(UUID id, Fraction new_count) { - changeObject(id, new_count.toInt()); + public void changeObject(UUID id, Fraction newCount) { + changeObject(id, newCount.toInt()); } @Override diff --git a/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectRegistry.java b/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectRegistry.java index 227aadcaa..9111b1f5f 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectRegistry.java +++ b/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectRegistry.java @@ -1,6 +1,7 @@ package ru.dbotthepony.mc.otm.storage; import net.minecraft.MethodsReturnNonnullByDefault; +import ru.dbotthepony.mc.otm.core.Fraction; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -13,8 +14,8 @@ import java.util.Objects; public class StorageObjectRegistry { private static final HashMap, StorageObjectTuple> REGISTRY = new HashMap<>(); - public static StorageObjectTuple register(Class identity, T empty) { - final var tuple = new StorageObjectTuple<>(identity, empty); + public static StorageObjectTuple register(Class identity, T empty, Fraction energyPerOperation) { + final var tuple = new StorageObjectTuple<>(identity, empty, energyPerOperation); REGISTRY.put(identity, tuple); return tuple; } diff --git a/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectTuple.java b/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectTuple.java index 81ef55251..6edc70d33 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectTuple.java +++ b/src/main/java/ru/dbotthepony/mc/otm/storage/StorageObjectTuple.java @@ -1,6 +1,10 @@ package ru.dbotthepony.mc.otm.storage; -public record StorageObjectTuple(Class identity, T empty) { +import ru.dbotthepony.mc.otm.core.Fraction; + +import javax.annotation.Nonnull; + +public record StorageObjectTuple(@Nonnull Class identity, @Nonnull T empty, @Nonnull Fraction energyPerOperation) { @Override public boolean equals(Object obj) { if (obj instanceof StorageObjectTuple tuple) diff --git a/src/main/java/ru/dbotthepony/mc/otm/storage/VirtualComponent.java b/src/main/java/ru/dbotthepony/mc/otm/storage/VirtualComponent.java deleted file mode 100644 index 38d292848..000000000 --- a/src/main/java/ru/dbotthepony/mc/otm/storage/VirtualComponent.java +++ /dev/null @@ -1,270 +0,0 @@ -package ru.dbotthepony.mc.otm.storage; - -import net.minecraft.MethodsReturnNonnullByDefault; -import ru.dbotthepony.mc.otm.core.Fraction; - -import javax.annotation.ParametersAreNonnullByDefault; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.UUID; -import java.util.function.Supplier; - -@MethodsReturnNonnullByDefault -@ParametersAreNonnullByDefault -public class VirtualComponent implements IStorageComponent, IStorageListener { - public record LocalTuple(T stack, UUID id, List> tuples) implements IStorageTuple { - @Override - public boolean equals(Object obj) { - return obj instanceof LocalTuple tuple && tuple.id.equals(id) || obj instanceof UUID id && this.id.equals(id); - } - - @Override - public int hashCode() { - return id.hashCode(); - } - } - - public record RemoteTuple(T object, UUID remote_id, IStorageView provider, LocalTuple local) { - public T extract(Fraction amount, boolean simulate) { - return provider.extractStack(remote_id, amount, simulate); - } - - public Fraction extractCount(Fraction amount, boolean simulate) { - return provider.extractStackCount(remote_id, amount, simulate); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof RemoteTuple tuple && tuple.remote_id.equals(remote_id) || obj instanceof UUID id && id.equals(remote_id); - } - - @Override - public int hashCode() { - return remote_id.hashCode(); - } - } - - protected final StorageObjectTuple identity; - - @Override - public Class storageIdentity() { - return identity.identity(); - } - - public VirtualComponent(Class identity) { - this.identity = StorageObjectRegistry.getOrError(identity); - } - - public static Supplier> factory(Class identity) { - return () -> new VirtualComponent<>(identity); - } - - // удаленный UUID -> Кортеж - protected final HashMap> indexed_by_remote_uuid = new HashMap<>(); - - // локальный UUID -> Локальный кортеж - protected final HashMap> indexed_by_local_uuid = new HashMap<>(); - - // Хеш ключ -> Список Локальных кортежей - protected final HashMap>> partitions = new HashMap<>(); - - // ArrayList для скорости работы - protected final ArrayList> listeners = new ArrayList<>(); - protected final ArrayList> consumers = new ArrayList<>(); - - public void add(IStorageIdentity identity) { - if (identity instanceof IStorageView provider) { - provider.addListenerAuto(this); - } else if (identity instanceof IStorageTrigger provider) { - provider.addListener(this); - } - - if (identity instanceof IStorageConsumer provider && !consumers.contains(provider)) { - consumers.add(provider); - } - } - - public void remove(IStorageIdentity identity) { - if (identity instanceof IStorageView provider) { - provider.removeListenerAuto(this); - } else if (identity instanceof IStorageTrigger provider) { - provider.removeListener(this); - } - - if (identity instanceof IStorageConsumer provider) { - consumers.remove(provider); - } - } - - @Override - public T insertStack(T stack, boolean simulate) { - var leftover = stack; - - for (var consumer : consumers) { - leftover = consumer.insertStack(leftover, simulate); - - if (leftover.isEmpty()) { - return leftover; - } - } - - return leftover; - } - - @Override - public boolean addListener(IStorageListener listener) { - if (!listeners.contains(listener)) { - listeners.add(listener); - return true; - } - - return false; - } - - @Override - public boolean removeListener(IStorageListener listener) { - return listeners.remove(listener); - } - - @Override - public T getStack(UUID id) { - final var tuple = indexed_by_local_uuid.get(id); - return tuple != null ? tuple.stack : identity.empty(); - } - - @Override - @SuppressWarnings("unchecked") - public T extractStack(UUID id, Fraction amount, boolean simulate) { - var tuple = indexed_by_local_uuid.get(id); - - if (tuple == null || amount.compareTo(Fraction.ZERO) == 0) - return identity.empty(); - - if (amount.compareTo(Fraction.MINUS_ONE) <= 0) - amount = tuple.stack.getMaxStackSize().orElse(tuple.stack.getCount()); - - var extract = tuple.stack.getCount().min(amount); - var extracted = Fraction.ZERO; - - final var copy = (T) tuple.stack.copy(); - - for (var remote_tuple : tuple.tuples) { - extracted = extracted.plus(remote_tuple.extractCount(extract.minus(extracted), simulate)); - - if (extracted.compareTo(extract) >= 0) - break; - } - - if (extracted.compareTo(Fraction.ZERO) > 0) { - copy.setCount(extracted); - return copy; - } - - return identity.empty(); - } - - @Override - public List> getStacks() { - int capacity = 0; - - for (var list : partitions.values()) - capacity += list.size(); - - final var output = new ArrayList>(capacity); - - for (var listing : partitions.values()) - output.addAll(listing); - - return output; - } - - @Override - @SuppressWarnings("unchecked") - public void addObject(T stack, UUID id, IStorageView provider) { - if (indexed_by_remote_uuid.containsKey(id)) { - throw new IllegalStateException("Already tracking tuple with id " + id); - } - - var items = partitions.computeIfAbsent(stack.partitionKey(), (k) -> new ArrayList<>()); - LocalTuple local_tuple = null; - - for (var item : items) { - if (item.stack.sameItem(stack)) { - item.stack.grow(stack.getCount()); - local_tuple = item; - break; - } - } - - boolean added = local_tuple == null; - - if (added) { - local_tuple = new LocalTuple<>((T) stack.copy(), UUID.randomUUID(), new ArrayList<>(1)); - items.add(local_tuple); - indexed_by_local_uuid.put(local_tuple.id, local_tuple); - } - - var tuple = new RemoteTuple<>((T) stack.copy(), id, provider, local_tuple); - local_tuple.tuples.add(tuple); - indexed_by_remote_uuid.put(id, tuple); - - if (added) { - for (var listener : listeners) { - listener.addObject(local_tuple.stack, local_tuple.id, this); - } - } else { - for (var listener : listeners) { - listener.changeObject(local_tuple.id, local_tuple.stack.getCount()); - } - } - } - - - @Override - public void changeObject(UUID id, Fraction new_count) { - assert new_count.compareTo(Fraction.ZERO) > 0; - var tuple = indexed_by_remote_uuid.get(id); - - if (tuple == null) - throw new IllegalStateException("No such tuple with id " + id); - - final var diff = new_count.minus(tuple.object.getCount()); - tuple.object.setCount(new_count); - tuple.local.stack.grow(diff); - - for (var listener : listeners) { - listener.changeObject(tuple.local.id, tuple.local.stack.getCount()); - } - } - - @Override - public void removeObject(UUID id) { - var tuple = indexed_by_remote_uuid.get(id); - - if (tuple == null) - throw new IllegalStateException("No such tuple with id " + id); - - final var item = tuple.local.stack.partitionKey(); - tuple.local.stack.shrink(tuple.object.getCount()); - tuple.local.tuples.remove(tuple); - - indexed_by_remote_uuid.remove(id); - - final boolean a = tuple.local.stack.getCount().compareTo(Fraction.ZERO) <= 0; - final boolean b = tuple.local.tuples.size() == 0; - - if (a || b) { - if (!(a && b)) - throw new IllegalStateException("View object is empty, but tuple list is not!"); - - indexed_by_local_uuid.remove(tuple.local.id); - partitions.get(item).remove(tuple.local); - - for (var listener : listeners) { - listener.removeObject(tuple.local.id); - } - } - } -} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockDriveRack.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockDriveRack.kt index 480c6b12b..18a5da9ad 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockDriveRack.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockDriveRack.kt @@ -3,11 +3,15 @@ package ru.dbotthepony.mc.otm.block import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.world.level.BlockGetter +import net.minecraft.world.level.Level import net.minecraft.world.level.block.EntityBlock import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.shapes.CollisionContext import net.minecraft.world.phys.shapes.VoxelShape +import ru.dbotthepony.mc.otm.Registry import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveRack import ru.dbotthepony.mc.otm.shapes.BlockShapes @@ -16,6 +20,17 @@ class BlockDriveRack : BlockMatteryRotatable(), EntityBlock { return BlockEntityDriveRack(blockPos, blockState) } + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (p_153214_ != Registry.BlockEntities.DRIVE_RACK || p_153212_.isClientSide) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is BlockEntityDriveRack) tile.tick() } + } + override fun getShape( p_60555_: BlockState, p_60556_: BlockGetter, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockItemMonitor.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockItemMonitor.kt index a5f2d196b..27ec55379 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockItemMonitor.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockItemMonitor.kt @@ -3,11 +3,16 @@ package ru.dbotthepony.mc.otm.block import net.minecraft.core.BlockPos import net.minecraft.core.Direction import net.minecraft.world.level.BlockGetter +import net.minecraft.world.level.Level import net.minecraft.world.level.block.EntityBlock import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.phys.shapes.CollisionContext import net.minecraft.world.phys.shapes.VoxelShape +import ru.dbotthepony.mc.otm.Registry +import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveRack import ru.dbotthepony.mc.otm.block.entity.BlockEntityItemMonitor import ru.dbotthepony.mc.otm.shapes.BlockShapes @@ -16,6 +21,17 @@ class BlockItemMonitor : BlockMatteryRotatable(), EntityBlock { return BlockEntityItemMonitor(blockPos, blockState) } + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (p_153214_ != Registry.BlockEntities.ITEM_MONITOR || p_153212_.isClientSide) + return null + + return BlockEntityTicker { _, _, _, tile -> if (tile is BlockEntityItemMonitor) tile.tick() } + } + override fun getShape( p_60555_: BlockState, p_60556_: BlockGetter, diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityDriveRack.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityDriveRack.kt index b0118d4f1..e801c08cc 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityDriveRack.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityDriveRack.kt @@ -24,6 +24,7 @@ import ru.dbotthepony.mc.otm.ifHas import ru.dbotthepony.mc.otm.menu.DriveRackMenu import ru.dbotthepony.mc.otm.set import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph +import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent import ru.dbotthepony.mc.otm.storage.VirtualComponent class BlockEntityDriveRack(p_155229_: BlockPos, p_155230_: BlockState) : @@ -35,11 +36,11 @@ class BlockEntityDriveRack(p_155229_: BlockPos, p_155230_: BlockState) : super.setChanged(slot, new_state, old_state) old_state.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageIdentity()) {c -> VirtualComponent(c)}.remove(it) + cell.computeIfAbsent(it.storageIdentity()) {c -> PoweredVirtualComponent(c, energy)}.remove(it) } new_state.getCapability(MatteryCapability.DRIVE).ifPresent { - cell.computeIfAbsent(it.storageIdentity()) {c -> VirtualComponent(c)}.add(it) + cell.computeIfAbsent(it.storageIdentity()) {c -> PoweredVirtualComponent(c, energy)}.add(it) } } } @@ -70,6 +71,10 @@ class BlockEntityDriveRack(p_155229_: BlockPos, p_155230_: BlockState) : energy = MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, Fraction(80000)) } + fun tick() { + batteryChargeLoop() + } + override fun getDefaultDisplayName(): Component { return NAME } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityItemMonitor.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityItemMonitor.kt index bae3321a1..2c60cc174 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityItemMonitor.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityItemMonitor.kt @@ -27,7 +27,11 @@ class BlockEntityItemMonitor(p_155229_: BlockPos, p_155230_: BlockState) : val cell = BasicStorageGraphNode() init { - energy = MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, Fraction(80000)) + energy = MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, Fraction(80_000)) + } + + fun tick() { + batteryChargeLoop() } override fun getDefaultDisplayName(): Component { 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 ec1be24e2..f070fc40a 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 @@ -20,15 +20,11 @@ class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDri constructor(capacity: Fraction, uuid: UUID) : super(capacity, uuid) constructor(capacity: Fraction) : super(capacity) - override fun identity(): StorageObjectTuple { - if (identity == null) - identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java) + private val identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java) + override fun identity() = identity - return identity!! - } - - fun insertObject(item: ItemStack?, simulate: Boolean): ItemStack { - return insertStack(ItemStackWrapper(item!!), simulate).stack + fun insertObject(item: ItemStack, simulate: Boolean): ItemStack { + return insertStack(ItemStackWrapper(item), simulate).stack } override fun serializeStack(item: IStorageTuple): CompoundTag? { @@ -90,8 +86,6 @@ class ItemMatteryDrive : AbstractMatteryDrive, IItemMatteryDri } companion object { - private var identity: StorageObjectTuple? = null - @JvmField val DUMMY = ItemMatteryDrive(Fraction(0), UUID(0L, 0L), 0) } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt index 10a41d88f..67099da1c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Fraction.kt @@ -254,6 +254,24 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @ @JvmOverloads constructor(value: BigDecimal, compact: Boolean = true) : this(powUnscaled(value.unscaledValue(), value.scale()), powScale(value.scale()), compact = compact) @JvmOverloads constructor(value: BigDecimal, div: BigDecimal, compact: Boolean = true) : this(powUnscaled(value.unscaledValue(), value.scale()).multiply(powScale(div.scale())), powScale(value.scale()).multiply(powUnscaled(div.unscaledValue(), div.scale())), compact = compact) + override fun equals(other: Any?): Boolean { + if (other is Fraction) { + if (other.divisor == divisor) + return other.value == value + + val a = value * other.divisor + val b = other.value * divisor + + return a == b + } + + return false + } + + override fun hashCode(): Int { + return 31 * value.hashCode() + divisor.hashCode() + } + fun compactAndCanonize(): Fraction { if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE) return this @@ -285,6 +303,11 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @ return value == BigInteger.ZERO } + fun isOne(): Boolean { + if (isNaN()) return false + return value != BigInteger.ZERO && value == divisor + } + fun compact(): Fraction { if (isNaN()) return this @@ -455,6 +478,7 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @ operator fun times(other: Fraction): Fraction { if (isNaN()) return this if (other.isNaN()) return other + if (other.isOne()) return this if (compact) return timesCompact(other) @@ -479,6 +503,7 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @ operator fun div(other: Fraction): Fraction { if (isNaN()) return this if (other.isNaN()) return other + if (other.isOne()) return this if (compact) return divCompact(other) 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 d8e6d1f2f..4cc16b874 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/menu/ItemMonitorMenu.kt @@ -1,11 +1,16 @@ package ru.dbotthepony.mc.otm.menu import net.minecraft.world.entity.player.Inventory +import net.minecraft.world.entity.player.Player import ru.dbotthepony.mc.otm.Registry import ru.dbotthepony.mc.otm.block.entity.BlockEntityItemMonitor +import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.capability.MatteryCapability import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewSupplier import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView import ru.dbotthepony.mc.otm.storage.ItemStackWrapper +import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent +import ru.dbotthepony.mc.otm.storage.VirtualComponent class ItemMonitorMenu @JvmOverloads constructor( p_38852_: Int, @@ -14,6 +19,8 @@ class ItemMonitorMenu @JvmOverloads constructor( ) : PoweredMatteryMenu(Registry.Menus.ITEM_MONITOR, p_38852_, inventory, tile), INetworkedItemViewSupplier { @JvmField val view = NetworkedItemView(inventory.player, this, tile == null) + private val subscribed: VirtualComponent? + private val local: PoweredVirtualComponent? override fun getNetworkedItemView(): NetworkedItemView { return view @@ -21,13 +28,24 @@ class ItemMonitorMenu @JvmOverloads constructor( init { if (tile != null) { - view.setComponent(tile.cell.getStorageGraph()!!.getVirtualComponent(ItemStackWrapper::class.java)) + subscribed = tile.cell.getStorageGraph()!!.getVirtualComponent(ItemStackWrapper::class.java) + local = PoweredVirtualComponent(subscribed, tile.getCapability(MatteryCapability.ENERGY).resolve().get()) + view.setComponent(local) + } else { + subscribed = null + local = null } addBatterySlot() addInventorySlots() } + override fun removed(p_38940_: Player) { + super.removed(p_38940_) + view.removed() + subscribed?.removeListenerAuto(local!!) + } + override fun broadcastChanges() { super.broadcastChanges() view.network() @@ -40,4 +58,4 @@ class ItemMonitorMenu @JvmOverloads constructor( override fun getWorkingSlotEnd(): Int { return 1 } -} \ No newline at end of file +} 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 d521b5891..fb69e090b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/API.kt @@ -94,7 +94,7 @@ interface IStorageView : IStorageTrigger { * @param simulate whenever to simulate the action or not * @return amount extracted */ - fun extractStackCount(id: UUID, amount: Fraction, simulate: Boolean): Fraction? { + fun extractStackCount(id: UUID, amount: Fraction, simulate: Boolean): Fraction { return extractStack(id, amount, simulate).count } @@ -134,7 +134,7 @@ interface IStorageListener { /** * Fired on whenever an object is changes on listener we subscribed to */ - fun changeObject(id: UUID, new_count: Fraction) + fun changeObject(id: UUID, newCount: Fraction) /** * Fired on whenever an object is removed from listener we subscribed to diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt new file mode 100644 index 000000000..16d64f31c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/storage/VirtualComponent.kt @@ -0,0 +1,337 @@ +package ru.dbotthepony.mc.otm.storage + +import ru.dbotthepony.mc.otm.capability.IMatteryEnergyStorage +import ru.dbotthepony.mc.otm.core.Fraction +import java.util.* +import java.util.function.Supplier + +class RemoteTuple(val obj: T, val remote_id: UUID, val provider: IStorageView, val local: LocalTuple) { + fun extract(amount: Fraction, simulate: Boolean): T { + return provider.extractStack(remote_id, amount, simulate) + } + + fun extractCount(amount: Fraction, simulate: Boolean): Fraction { + return provider.extractStackCount(remote_id, amount, simulate) + } + + override fun equals(other: Any?): Boolean { + return other is RemoteTuple<*> && other.remote_id == remote_id || other is UUID && other == remote_id + } + + override fun hashCode(): Int { + return remote_id.hashCode() + } +} + +class LocalTuple(override val stack: T, override val id: UUID, val tuples: ArrayList>) : IStorageTuple { + override fun id(): UUID { + return id + } + + override fun stack(): T { + return stack + } +} + +open class VirtualComponent(identity: Class) : IStorageComponent, IStorageListener { + @JvmField + protected val identity: StorageObjectTuple + final override fun storageIdentity() = identity.identity() + + init { + this.identity = StorageObjectRegistry.getOrError(identity) + } + + // удаленный UUID -> Кортеж + @JvmField + protected val remoteByUUID = HashMap>() + + // локальный UUID -> Локальный кортеж + @JvmField + protected val localByUUID = HashMap>() + + // Хеш ключ -> Список Локальных кортежей + @JvmField + protected val partitions = HashMap>>() + + // ArrayList для скорости работы + @JvmField + protected val listeners = ArrayList>() + + @JvmField + protected val consumers = ArrayList>() + + open fun add(identity: IStorageIdentity) { + if (identity is IStorageView) { + identity.addListenerAuto(this) + } else if (identity is IStorageTrigger) { + identity.addListener(this) + } + + if (identity is IStorageConsumer && !consumers.contains(identity)) { + consumers.add(identity); + } + } + + open fun remove(identity: IStorageIdentity) { + if (identity is IStorageView) { + identity.removeListenerAuto(this) + } else if (identity is IStorageTrigger) { + identity.removeListener(this) + } + + if (identity is IStorageConsumer) { + consumers.remove(identity) + } + } + + override fun addListener(listener: IStorageListener): Boolean { + if (!listeners.contains(listener)) { + listeners.add(listener) + return true + } + + return false + } + + override fun removeListener(listener: IStorageListener): Boolean { + return listeners.remove(listener) + } + + override fun getStack(id: UUID): T { + return localByUUID[id]?.stack ?: identity.empty + } + + override fun getStacks(): List> { + var capacity = 0 + for (list in partitions.values) capacity += list.size + val output = ArrayList>(capacity) + for (listing in partitions.values) output.addAll(listing) + return output + } + + override fun addObject(stack: T, id: UUID, provider: IStorageView) { + check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" } + val items = partitions.computeIfAbsent(stack.partitionKey()) { ArrayList() } + var local: LocalTuple? = null + + for (item in items) { + if (item.stack.sameItem(stack)) { + item.stack.grow(stack.count) + local = item + break + } + } + + val added = local == null + + if (local == null) { + local = LocalTuple(stack.copy() as T, UUID.randomUUID(), ArrayList>(1)) + items.add(local) + localByUUID[local.id] = local + } + + val remote = RemoteTuple(stack.copy() as T, id, provider, local) + local.tuples.add(remote) + remoteByUUID[id] = remote + + if (added) { + for (listener in listeners) { + listener.addObject(local.stack, local.id, this) + } + } else { + for (listener in listeners) { + listener.changeObject(local.id, local.stack.count) + } + } + } + + override fun changeObject(id: UUID, newCount: Fraction) { + assert(newCount > Fraction.ZERO) + val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") + + val diff = newCount - tuple.obj.count + tuple.obj.count = newCount + tuple.local.stack.grow(diff) + + for (listener in listeners) { + listener.changeObject(tuple.local.id, tuple.local.stack.count) + } + } + + override fun removeObject(id: UUID) { + val tuple = remoteByUUID[id] ?: throw IllegalStateException("No such tuple with id $id") + val key = tuple.local.stack.partitionKey() + + tuple.local.stack.shrink(tuple.obj.count) + tuple.local.tuples.remove(tuple) + + remoteByUUID.remove(id) + + val a = tuple.local.stack.count <= Fraction.ZERO + val b = tuple.local.tuples.size == 0 + + if (a || b) { + check(a && b) { "View object is empty, but tuple list is not!" } + localByUUID.remove(tuple.local.id) + partitions[key]!!.remove(tuple.local) + + for (listener in listeners) { + listener.removeObject(tuple.local.id) + } + } + } + + override fun insertStack(stack: T, simulate: Boolean): T { + var leftover = stack + + for (consumer in consumers) { + leftover = consumer.insertStack(leftover, simulate) + + if (leftover.isEmpty()) { + return leftover + } + } + + return leftover + } + + override fun extractStack(id: UUID, amount: Fraction, simulate: Boolean): T { + if (amount.isZero()) + return identity.empty + + var amount = amount + val tuple: LocalTuple? = localByUUID[id] + + if (tuple == null || amount.isZero()) + return identity.empty + + if (amount <= Fraction.MINUS_ONE) + amount = tuple.stack.getMaxStackSize().orElse(tuple.stack.count) + + val toExtract = tuple.stack.count.min(amount) + var extracted = Fraction.ZERO + val copy = tuple.stack.copy() as T + + for (remote_tuple in tuple.tuples) { + extracted += remote_tuple.extractCount(toExtract - extracted, simulate) + + if (extracted >= toExtract) + break + } + + if (extracted > Fraction.ZERO) { + copy.count = extracted + return copy + } + + return identity.empty() + } +} + +open class PoweredVirtualComponent(identity: Class, @JvmField val energyProvider: () -> IMatteryEnergyStorage) : VirtualComponent(identity) { + constructor(identity: Class, energyStorage: IMatteryEnergyStorage) : this(identity, {energyStorage}) + constructor(other: VirtualComponent, energyStorage: IMatteryEnergyStorage) : this(other.storageIdentity(), energyStorage) { + add(other) + } + + override fun insertStack(stack: T, simulate: Boolean): T { + val required = stack.count * identity.energyPerOperation + val energy = energyProvider() + val extracted = energy.extractEnergyInner(required, true) + + if (extracted.isZero()) { + return stack.copy() as T + } + + if (extracted == required) { + val leftover = super.insertStack(stack, simulate) + + if (leftover.isEmpty()) { + if (!simulate) { + energy.extractEnergyInner(required, false) + } + + return leftover + } + + if (!simulate) { + val requiredNew = (stack.count - leftover.count) * identity.energyPerOperation + energy.extractEnergyInner(requiredNew, false) + } + + return leftover + } + + val stack = stack.copy() as T + val oldCount = stack.count + stack.count = extracted / identity.energyPerOperation + val diff = oldCount - stack.count + val newRequired = stack.count * identity.energyPerOperation + val newExtracted = energy.extractEnergyInner(newRequired, true) + + if (newExtracted == newRequired) { + val leftover = super.insertStack(stack, simulate) + + if (leftover.isEmpty()) { + if (!simulate) { + energy.extractEnergyInner(newRequired, false) + } + + leftover.count = diff + return leftover + } + + if (!simulate) { + val requiredNew = (stack.count - leftover.count) * identity.energyPerOperation + energy.extractEnergyInner(requiredNew, false) + } + + leftover.count += diff + return leftover + } + + return stack + } + + override fun extractStack(id: UUID, amount: Fraction, simulate: Boolean): T { + val required = amount * identity.energyPerOperation + val energy = energyProvider() + val extracted = energy.extractEnergyInner(required, true) + + if (extracted.isZero()) { + return identity.empty + } + + if (extracted == required) { + val extractedStack = super.extractStack(id, amount, simulate) + + if (extractedStack.isEmpty()) { + return extractedStack + } + + if (!simulate) { + if (extractedStack.count == amount) { + energy.extractEnergyInner(required, false) + } else { + energy.extractEnergyInner(extractedStack.count * identity.energyPerOperation, false) + } + } + + return extractedStack + } + + val amount = required / identity.energyPerOperation + val extractedStack = super.extractStack(id, amount, simulate) + + if (extractedStack.isEmpty()) { + return extractedStack + } + + if (!simulate) { + energy.extractEnergyInner(extractedStack.count * identity.energyPerOperation, false) + } + + return extractedStack + } +}