VirtualComponent on kotlin, PoweredVirtualComponent, powered storage system
This commit is contained in:
parent
9261562de7
commit
0ed841ab21
@ -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) {
|
||||
|
@ -25,16 +25,16 @@ public class ItemMonitorScreen extends MatteryScreen<ItemMonitorMenu> {
|
||||
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;
|
||||
|
||||
|
@ -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<ItemStackWrapper> {
|
||||
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<ItemStackWrapper> {
|
||||
}
|
||||
|
||||
@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
|
||||
|
@ -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<Class<? extends IStorageStack>, StorageObjectTuple<? extends IStorageStack>> REGISTRY = new HashMap<>();
|
||||
|
||||
public static <T extends IStorageStack> StorageObjectTuple<T> register(Class<T> identity, T empty) {
|
||||
final var tuple = new StorageObjectTuple<>(identity, empty);
|
||||
public static <T extends IStorageStack> StorageObjectTuple<T> register(Class<T> identity, T empty, Fraction energyPerOperation) {
|
||||
final var tuple = new StorageObjectTuple<>(identity, empty, energyPerOperation);
|
||||
REGISTRY.put(identity, tuple);
|
||||
return tuple;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
package ru.dbotthepony.mc.otm.storage;
|
||||
|
||||
public record StorageObjectTuple<T extends IStorageStack>(Class<T> identity, T empty) {
|
||||
import ru.dbotthepony.mc.otm.core.Fraction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public record StorageObjectTuple<T extends IStorageStack>(@Nonnull Class<T> identity, @Nonnull T empty, @Nonnull Fraction energyPerOperation) {
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof StorageObjectTuple tuple)
|
||||
|
@ -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<T extends IStorageStack> implements IStorageComponent<T>, IStorageListener<T> {
|
||||
public record LocalTuple<T extends IStorageStack>(T stack, UUID id, List<RemoteTuple<T>> tuples) implements IStorageTuple<T> {
|
||||
@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 extends IStorageStack>(T object, UUID remote_id, IStorageView<T> provider, LocalTuple<T> 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<T> identity;
|
||||
|
||||
@Override
|
||||
public Class<T> storageIdentity() {
|
||||
return identity.identity();
|
||||
}
|
||||
|
||||
public VirtualComponent(Class<T> identity) {
|
||||
this.identity = StorageObjectRegistry.getOrError(identity);
|
||||
}
|
||||
|
||||
public static <T extends IStorageStack> Supplier<VirtualComponent<T>> factory(Class<T> identity) {
|
||||
return () -> new VirtualComponent<>(identity);
|
||||
}
|
||||
|
||||
// удаленный UUID -> Кортеж
|
||||
protected final HashMap<UUID, RemoteTuple<T>> indexed_by_remote_uuid = new HashMap<>();
|
||||
|
||||
// локальный UUID -> Локальный кортеж
|
||||
protected final HashMap<UUID, LocalTuple<T>> indexed_by_local_uuid = new HashMap<>();
|
||||
|
||||
// Хеш ключ -> Список Локальных кортежей
|
||||
protected final HashMap<Object, ArrayList<LocalTuple<T>>> partitions = new HashMap<>();
|
||||
|
||||
// ArrayList для скорости работы
|
||||
protected final ArrayList<IStorageListener<T>> listeners = new ArrayList<>();
|
||||
protected final ArrayList<IStorageConsumer<T>> consumers = new ArrayList<>();
|
||||
|
||||
public void add(IStorageIdentity<T> identity) {
|
||||
if (identity instanceof IStorageView<T> provider) {
|
||||
provider.addListenerAuto(this);
|
||||
} else if (identity instanceof IStorageTrigger<T> provider) {
|
||||
provider.addListener(this);
|
||||
}
|
||||
|
||||
if (identity instanceof IStorageConsumer<T> provider && !consumers.contains(provider)) {
|
||||
consumers.add(provider);
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(IStorageIdentity<T> identity) {
|
||||
if (identity instanceof IStorageView<T> provider) {
|
||||
provider.removeListenerAuto(this);
|
||||
} else if (identity instanceof IStorageTrigger<T> provider) {
|
||||
provider.removeListener(this);
|
||||
}
|
||||
|
||||
if (identity instanceof IStorageConsumer<T> 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<T> listener) {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeListener(IStorageListener<T> 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<IStorageTuple<T>> getStacks() {
|
||||
int capacity = 0;
|
||||
|
||||
for (var list : partitions.values())
|
||||
capacity += list.size();
|
||||
|
||||
final var output = new ArrayList<IStorageTuple<T>>(capacity);
|
||||
|
||||
for (var listing : partitions.values())
|
||||
output.addAll(listing);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void addObject(T stack, UUID id, IStorageView<T> 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<T> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 <T : BlockEntity?> getTicker(
|
||||
p_153212_: Level,
|
||||
p_153213_: BlockState,
|
||||
p_153214_: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
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,
|
||||
|
@ -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 <T : BlockEntity?> getTicker(
|
||||
p_153212_: Level,
|
||||
p_153213_: BlockState,
|
||||
p_153214_: BlockEntityType<T>
|
||||
): BlockEntityTicker<T>? {
|
||||
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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -20,15 +20,11 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
||||
constructor(capacity: Fraction, uuid: UUID) : super(capacity, uuid)
|
||||
constructor(capacity: Fraction) : super(capacity)
|
||||
|
||||
override fun identity(): StorageObjectTuple<ItemStackWrapper> {
|
||||
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<ItemStackWrapper>): CompoundTag? {
|
||||
@ -90,8 +86,6 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var identity: StorageObjectTuple<ItemStackWrapper>? = null
|
||||
|
||||
@JvmField
|
||||
val DUMMY = ItemMatteryDrive(Fraction(0), UUID(0L, 0L), 0)
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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<ItemStackWrapper>?
|
||||
private val local: PoweredVirtualComponent<ItemStackWrapper>?
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
|
||||
* @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<T : IStorageStack> {
|
||||
/**
|
||||
* 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
|
||||
|
@ -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<T : IStorageStack>(val obj: T, val remote_id: UUID, val provider: IStorageView<T>, val local: LocalTuple<T>) {
|
||||
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<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 {
|
||||
return stack
|
||||
}
|
||||
}
|
||||
|
||||
open class VirtualComponent<T : IStorageStack>(identity: Class<T>) : IStorageComponent<T>, IStorageListener<T> {
|
||||
@JvmField
|
||||
protected val identity: StorageObjectTuple<T>
|
||||
final override fun storageIdentity() = identity.identity()
|
||||
|
||||
init {
|
||||
this.identity = StorageObjectRegistry.getOrError(identity)
|
||||
}
|
||||
|
||||
// удаленный UUID -> Кортеж
|
||||
@JvmField
|
||||
protected val remoteByUUID = HashMap<UUID, RemoteTuple<T>>()
|
||||
|
||||
// локальный UUID -> Локальный кортеж
|
||||
@JvmField
|
||||
protected val localByUUID = HashMap<UUID, LocalTuple<T>>()
|
||||
|
||||
// Хеш ключ -> Список Локальных кортежей
|
||||
@JvmField
|
||||
protected val partitions = HashMap<Any, ArrayList<LocalTuple<T>>>()
|
||||
|
||||
// ArrayList для скорости работы
|
||||
@JvmField
|
||||
protected val listeners = ArrayList<IStorageListener<T>>()
|
||||
|
||||
@JvmField
|
||||
protected val consumers = ArrayList<IStorageConsumer<T>>()
|
||||
|
||||
open fun add(identity: IStorageIdentity<T>) {
|
||||
if (identity is IStorageView<T>) {
|
||||
identity.addListenerAuto(this)
|
||||
} else if (identity is IStorageTrigger<T>) {
|
||||
identity.addListener(this)
|
||||
}
|
||||
|
||||
if (identity is IStorageConsumer<T> && !consumers.contains(identity)) {
|
||||
consumers.add(identity);
|
||||
}
|
||||
}
|
||||
|
||||
open fun remove(identity: IStorageIdentity<T>) {
|
||||
if (identity is IStorageView<T>) {
|
||||
identity.removeListenerAuto(this)
|
||||
} else if (identity is IStorageTrigger<T>) {
|
||||
identity.removeListener(this)
|
||||
}
|
||||
|
||||
if (identity is IStorageConsumer<T>) {
|
||||
consumers.remove(identity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun addListener(listener: IStorageListener<T>): Boolean {
|
||||
if (!listeners.contains(listener)) {
|
||||
listeners.add(listener)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
override fun removeListener(listener: IStorageListener<T>): Boolean {
|
||||
return listeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun getStack(id: UUID): T {
|
||||
return localByUUID[id]?.stack ?: identity.empty
|
||||
}
|
||||
|
||||
override fun getStacks(): List<IStorageTuple<T>> {
|
||||
var capacity = 0
|
||||
for (list in partitions.values) capacity += list.size
|
||||
val output = ArrayList<IStorageTuple<T>>(capacity)
|
||||
for (listing in partitions.values) output.addAll(listing)
|
||||
return output
|
||||
}
|
||||
|
||||
override fun addObject(stack: T, id: UUID, provider: IStorageView<T>) {
|
||||
check(!remoteByUUID.containsKey(id)) { "Already tracking tuple with id $id" }
|
||||
val items = partitions.computeIfAbsent(stack.partitionKey()) { ArrayList() }
|
||||
var local: LocalTuple<T>? = 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<RemoteTuple<T>>(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<T>? = 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<T : IStorageStack>(identity: Class<T>, @JvmField val energyProvider: () -> IMatteryEnergyStorage) : VirtualComponent<T>(identity) {
|
||||
constructor(identity: Class<T>, energyStorage: IMatteryEnergyStorage) : this(identity, {energyStorage})
|
||||
constructor(other: VirtualComponent<T>, 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user