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