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.capability.drive.DrivePool;
|
||||||
import ru.dbotthepony.mc.otm.client.AndroidGui;
|
import ru.dbotthepony.mc.otm.client.AndroidGui;
|
||||||
import ru.dbotthepony.mc.otm.client.EventHandler;
|
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.item.ItemPortableCondensationDrive;
|
||||||
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
|
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
|
||||||
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
|
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
|
||||||
@ -165,7 +166,7 @@ public class OverdriveThatMatters {
|
|||||||
|
|
||||||
MatterRegistry.registerInitialItems();
|
MatterRegistry.registerInitialItems();
|
||||||
|
|
||||||
StorageObjectRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY);
|
StorageObjectRegistry.register(ItemStackWrapper.class, ItemStackWrapper.EMPTY, new Fraction("3.125"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupClient(final FMLClientSetupEvent event) {
|
private void setupClient(final FMLClientSetupEvent event) {
|
||||||
|
@ -25,16 +25,16 @@ public class ItemMonitorScreen extends MatteryScreen<ItemMonitorMenu> {
|
|||||||
protected FramePanel makeMainFrame() {
|
protected FramePanel makeMainFrame() {
|
||||||
var frame = new FramePanel(this, null, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, getTitle());
|
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);
|
var scroll_bar = new ScrollBarPanel(this, frame, 0, 0, 0);
|
||||||
scroll_bar.setDock(Dock.RIGHT);
|
scroll_bar.setDock(Dock.RIGHT);
|
||||||
scroll_bar.setupRowMultiplier(() -> {
|
scroll_bar.setupRowMultiplier(() -> {
|
||||||
return menu.view.getItems().size() / GRID_WIDTH;
|
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++) {
|
for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; i++) {
|
||||||
final int index = i;
|
final int index = i;
|
||||||
|
|
||||||
|
@ -16,7 +16,6 @@ import ru.dbotthepony.mc.otm.storage.*;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -77,6 +76,11 @@ public class NetworkedItemView implements IStorageListener<ItemStackWrapper> {
|
|||||||
provider.addListenerAuto(this);
|
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 record NetworkedItem(int id, ItemStack stack, @Nullable UUID id_upstream) {
|
||||||
public NetworkedItem(int id, ItemStack stack) {
|
public NetworkedItem(int id, ItemStack stack) {
|
||||||
this(id, stack, null);
|
this(id, stack, null);
|
||||||
@ -100,8 +104,8 @@ public class NetworkedItemView implements IStorageListener<ItemStackWrapper> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changeObject(UUID id, Fraction new_count) {
|
public void changeObject(UUID id, Fraction newCount) {
|
||||||
changeObject(id, new_count.toInt());
|
changeObject(id, newCount.toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ru.dbotthepony.mc.otm.storage;
|
package ru.dbotthepony.mc.otm.storage;
|
||||||
|
|
||||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||||
|
import ru.dbotthepony.mc.otm.core.Fraction;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -13,8 +14,8 @@ import java.util.Objects;
|
|||||||
public class StorageObjectRegistry {
|
public class StorageObjectRegistry {
|
||||||
private static final HashMap<Class<? extends IStorageStack>, StorageObjectTuple<? extends IStorageStack>> REGISTRY = new HashMap<>();
|
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) {
|
public static <T extends IStorageStack> StorageObjectTuple<T> register(Class<T> identity, T empty, Fraction energyPerOperation) {
|
||||||
final var tuple = new StorageObjectTuple<>(identity, empty);
|
final var tuple = new StorageObjectTuple<>(identity, empty, energyPerOperation);
|
||||||
REGISTRY.put(identity, tuple);
|
REGISTRY.put(identity, tuple);
|
||||||
return tuple;
|
return tuple;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
package ru.dbotthepony.mc.otm.storage;
|
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
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj instanceof StorageObjectTuple tuple)
|
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.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.world.level.BlockGetter
|
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.EntityBlock
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
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.level.block.state.BlockState
|
||||||
import net.minecraft.world.phys.shapes.CollisionContext
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape
|
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.BlockEntityDriveRack
|
||||||
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
||||||
|
|
||||||
@ -16,6 +20,17 @@ class BlockDriveRack : BlockMatteryRotatable(), EntityBlock {
|
|||||||
return BlockEntityDriveRack(blockPos, blockState)
|
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(
|
override fun getShape(
|
||||||
p_60555_: BlockState,
|
p_60555_: BlockState,
|
||||||
p_60556_: BlockGetter,
|
p_60556_: BlockGetter,
|
||||||
|
@ -3,11 +3,16 @@ package ru.dbotthepony.mc.otm.block
|
|||||||
import net.minecraft.core.BlockPos
|
import net.minecraft.core.BlockPos
|
||||||
import net.minecraft.core.Direction
|
import net.minecraft.core.Direction
|
||||||
import net.minecraft.world.level.BlockGetter
|
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.EntityBlock
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity
|
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.level.block.state.BlockState
|
||||||
import net.minecraft.world.phys.shapes.CollisionContext
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape
|
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.block.entity.BlockEntityItemMonitor
|
||||||
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
||||||
|
|
||||||
@ -16,6 +21,17 @@ class BlockItemMonitor : BlockMatteryRotatable(), EntityBlock {
|
|||||||
return BlockEntityItemMonitor(blockPos, blockState)
|
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(
|
override fun getShape(
|
||||||
p_60555_: BlockState,
|
p_60555_: BlockState,
|
||||||
p_60556_: BlockGetter,
|
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.menu.DriveRackMenu
|
||||||
import ru.dbotthepony.mc.otm.set
|
import ru.dbotthepony.mc.otm.set
|
||||||
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
import ru.dbotthepony.mc.otm.graph.storage.StorageNetworkGraph
|
||||||
|
import ru.dbotthepony.mc.otm.storage.PoweredVirtualComponent
|
||||||
import ru.dbotthepony.mc.otm.storage.VirtualComponent
|
import ru.dbotthepony.mc.otm.storage.VirtualComponent
|
||||||
|
|
||||||
class BlockEntityDriveRack(p_155229_: BlockPos, p_155230_: BlockState) :
|
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)
|
super.setChanged(slot, new_state, old_state)
|
||||||
|
|
||||||
old_state.getCapability(MatteryCapability.DRIVE).ifPresent {
|
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 {
|
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))
|
energy = MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, Fraction(80000))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
batteryChargeLoop()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getDefaultDisplayName(): Component {
|
override fun getDefaultDisplayName(): Component {
|
||||||
return NAME
|
return NAME
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,11 @@ class BlockEntityItemMonitor(p_155229_: BlockPos, p_155230_: BlockState) :
|
|||||||
val cell = BasicStorageGraphNode()
|
val cell = BasicStorageGraphNode()
|
||||||
|
|
||||||
init {
|
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 {
|
override fun getDefaultDisplayName(): Component {
|
||||||
|
@ -20,15 +20,11 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
|||||||
constructor(capacity: Fraction, uuid: UUID) : super(capacity, uuid)
|
constructor(capacity: Fraction, uuid: UUID) : super(capacity, uuid)
|
||||||
constructor(capacity: Fraction) : super(capacity)
|
constructor(capacity: Fraction) : super(capacity)
|
||||||
|
|
||||||
override fun identity(): StorageObjectTuple<ItemStackWrapper> {
|
private val identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java)
|
||||||
if (identity == null)
|
override fun identity() = identity
|
||||||
identity = StorageObjectRegistry.getOrError(ItemStackWrapper::class.java)
|
|
||||||
|
|
||||||
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? {
|
override fun serializeStack(item: IStorageTuple<ItemStackWrapper>): CompoundTag? {
|
||||||
@ -90,8 +86,6 @@ class ItemMatteryDrive : AbstractMatteryDrive<ItemStackWrapper>, IItemMatteryDri
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private var identity: StorageObjectTuple<ItemStackWrapper>? = null
|
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
val DUMMY = ItemMatteryDrive(Fraction(0), UUID(0L, 0L), 0)
|
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, 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)
|
@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 {
|
fun compactAndCanonize(): Fraction {
|
||||||
if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE)
|
if (value == BigInteger.ZERO || value == BigInteger.ONE || divisor == BigInteger.ONE)
|
||||||
return this
|
return this
|
||||||
@ -285,6 +303,11 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
|||||||
return value == BigInteger.ZERO
|
return value == BigInteger.ZERO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isOne(): Boolean {
|
||||||
|
if (isNaN()) return false
|
||||||
|
return value != BigInteger.ZERO && value == divisor
|
||||||
|
}
|
||||||
|
|
||||||
fun compact(): Fraction {
|
fun compact(): Fraction {
|
||||||
if (isNaN()) return this
|
if (isNaN()) return this
|
||||||
|
|
||||||
@ -455,6 +478,7 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
|||||||
operator fun times(other: Fraction): Fraction {
|
operator fun times(other: Fraction): Fraction {
|
||||||
if (isNaN()) return this
|
if (isNaN()) return this
|
||||||
if (other.isNaN()) return other
|
if (other.isNaN()) return other
|
||||||
|
if (other.isOne()) return this
|
||||||
|
|
||||||
if (compact)
|
if (compact)
|
||||||
return timesCompact(other)
|
return timesCompact(other)
|
||||||
@ -479,6 +503,7 @@ data class Fraction @JvmOverloads constructor(@JvmField val value: BigInteger, @
|
|||||||
operator fun div(other: Fraction): Fraction {
|
operator fun div(other: Fraction): Fraction {
|
||||||
if (isNaN()) return this
|
if (isNaN()) return this
|
||||||
if (other.isNaN()) return other
|
if (other.isNaN()) return other
|
||||||
|
if (other.isOne()) return this
|
||||||
|
|
||||||
if (compact)
|
if (compact)
|
||||||
return divCompact(other)
|
return divCompact(other)
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
package ru.dbotthepony.mc.otm.menu
|
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 ru.dbotthepony.mc.otm.Registry
|
import ru.dbotthepony.mc.otm.Registry
|
||||||
import ru.dbotthepony.mc.otm.block.entity.BlockEntityItemMonitor
|
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.INetworkedItemViewSupplier
|
||||||
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView
|
||||||
import ru.dbotthepony.mc.otm.storage.ItemStackWrapper
|
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(
|
class ItemMonitorMenu @JvmOverloads constructor(
|
||||||
p_38852_: Int,
|
p_38852_: Int,
|
||||||
@ -14,6 +19,8 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
) : PoweredMatteryMenu(Registry.Menus.ITEM_MONITOR, p_38852_, inventory, tile), INetworkedItemViewSupplier {
|
) : PoweredMatteryMenu(Registry.Menus.ITEM_MONITOR, p_38852_, inventory, tile), INetworkedItemViewSupplier {
|
||||||
@JvmField
|
@JvmField
|
||||||
val view = NetworkedItemView(inventory.player, this, tile == null)
|
val view = NetworkedItemView(inventory.player, this, tile == null)
|
||||||
|
private val subscribed: VirtualComponent<ItemStackWrapper>?
|
||||||
|
private val local: PoweredVirtualComponent<ItemStackWrapper>?
|
||||||
|
|
||||||
override fun getNetworkedItemView(): NetworkedItemView {
|
override fun getNetworkedItemView(): NetworkedItemView {
|
||||||
return view
|
return view
|
||||||
@ -21,13 +28,24 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (tile != null) {
|
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()
|
addBatterySlot()
|
||||||
addInventorySlots()
|
addInventorySlots()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun removed(p_38940_: Player) {
|
||||||
|
super.removed(p_38940_)
|
||||||
|
view.removed()
|
||||||
|
subscribed?.removeListenerAuto(local!!)
|
||||||
|
}
|
||||||
|
|
||||||
override fun broadcastChanges() {
|
override fun broadcastChanges() {
|
||||||
super.broadcastChanges()
|
super.broadcastChanges()
|
||||||
view.network()
|
view.network()
|
||||||
@ -40,4 +58,4 @@ class ItemMonitorMenu @JvmOverloads constructor(
|
|||||||
override fun getWorkingSlotEnd(): Int {
|
override fun getWorkingSlotEnd(): Int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ interface IStorageView<T : IStorageStack> : IStorageTrigger<T> {
|
|||||||
* @param simulate whenever to simulate the action or not
|
* @param simulate whenever to simulate the action or not
|
||||||
* @return amount extracted
|
* @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
|
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
|
* 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
|
* 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