Storage system initial commit

This commit is contained in:
DBotThePony 2021-09-08 15:05:55 +07:00
parent 7ff41bbda2
commit 56a45b9ad3
Signed by: DBot
GPG Key ID: DCC23B5715498507
46 changed files with 2464 additions and 740 deletions

View File

@ -1,15 +1,22 @@
package ru.dbotthepony.mc.otm;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
import net.minecraftforge.fmlserverevents.FMLServerStoppingEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityBlackHole;
@ -25,7 +32,12 @@ import ru.dbotthepony.mc.otm.matter.MatterGrid;
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import java.util.UUID;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
// The value here should match an entry in the META-INF/mods.toml file
@ -39,6 +51,67 @@ public class OverdriveThatMatters {
public static CreativeModeTab CREATIVE_TAB;
private static final WeakHashMap<Level, Set<Supplier<Boolean>>> tick_until = new WeakHashMap<>();
private static final WeakHashMap<Level, ArrayList<Consumer<Level>>> tick_once = new WeakHashMap<>();
@SubscribeEvent
public void onServerStarting(FMLServerStartedEvent event) {
tick_until.clear();
}
@SubscribeEvent
public void onServerStopping(FMLServerStoppingEvent event) {
tick_until.clear();
}
@SubscribeEvent
public void onPreTick(TickEvent.WorldTickEvent event) {
if (event.phase != TickEvent.Phase.START || event.side != LogicalSide.SERVER)
return;
var set = tick_until.get(event.world);
if (set != null) {
ArrayList<Supplier<Boolean>> invalid = new ArrayList<>();
for (var ticker : set) {
if (ticker.get()) {
invalid.add(ticker);
}
}
if (invalid.size() != 0) {
for (Supplier<Boolean> cell : invalid) {
set.remove(cell);
}
}
if (set.size() == 0) {
tick_until.remove(event.world);
}
}
var list = tick_once.get(event.world);
if (list != null) {
ArrayList<Supplier<Boolean>> invalid = new ArrayList<>();
for (var ticker : list) {
ticker.accept(event.world);
}
tick_once.remove(event.world);
}
}
public static void tickUntil(Level level, Supplier<Boolean> ticker) {
tick_until.computeIfAbsent(level, (k) -> new HashSet<>()).add(ticker);
}
public static void tickOnce(Level level, Consumer<Level> ticker) {
tick_once.computeIfAbsent(level, (k) -> new ArrayList<>()).add(ticker);
}
public OverdriveThatMatters() {
// Register the setup method for modloading
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);

View File

@ -107,6 +107,8 @@ public class Registry {
public static final ResourceLocation MATTER_REPLICATOR = new ResourceLocation(OverdriveThatMatters.MOD_ID, "matter_replicator");
public static final ResourceLocation MATTER_BOTTLER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "matter_bottler");
public static final ResourceLocation DRIVE_VIEWER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "drive_viewer");
public static final ResourceLocation DRIVE_RACK = new ResourceLocation(OverdriveThatMatters.MOD_ID, "drive_rack");
public static final ResourceLocation ITEM_MONITOR = new ResourceLocation(OverdriveThatMatters.MOD_ID, "item_monitor");
public static final ResourceLocation BLACK_HOLE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "black_hole");
public static final ResourceLocation CARGO_CRATE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "cargo_crate");
@ -238,6 +240,8 @@ public class Registry {
public static final BlockMatterBottler MATTER_BOTTLER = new BlockMatterBottler();
public static final BlockDriveViewer DRIVE_VIEWER = new BlockDriveViewer();
public static final BlockCargoCrate CARGO_CRATE = new BlockCargoCrate();
public static final BlockDriveRack DRIVE_RACK = new BlockDriveRack();
public static final BlockItemMonitor ITEM_MONITOR = new BlockItemMonitor();
public static final BlockBlackHole BLACK_HOLE = new BlockBlackHole();
@ -313,6 +317,8 @@ public class Registry {
DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER);
BLACK_HOLE.setRegistryName(Names.BLACK_HOLE);
CARGO_CRATE.setRegistryName(Names.CARGO_CRATE);
DRIVE_RACK.setRegistryName(Names.DRIVE_RACK);
ITEM_MONITOR.setRegistryName(Names.ITEM_MONITOR);
TRITANIUM_BLOCK.setRegistryName(Names.TRITANIUM_BLOCK);
TRITANIUM_STRIPED_BLOCK.setRegistryName(Names.TRITANIUM_STRIPED_BLOCK);
@ -340,6 +346,8 @@ public class Registry {
event.getRegistry().register(TRITANIUM_ORE);
event.getRegistry().register(DEEPSLATE_TRITANIUM_ORE);
event.getRegistry().register(TRITANIUM_RAW_BLOCK);
event.getRegistry().register(DRIVE_RACK);
event.getRegistry().register(ITEM_MONITOR);
for (var crate : CRATES) {
event.getRegistry().register(crate);
@ -365,6 +373,8 @@ public class Registry {
public static final Item TRITANIUM_ORE = new BlockItem(Blocks.TRITANIUM_ORE, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item DEEPSLATE_TRITANIUM_ORE = new BlockItem(Blocks.DEEPSLATE_TRITANIUM_ORE, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item TRITANIUM_RAW_BLOCK = new BlockItem(Blocks.TRITANIUM_RAW_BLOCK, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item DRIVE_RACK = new BlockItem(Blocks.DRIVE_RACK, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item ITEM_MONITOR = new BlockItem(Blocks.ITEM_MONITOR, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item TRITANIUM_ORE_CLUMP = new Item(new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
public static final Item TRITANIUM_INGOT = new Item(new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB));
@ -436,6 +446,8 @@ public class Registry {
DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER);
BLACK_HOLE.setRegistryName(Names.BLACK_HOLE);
CARGO_CRATE.setRegistryName(Names.CARGO_CRATE);
DRIVE_RACK.setRegistryName(Names.DRIVE_RACK);
ITEM_MONITOR.setRegistryName(Names.ITEM_MONITOR);
TRITANIUM_ORE.setRegistryName(Names.TRITANIUM_ORE);
DEEPSLATE_TRITANIUM_ORE.setRegistryName(Names.DEEPSLATE_TRITANIUM_ORE);
@ -500,6 +512,8 @@ public class Registry {
event.getRegistry().register(DRIVE_VIEWER);
event.getRegistry().register(BLACK_HOLE);
event.getRegistry().register(CARGO_CRATE);
event.getRegistry().register(DRIVE_RACK);
event.getRegistry().register(ITEM_MONITOR);
event.getRegistry().register(TRITANIUM_ORE);
event.getRegistry().register(DEEPSLATE_TRITANIUM_ORE);
@ -571,6 +585,8 @@ public class Registry {
public static final BlockEntityType<BlockEntityDriveViewer> DRIVE_VIEWER = BlockEntityType.Builder.of(BlockEntityDriveViewer::new, Blocks.DRIVE_VIEWER).build(null);
public static final BlockEntityType<BlockEntityBlackHole> BLACK_HOLE = BlockEntityType.Builder.of(BlockEntityBlackHole::new, Blocks.BLACK_HOLE).build(null);
public static final BlockEntityType<BlockEntityCargoCrate> CARGO_CRATE = BlockEntityType.Builder.of(BlockEntityCargoCrate::new, Blocks.CARGO_CRATE).build(null);
public static final BlockEntityType<BlockEntityDriveRack> DRIVE_RACK = BlockEntityType.Builder.of(BlockEntityDriveRack::new, Blocks.DRIVE_RACK).build(null);
public static final BlockEntityType<BlockEntityItemMonitor> ITEM_MONITOR = BlockEntityType.Builder.of(BlockEntityItemMonitor::new, Blocks.ITEM_MONITOR).build(null);
static {
ANDROID_STATION.setRegistryName(Names.ANDROID_STATION);
@ -586,6 +602,8 @@ public class Registry {
DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER);
BLACK_HOLE.setRegistryName(Names.BLACK_HOLE);
CARGO_CRATE.setRegistryName(Names.CARGO_CRATE);
DRIVE_RACK.setRegistryName(Names.DRIVE_RACK);
ITEM_MONITOR.setRegistryName(Names.ITEM_MONITOR);
}
@SubscribeEvent
@ -603,6 +621,8 @@ public class Registry {
event.getRegistry().register(DRIVE_VIEWER);
event.getRegistry().register(BLACK_HOLE);
event.getRegistry().register(CARGO_CRATE);
event.getRegistry().register(DRIVE_RACK);
event.getRegistry().register(ITEM_MONITOR);
// OverdriveThatMatters.LOGGER.info("Registered block entities");
}
@ -846,6 +866,8 @@ public class Registry {
public static final MenuType<MatterBottlerMenu> MATTER_BOTTLER = new MenuType<>(MatterBottlerMenu::new);
public static final MenuType<DriveViewerMenu> DRIVE_VIEWER = new MenuType<>(DriveViewerMenu::new);
public static final MenuType<CargoCrateMenu> CARGO_CRATE = new MenuType<>(CargoCrateMenu::new);
public static final MenuType<DriveRackMenu> DRIVE_RACK = new MenuType<>(DriveRackMenu::new);
public static final MenuType<ItemMonitorMenu> ITEM_MONITOR = new MenuType<>(ItemMonitorMenu::new);
static {
ANDROID_STATION.setRegistryName(Names.ANDROID_STATION);
@ -859,6 +881,8 @@ public class Registry {
MATTER_BOTTLER.setRegistryName(Names.MATTER_BOTTLER);
DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER);
CARGO_CRATE.setRegistryName(Names.CARGO_CRATE);
DRIVE_RACK.setRegistryName(Names.DRIVE_RACK);
ITEM_MONITOR.setRegistryName(Names.ITEM_MONITOR);
}
@SubscribeEvent
@ -874,6 +898,8 @@ public class Registry {
event.getRegistry().register(MATTER_BOTTLER);
event.getRegistry().register(DRIVE_VIEWER);
event.getRegistry().register(CARGO_CRATE);
event.getRegistry().register(DRIVE_RACK);
event.getRegistry().register(ITEM_MONITOR);
// OverdriveThatMatters.LOGGER.info("Registered menus");
}
@ -891,6 +917,8 @@ public class Registry {
MenuScreens.register(MATTER_BOTTLER, MatterBottlerScreen::new);
MenuScreens.register(DRIVE_VIEWER, DriveViewerScreen::new);
MenuScreens.register(CARGO_CRATE, CargoCrateScreen::new);
MenuScreens.register(DRIVE_RACK, DriveRackScreen::new);
MenuScreens.register(ITEM_MONITOR, ItemMonitorScreen::new);
// OverdriveThatMatters.LOGGER.info("Registered screens");
}

View File

@ -0,0 +1,17 @@
package ru.dbotthepony.mc.otm.block;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveRack;
import javax.annotation.Nullable;
public class BlockDriveRack extends BlockMatteryRotatable implements EntityBlock {
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new BlockEntityDriveRack(blockPos, blockState);
}
}

View File

@ -0,0 +1,17 @@
package ru.dbotthepony.mc.otm.block;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityItemMonitor;
import javax.annotation.Nullable;
public class BlockItemMonitor extends BlockMatteryRotatable implements EntityBlock {
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new BlockEntityItemMonitor(blockPos, blockState);
}
}

View File

@ -0,0 +1,117 @@
package ru.dbotthepony.mc.otm.block.entity;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.AbstractStorageGridCell;
import ru.dbotthepony.mc.otm.capability.IStorageGridCell;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.MatteryMachineEnergyStorage;
import ru.dbotthepony.mc.otm.container.MatteryContainer;
import ru.dbotthepony.mc.otm.menu.DriveRackMenu;
import ru.dbotthepony.mc.otm.storage.IStorageIdentity;
import ru.dbotthepony.mc.otm.storage.ItemStackObject;
import ru.dbotthepony.mc.otm.storage.StorageItemView;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.List;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class BlockEntityDriveRack extends BlockEntityMatteryPowered {
public BlockEntityDriveRack(BlockPos p_155229_, BlockState p_155230_) {
super(Registry.BlockEntities.DRIVE_RACK, p_155229_, p_155230_);
energy = new MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, new BigDecimal(80_000));
}
public final MatteryContainer drives = new MatteryContainer(this::setChanged, 4) {
@Override
public void setChanged(int slot, ItemStack new_state, ItemStack old_state) {
super.setChanged(slot, new_state, old_state);
/*old_state.getCapability(MatteryCapability.DRIVE).ifPresent((drive) -> {
drive.removeListenerAuto(cell.computeIfAbsent(ItemStackObject.class, StorageItemView::new));
});
new_state.getCapability(MatteryCapability.DRIVE).ifPresent((drive) -> {
drive.addListenerAuto(cell.computeIfAbsent(ItemStackObject.class, StorageItemView::new));
});*/
old_state.getCapability(MatteryCapability.DRIVE).ifPresent(cell::removeStorageComponent);
new_state.getCapability(MatteryCapability.DRIVE).ifPresent(cell::addStorageComponent);
}
};
public final AbstractStorageGridCell cell = new AbstractStorageGridCell();
@Override
public void load(CompoundTag nbt) {
super.load(nbt);
drives.deserializeNBT(nbt.getCompound("drives"));
}
@Override
public CompoundTag save(CompoundTag nbt) {
nbt.put("drives", drives.serializeNBT());
return super.save(nbt);
}
@Override
public void setLevel(Level p_155231_) {
super.setLevel(p_155231_);
cell.scheduleDiscoverNeighbours(p_155231_, getBlockPos());
}
private static final TranslatableComponent NAME = new TranslatableComponent("block.overdrive_that_matters.drive_rack");
@Override
protected Component getDefaultDisplayName() {
return NAME;
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int containerID, Inventory inventory, Player ply) {
return new DriveRackMenu(containerID, inventory, this);
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (cap == MatteryCapability.STORAGE_CELL) {
return cell.get().cast();
}
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
cell.invalidate();
}
@Override
public void reviveCaps() {
super.reviveCaps();
cell.revive();
}
}

View File

@ -0,0 +1,74 @@
package ru.dbotthepony.mc.otm.block.entity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.capability.AbstractStorageGridCell;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.MatteryMachineEnergyStorage;
import ru.dbotthepony.mc.otm.menu.ItemMonitorMenu;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigDecimal;
public class BlockEntityItemMonitor extends BlockEntityMatteryPowered {
public BlockEntityItemMonitor(BlockPos p_155229_, BlockState p_155230_) {
super(Registry.BlockEntities.ITEM_MONITOR, p_155229_, p_155230_);
energy = new MatteryMachineEnergyStorage(this, MatteryMachineEnergyStorage.MachineType.WORKER, new BigDecimal(80_000));
}
public final AbstractStorageGridCell cell = new AbstractStorageGridCell();
private static final TranslatableComponent NAME = new TranslatableComponent("block.overdrive_that_matters.item_storage_viewer");
@Override
protected Component getDefaultDisplayName() {
return NAME;
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int containerID, Inventory inventory, Player ply) {
return new ItemMonitorMenu(containerID, inventory, this);
}
@Override
public void setLevel(Level p_155231_) {
super.setLevel(p_155231_);
cell.scheduleDiscoverNeighbours(p_155231_, getBlockPos());
}
@Nonnull
@Override
public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
if (cap == MatteryCapability.STORAGE_CELL) {
return cell.get().cast();
}
return super.getCapability(cap, side);
}
@Override
public void invalidateCaps() {
super.invalidateCaps();
cell.invalidate();
}
@Override
public void reviveCaps() {
super.reviveCaps();
cell.revive();
}
}

View File

@ -72,7 +72,7 @@ public class BlockEntityMatterReplicator extends BlockEntityMatteryWorker implem
@Nonnull
@Override
protected MachineJobStatus onJobFinish(MachineJob job) {
if (!regular_slots.addItem(job.stack()).isEmpty()) {
if (!regular_slots.fullyAddItem(job.stack())) {
return new MachineJobStatus(false, 20);
}

View File

@ -0,0 +1,101 @@
package ru.dbotthepony.mc.otm.capability;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.storage.IStorageIdentity;
import ru.dbotthepony.mc.otm.storage.IStorageObject;
import ru.dbotthepony.mc.otm.storage.StorageGrid;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class AbstractStorageGridCell implements IStorageGridCell {
private LazyOptional<IStorageGridCell> resolver = LazyOptional.of(() -> this);
private boolean valid = true;
protected final ArrayList<IStorageIdentity<?>> components = new ArrayList<>();
@Override
public List<? extends IStorageIdentity<?>> getComponents() {
return Collections.unmodifiableList(components);
}
public <T extends IStorageObject, U extends IStorageIdentity<T>> U computeIfAbsent(Class<T> identity, Supplier<U> provider) {
for (var component : components) {
if (component.storageIdentity() == identity) {
return (U) component;
}
}
var factory = provider.get();
addStorageComponent(factory);
return factory;
}
public void addStorageComponent(IStorageIdentity<?> component) {
for (var component1 : components) {
if (component == component1 || component1.storageIdentity() == component.storageIdentity()) {
return;
}
}
detachStorage();
components.add(component);
attachStorage();
}
public void removeStorageComponent(IStorageIdentity<?> component) {
for (var component1 : components) {
if (component == component1 || component1.storageIdentity() == component.storageIdentity()) {
detachStorage();
components.remove(component1);
attachStorage();
}
}
}
public void invalidate() {
if (!valid)
return;
valid = false;
resolver.invalidate();
}
public void revive() {
if (valid)
return;
valid = true;
resolver = LazyOptional.of(() -> this);
}
public LazyOptional<IStorageGridCell> get() {
return valid ? resolver : LazyOptional.empty();
}
protected StorageGrid storage_grid;
@Nullable
@Override
public StorageGrid getStorageGrid() {
return storage_grid;
}
@Override
public void setStorageGrid(StorageGrid grid) {
OverdriveThatMatters.LOGGER.info("Set grid {} {}", this, grid);
storage_grid = grid;
}
@Override
public boolean isValidStorageCell() {
return valid;
}
}

View File

@ -0,0 +1,126 @@
package ru.dbotthepony.mc.otm.capability;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.SectionPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.storage.IStorageIdentity;
import ru.dbotthepony.mc.otm.storage.StorageGrid;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageGridCell {
/**
* 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();
*
* Failing to do it in this order will cause IllegalStateException
*
* @return a list of all providers, inheriting IStorageIdentity, or an empty list
*/
List<? extends IStorageIdentity<?>> getComponents();
default List<? extends IStorageIdentity<?>> getComponentsForView() {
return getComponents();
}
default List<? extends IStorageIdentity<?>> getComponentsForInterfaces() {
return getComponents();
}
default List<? extends IStorageIdentity<?>> getComponentsForExporters() {
return getComponents();
}
default void detachStorage() {
var grid = getStorageGrid();
if (grid != null) {
grid.detach(this);
}
}
default void attachStorage() {
var grid = getStorageGrid();
if (grid != null) {
grid.attach(this);
}
}
@Nullable
StorageGrid getStorageGrid();
void setStorageGrid(@Nullable StorageGrid grid);
boolean isValidStorageCell();
default void scheduleDiscoverNeighbours(Level level, BlockPos pos) {
if (level instanceof ServerLevel)
StorageGrid.scheduleDiscoverNeighbours(this, level, pos);
}
default boolean connectOrCreateStorageGrid(Level level, BlockPos pos) {
return connectOrCreateStorageGrid(level, pos, false);
}
default void onNeighbourStorageCell(Level level, BlockPos pos, Direction direction, IStorageGridCell cell) {
}
default boolean connectOrCreateStorageGrid(Level level, BlockPos pos, boolean force) {
OverdriveThatMatters.LOGGER.info("Discover {}", this);
if (getStorageGrid() != null && !force)
return true;
boolean full_discovery = true;
for (Direction direction : Direction.values()) {
BlockPos offset = pos.offset(direction.getNormal());
// level.getBlockEntity can drink big cup of deadlocks
LevelChunk get_chunk = level.getChunkSource().getChunkNow(SectionPos.blockToSectionCoord(offset.getX()), SectionPos.blockToSectionCoord(offset.getZ()));
if (get_chunk == null) {
full_discovery = false;
continue;
}
BlockEntity get_entity = get_chunk.getBlockEntity(offset);
if (get_entity != null && get_entity.getCapability(MatteryCapability.STORAGE_CELL, direction.getOpposite()).isPresent()) {
var cell = get_entity.getCapability(MatteryCapability.STORAGE_CELL, direction.getOpposite()).resolve().get();
var grid = cell.getStorageGrid();
if (grid != null && grid != getStorageGrid()) {
if (getStorageGrid() == null) {
grid.add(this);
} else {
grid.merge(getStorageGrid());
}
}
if (cell.isValidStorageCell())
onNeighbourStorageCell(level, pos, direction, cell);
}
}
if (getStorageGrid() == null) {
new StorageGrid().add(this);
}
return full_discovery;
}
}

View File

@ -32,6 +32,9 @@ public class MatteryCapability {
@CapabilityInject(IMatteryDrive.class)
public static Capability<IMatteryDrive> DRIVE = null;
@CapabilityInject(IStorageGridCell.class)
public static Capability<IStorageGridCell> STORAGE_CELL = null;
public static void register() {
CapabilityManager.INSTANCE.register(IAndroidCapability.class);
CapabilityManager.INSTANCE.register(IMatteryEnergyStorage.class);
@ -40,6 +43,7 @@ public class MatteryCapability {
CapabilityManager.INSTANCE.register(IMatterGridCell.class);
CapabilityManager.INSTANCE.register(IMatterTaskProvider.class);
CapabilityManager.INSTANCE.register(IMatteryDrive.class);
CapabilityManager.INSTANCE.register(IStorageGridCell.class);
}
public static final MathContext ROUND_RULES = new MathContext(32, RoundingMode.HALF_DOWN);

View File

@ -6,6 +6,7 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.util.INBTSerializable;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.storage.*;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -16,7 +17,7 @@ import java.util.function.Predicate;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
public interface IMatteryDrive {
public interface IMatteryDrive extends IStorageComponent<ItemStackObject> {
record StoredStack(ItemStack stack, UUID id) {};
List<StoredStack> getItems();
@ -39,9 +40,6 @@ public interface IMatteryDrive {
ItemStack getItem(UUID id);
void addListener(IItemViewListener listener);
void removeListener(IItemViewListener listener);
int getStoredCount();
int getCapacity();

View File

@ -1,5 +1,6 @@
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;
@ -9,11 +10,19 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.registries.RegistryManager;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.storage.IStorageListener;
import ru.dbotthepony.mc.otm.storage.IStorageTuple;
import ru.dbotthepony.mc.otm.storage.ItemStackObject;
import ru.dbotthepony.mc.otm.storage.StorageTuple;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.*;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class MatteryDrive implements IMatteryDrive {
protected final HashMap<Item, List<StoredStack>> items = new HashMap<>();
protected final HashMap<UUID, StoredStack> items_by_id = new HashMap<>();
@ -115,33 +124,6 @@ public class MatteryDrive implements IMatteryDrive {
return build_list;
}
protected final ArrayList<IItemViewListener> listeners = new ArrayList<>();
@Override
public void addListener(IItemViewListener listener) {
if (listeners.contains(listener))
return;
listeners.add(listener);
for (var list : this.items.values()) {
for (var state : list) {
listener.addViewItem(state.stack(), state.id());
}
}
}
@Override
public void removeListener(IItemViewListener listener) {
if (listeners.remove(listener)) {
for (var list : this.items.values()) {
for (var state : list) {
listener.removeViewItem(state.id());
}
}
}
}
@Nonnull
@Override
public ItemStack getItem(UUID id) {
@ -165,8 +147,8 @@ public class MatteryDrive implements IMatteryDrive {
state.stack().grow(max_insert);
stored += max_insert;
for (var listener : listeners) {
listener.changeViewItem(state.id(), state.stack().getCount());
for (var listener : listeners2) {
listener.changeObject(state.id(), new BigDecimal(state.stack().getCount()));
}
markDirty();
@ -192,8 +174,8 @@ public class MatteryDrive implements IMatteryDrive {
listing.add(state);
items_by_id.put(state.id(), state);
for (var listener : listeners) {
listener.addViewItem(state.stack(), state.id());
for (var listener : listeners2) {
listener.addObject(new ItemStackObject(state.stack()), state.id(), this);
}
markDirty();
@ -204,6 +186,12 @@ public class MatteryDrive implements IMatteryDrive {
return copy_item;
}
@Override
public ItemStackObject insertObject(ItemStackObject obj, boolean simulate) {
var get = insertItem(obj.stack(), simulate);
return get.isEmpty() ? ItemStackObject.EMPTY : new ItemStackObject(get);
}
@Nonnull
@Override
public ItemStack extractItem(UUID id, int amount, boolean obey_stack_size, boolean simulate) {
@ -229,8 +217,8 @@ public class MatteryDrive implements IMatteryDrive {
listing.remove(get);
different_stacks--;
for (var listener : listeners) {
listener.removeViewItem(get.id());
for (var listener : listeners2) {
listener.removeObject(get.id());
}
if (listing.size() == 0) {
@ -242,8 +230,8 @@ public class MatteryDrive implements IMatteryDrive {
get.stack().shrink(extract);
if (get.stack().getCount() != 0) {
for (var listener : listeners) {
listener.changeViewItem(get.id(), get.stack().getCount());
for (var listener : listeners2) {
listener.changeObject(get.id(), new BigDecimal(get.stack().getCount()));
}
}
@ -334,4 +322,51 @@ public class MatteryDrive implements IMatteryDrive {
}
}
}
@Override
public Class<ItemStackObject> storageIdentity() {
return ItemStackObject.class;
}
@Override
public ItemStackObject getStoredObject(UUID id) {
var get = getItem(id);
return get.isEmpty() ? ItemStackObject.EMPTY : new ItemStackObject(get);
}
@Override
public ItemStackObject extractObject(UUID id, BigDecimal amount, boolean simulate) {
var get = extractItem(id, amount.intValue(), simulate);
return get.isEmpty() ? ItemStackObject.EMPTY : new ItemStackObject(get);
}
@Override
public List<IStorageTuple<ItemStackObject>> getStorageObjects() {
int amount = 0;
for (var listing : items.values())
amount += listing.size();
var list = new ArrayList<IStorageTuple<ItemStackObject>>(amount);
for (var listing : items.values()) {
for (var stack : listing) {
list.add(new StorageTuple<>(stack.id(), new ItemStackObject(stack.stack())));
}
}
return list;
}
protected final HashSet<IStorageListener<ItemStackObject>> listeners2 = new HashSet<>();
@Override
public boolean addListener(IStorageListener<ItemStackObject> listener) {
return listeners2.add(listener);
}
@Override
public boolean removeListener(IStorageListener<ItemStackObject> listener) {
return listeners2.remove(listener);
}
}

View File

@ -0,0 +1,33 @@
package ru.dbotthepony.mc.otm.client.screen;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import ru.dbotthepony.mc.otm.client.screen.panels.FlexGridPanel;
import ru.dbotthepony.mc.otm.client.screen.panels.SlotPanel;
import ru.dbotthepony.mc.otm.menu.DriveRackMenu;
import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
import ru.dbotthepony.mc.otm.menu.widget.GaugeWidget;
import java.util.List;
public class DriveRackScreen extends MatteryScreen<DriveRackMenu> implements MatteryScreen.IMatteryScreenGaugeGetter, MatteryScreen.IMatteryScreenBatteryGetter, MatteryScreen.IMatteryScreenGridBased {
public DriveRackScreen(DriveRackMenu menu, Inventory inventory, Component title) {
super(menu, inventory, title);
}
@Override
public List<GaugeWidget> getGauges() {
return List.of(menu.battery_widget);
}
@Override
public List<MatterySlot> getBatterySlots() {
return List.of(menu.battery_slot);
}
@Override
public void createGridPanels(FlexGridPanel grid) {
for (var slot : menu.drives)
new SlotPanel<>(this, grid, slot);
}
}

View File

@ -9,6 +9,7 @@ import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.item.ItemStack;
import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.menu.data.InteractPacket;
import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
import ru.dbotthepony.mc.otm.menu.widget.GaugeWidget;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
@ -116,12 +117,8 @@ public class DriveViewerScreen extends MatteryScreen<DriveViewerMenu> implements
@Override
protected boolean mouseClickedInner(double mouse_x, double mouse_y, int mouse_click_type) {
int findex = index + scroll_bar.getScroll(GRID_WIDTH);
var list = menu.view.getItems();
var action = mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT ? ClickAction.PRIMARY : ClickAction.SECONDARY;
menu.view.mouseClick(findex, mouse_click_type);
var type = mouse_click_type == InputConstants.MOUSE_BUTTON_MIDDLE ? ClickType.CLONE : hasShiftDown() ? ClickType.QUICK_MOVE : ClickType.PICKUP;
MatteryNetworking.send(new DriveViewerMenu.InteractPacket(menu.containerId, findex >= list.size() ? -1 : list.get(findex).id(), type, action));
return true;
}
};
@ -163,7 +160,7 @@ public class DriveViewerScreen extends MatteryScreen<DriveViewerMenu> implements
@Override
protected boolean mouseReleasedInner(double mouse_x, double mouse_y, int flag) {
if (clicking)
MatteryNetworking.send(new DriveViewerMenu.FilterSetPacket(menu.containerId, index, menu.getCarried()));
MatteryNetworking.send(null, new DriveViewerMenu.FilterSetPacket(menu.containerId, index, menu.getCarried()));
clicking = false;
@ -197,7 +194,7 @@ public class DriveViewerScreen extends MatteryScreen<DriveViewerMenu> implements
var filter = menu.getFilter();
if (filter != null) {
MatteryNetworking.send(new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.MATCH_NBT, !filter.match_nbt));
MatteryNetworking.send(null, new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.MATCH_NBT, !filter.match_nbt));
disableFor(20);
}
}
@ -225,7 +222,7 @@ public class DriveViewerScreen extends MatteryScreen<DriveViewerMenu> implements
var filter = menu.getFilter();
if (filter != null) {
MatteryNetworking.send(new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.MATCH_TAG, !filter.match_tag));
MatteryNetworking.send(null, new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.MATCH_TAG, !filter.match_tag));
disableFor(20);
}
}
@ -253,7 +250,7 @@ public class DriveViewerScreen extends MatteryScreen<DriveViewerMenu> implements
var filter = menu.getFilter();
if (filter != null) {
MatteryNetworking.send(new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.BLACKLIST, !filter.blacklist));
MatteryNetworking.send(null, new DriveViewerMenu.FilterSwitchPacket(menu.containerId, DriveViewerMenu.FilterSwitch.BLACKLIST, !filter.blacklist));
disableFor(20);
}
}

View File

@ -0,0 +1,97 @@
package ru.dbotthepony.mc.otm.client.screen;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.ClickAction;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.item.ItemStack;
import ru.dbotthepony.mc.otm.client.screen.panels.*;
import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.menu.ItemMonitorMenu;
import ru.dbotthepony.mc.otm.menu.data.InteractPacket;
import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
import ru.dbotthepony.mc.otm.menu.widget.GaugeWidget;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ItemMonitorScreen extends MatteryScreen<ItemMonitorMenu> implements MatteryScreen.IMatteryScreenGaugeGetter, MatteryScreen.IMatteryScreenBatteryGetter {
public static final int FRAME_WIDTH = 210;
public static final int FRAME_HEIGHT = 110;
public static final int GRID_WIDTH = 9;
public static final int GRID_HEIGHT = 5;
public ItemMonitorScreen(ItemMonitorMenu menu, Inventory inventory, Component title) {
super(menu, inventory, title);
}
@Override
public List<GaugeWidget> getGauges() {
return List.of(menu.battery_widget);
}
@Override
public List<MatterySlot> getBatterySlots() {
return List.of(menu.battery_slot);
}
@Nullable
@Override
protected FramePanel makeMainFrame() {
var frame = new FramePanel(this, null, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, getTitle());
autoAttachToFrame(frame);
var grid = new GridPanel(this, frame, 0, 0, 0, 0, GRID_WIDTH, GRID_HEIGHT);
grid.setDock(Dock.FILL);
grid.setDockMargin(2, 2, 2, 2);
var scroll_bar = new ScrollBarPanel(this, frame, 0, 0, 0);
scroll_bar.setDock(Dock.RIGHT);
scroll_bar.setupRowMultiplier(() -> {
return menu.view.getItems().size() / GRID_WIDTH;
});
for (int i = 0; i < GRID_WIDTH * GRID_HEIGHT; i++) {
final int index = i;
new AbstractSlotPanel(this, grid, 0, 0) {
@Nonnull
@Override
protected ItemStack getItemStack() {
int findex = index + scroll_bar.getScroll(menu.view.getItems().size() / GRID_WIDTH);
var list = menu.view.getItems();
if (findex >= list.size()) {
return ItemStack.EMPTY;
}
return list.get(findex).stack();
}
@Override
protected boolean mouseScrolledInner(double mouse_x, double mouse_y, double scroll) {
return scroll_bar.mouseScrolledInner(mouse_x, mouse_y, scroll);
}
@Override
protected boolean mouseClickedInner(double mouse_x, double mouse_y, int mouse_click_type) {
int findex = index + scroll_bar.getScroll(GRID_WIDTH);
menu.view.mouseClick(findex, mouse_click_type);
return true;
}
};
}
return frame;
}
}

View File

@ -1,20 +1,40 @@
package ru.dbotthepony.mc.otm.container;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
public class MatteryContainer extends SimpleContainer {
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class MatteryContainer implements Container, Iterable<ItemStack> {
protected final Runnable watcher;
protected int ignore_change_notifications = 0;
protected final int size;
protected final ItemStack[] slots;
protected final ItemStack[] tracked_slots;
public MatteryContainer(Runnable watcher, int size) {
this.size = size;
slots = new ItemStack[this.size];
tracked_slots = new ItemStack[this.size];
Arrays.fill(slots, ItemStack.EMPTY);
Arrays.fill(tracked_slots, ItemStack.EMPTY);
this.watcher = watcher;
}
public void startIgnore() {
ignore_change_notifications++;
@ -24,11 +44,6 @@ public class MatteryContainer extends SimpleContainer {
ignore_change_notifications--;
}
public MatteryContainer(Runnable watcher, int i) {
super(i);
this.watcher = watcher;
}
public MatteryContainer of(@Nullable CompoundTag tag) {
if (tag == null)
return this;
@ -55,10 +70,13 @@ public class MatteryContainer extends SimpleContainer {
// нам не интересен размер
if (tag.get("items") instanceof ListTag list)
for (int i = 0; i < Math.min(list.size(), getContainerSize()); i++)
if (list.get(i) instanceof CompoundTag get_tag)
if (tag.get("items") instanceof ListTag list) {
for (int i = 0; i < Math.min(list.size(), getContainerSize()); i++) {
if (list.get(i) instanceof CompoundTag get_tag) {
setItem(i, ItemStack.of(get_tag));
}
}
}
ignore_change_notifications--;
setChanged();
@ -83,11 +101,6 @@ public class MatteryContainer extends SimpleContainer {
}
}
public MatteryContainer(Runnable watcher, ItemStack... p_19152_) {
super(p_19152_);
this.watcher = watcher;
}
public ContainerIteratorItemStack stackIterator() {
return new ContainerIteratorItemStack(this);
}
@ -104,17 +117,9 @@ public class MatteryContainer extends SimpleContainer {
capabilityIterator(cap).consume(consumer);
}
@Override
public void setChanged() {
if (ignore_change_notifications > 0)
return;
super.setChanged();
watcher.run();
}
public void setChanged(int slot, ItemStack new_state, ItemStack old_state) {
setChanged();
if (ignore_change_notifications == 0)
watcher.run();
}
public CompoundTag serializeNBT() {
@ -157,16 +162,6 @@ public class MatteryContainer extends SimpleContainer {
return false;
}
@FunctionalInterface
public interface MatteryContainerInsertValidator {
boolean apply(int slot, @Nonnull ItemStack stack);
}
@FunctionalInterface
public interface MatteryContainerExtractValidator {
boolean apply(int slot, int amount, @Nonnull ItemStack stack);
}
protected MatteryContainerHandler handler;
public IItemHandler handler(MatteryContainerInsertValidator insert_validator, MatteryContainerExtractValidator extract_validator) {
@ -194,114 +189,165 @@ public class MatteryContainer extends SimpleContainer {
return getMaxStackSize();
}
static class MatteryContainerHandler implements IItemHandler {
private final MatteryContainer container;
private final MatteryContainerInsertValidator insert_validator;
private final MatteryContainerExtractValidator extract_validator;
public ItemStack addItem(ItemStack stack, int start, int end, boolean simulate) {
if (stack.isEmpty() || start < 0 || end > size || start >= end)
return ItemStack.EMPTY;
MatteryContainerHandler(MatteryContainer container, MatteryContainerInsertValidator insert_validator, MatteryContainerExtractValidator extract_validator) {
this.container = container;
this.insert_validator = insert_validator;
this.extract_validator = extract_validator;
}
var copy = stack.copy();
MatteryContainerHandler(MatteryContainer container) {
this(container, (slot, stack) -> true, (slot, amount, stack) -> true);
}
// двигаем в одинаковые слоты
for (int slot = start; slot < end; slot++) {
if (ItemStack.isSameItemSameTags(slots[slot], copy)) {
var this_stack = slots[slot];
int slot_limit = Math.min(getMaxStackSize(slot), this_stack.getMaxStackSize());
MatteryContainerHandler(MatteryContainer container, MatteryContainerInsertValidator insert_validator) {
this(container, insert_validator, (slot, amount, stack) -> true);
}
if (this_stack.getCount() < slot_limit) {
int new_count = Math.min(this_stack.getCount() + copy.getCount(), slot_limit);
int diff = new_count - this_stack.getCount();
@Override
public int getSlots() {
return container.getContainerSize();
}
@Nonnull
@Override
public ItemStack getStackInSlot(int slot) {
return container.getItem(slot);
}
@Nonnull
@Override
public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {
if (!insert_validator.apply(slot, stack))
return stack;
ItemStack self_stack = container.getItem(slot);
if (self_stack.isEmpty()) {
if (!simulate) {
var copy = stack.copy();
container.setChanged(slot, copy, ItemStack.EMPTY);
container.setItem(slot, copy);
}
return ItemStack.EMPTY;
} else if (self_stack.isStackable() && self_stack.getMaxStackSize() > self_stack.getCount() && ItemStack.isSameItemSameTags(self_stack, stack)) {
int new_count = Math.min(self_stack.getMaxStackSize(), self_stack.getCount() + stack.getCount());
int diff = new_count - self_stack.getCount();
if (diff != 0) {
if (!simulate) {
ItemStack copy_self = self_stack.copy();
self_stack.grow(diff);
container.setChanged(slot, self_stack, copy_self);
var old = this_stack.copy();
this_stack.setCount(new_count);
tracked_slots[slot] = this_stack.copy();
setChanged(slot, this_stack, old);
}
ItemStack copy = stack.copy();
copy.shrink(diff);
if (copy.isEmpty()) {
return copy;
}
}
}
}
// двигаем в пустые слоты
for (int slot = start; slot < end; slot++) {
if (slots[slot].isEmpty()) {
int diff = Math.min(copy.getCount(), Math.min(getMaxStackSize(slot), copy.getMaxStackSize()));
if (!simulate) {
var put_copy = copy.copy();
put_copy.setCount(diff);
tracked_slots[slot] = put_copy.copy();
setChanged(slot, put_copy, ItemStack.EMPTY);
}
copy.shrink(diff);
if (copy.isEmpty()) {
return copy;
}
}
return stack;
}
@Nonnull
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
if (amount == 0)
return ItemStack.EMPTY;
return copy;
}
if (amount < 0)
throw new IllegalArgumentException("Can not extract negative amount of items");
public ItemStack addItem(ItemStack stack, boolean simulate) {
return addItem(stack, 0, size, simulate);
}
ItemStack self_stack = container.getItem(slot);
public ItemStack addItem(ItemStack stack) {
return addItem(stack, 0, size, false);
}
if (self_stack.isEmpty())
return ItemStack.EMPTY;
public boolean fullyAddItem(ItemStack stack) {
if (!addItem(stack, 0, size, true).isEmpty()) {
return false;
}
if (!extract_validator.apply(slot, amount, self_stack))
return ItemStack.EMPTY;
return addItem(stack, 0, size, false).isEmpty();
}
int minimal = Math.min(amount, self_stack.getCount());
ItemStack copy = self_stack.copy();
copy.setCount(minimal);
public boolean fullyAddItem(ItemStack stack, int start, int end) {
if (!addItem(stack, start, end, true).isEmpty()) {
return false;
}
if (!simulate) {
ItemStack copy_self = self_stack.copy();
self_stack.shrink(minimal);
container.setChanged(slot, self_stack, copy_self);
return addItem(stack, start, end, false).isEmpty();
}
@Override
public int getContainerSize() {
return size;
}
@Override
public boolean isEmpty() {
for (var stack : slots)
if (!stack.isEmpty())
return false;
return true;
}
@Override
public ItemStack getItem(int slot) {
return slots[slot];
}
@Override
public ItemStack removeItem(int slot, int amount) {
if (amount <= 0 || slot < 0 || slot >= size || slots[slot].isEmpty())
return ItemStack.EMPTY;
var old = slots[slot].copy();
var split = slots[slot].split(amount);
tracked_slots[slot] = slots[slot].copy();
setChanged(slot, slots[slot].isEmpty() ? ItemStack.EMPTY : slots[slot], old);
return split;
}
@Override
public ItemStack removeItemNoUpdate(int slot) {
var old = slots[slot];
slots[slot] = ItemStack.EMPTY;
tracked_slots[slot] = ItemStack.EMPTY;
return old;
}
@Override
public void setItem(int slot, ItemStack stack) {
if (slots[slot].isEmpty() && stack.isEmpty() || stack == slots[slot])
return;
var old = slots[slot];
slots[slot] = stack;
tracked_slots[slot] = stack.copy();
setChanged(slot, stack, old);
}
@Override
public void setChanged() {
for (int slot = 0; slot < size; slot++) {
if (!ItemStack.isSameItemSameTags(slots[slot], tracked_slots[slot])) {
setChanged(slot, slots[slot], tracked_slots[slot]);
tracked_slots[slot] = slots[slot].copy();
// mojang соси))0)0))0)))))0)
}
return copy;
}
@Override
public int getSlotLimit(int slot) {
return container.getMaxStackSize();
}
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
return insert_validator.apply(slot, stack);
}
MatteryContainerHandler cloneOf(MatteryContainer container) {
return new MatteryContainerHandler(container, insert_validator, extract_validator);
}
}
@Override
public boolean stillValid(Player player) {
return true;
}
@Override
public void clearContent() {
for (int slot = 0; slot < size; slot++) {
if (!slots[slot].isEmpty()) {
setChanged(slot, ItemStack.EMPTY, slots[slot]);
}
}
Arrays.fill(tracked_slots, ItemStack.EMPTY);
}
@Override
public Iterator<ItemStack> iterator() {
return Arrays.stream(slots).iterator();
}
}

View File

@ -0,0 +1,10 @@
package ru.dbotthepony.mc.otm.container;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
@FunctionalInterface
public interface MatteryContainerExtractValidator {
boolean apply(int slot, int amount, @Nonnull ItemStack stack);
}

View File

@ -0,0 +1,117 @@
package ru.dbotthepony.mc.otm.container;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
class MatteryContainerHandler implements IItemHandler {
private final MatteryContainer container;
private final MatteryContainerInsertValidator insert_validator;
private final MatteryContainerExtractValidator extract_validator;
MatteryContainerHandler(MatteryContainer container, MatteryContainerInsertValidator insert_validator, MatteryContainerExtractValidator extract_validator) {
this.container = container;
this.insert_validator = insert_validator;
this.extract_validator = extract_validator;
}
MatteryContainerHandler(MatteryContainer container) {
this(container, (slot, stack) -> true, (slot, amount, stack) -> true);
}
MatteryContainerHandler(MatteryContainer container, MatteryContainerInsertValidator insert_validator) {
this(container, insert_validator, (slot, amount, stack) -> true);
}
@Override
public int getSlots() {
return container.getContainerSize();
}
@Nonnull
@Override
public ItemStack getStackInSlot(int slot) {
return container.getItem(slot);
}
@Nonnull
@Override
public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {
if (!insert_validator.apply(slot, stack))
return stack;
ItemStack self_stack = container.getItem(slot);
if (self_stack.isEmpty()) {
if (!simulate) {
var copy = stack.copy();
container.setChanged(slot, copy, ItemStack.EMPTY);
container.setItem(slot, copy);
}
return ItemStack.EMPTY;
} else if (self_stack.isStackable() && self_stack.getMaxStackSize() > self_stack.getCount() && ItemStack.isSameItemSameTags(self_stack, stack)) {
int new_count = Math.min(self_stack.getMaxStackSize(), self_stack.getCount() + stack.getCount());
int diff = new_count - self_stack.getCount();
if (diff != 0) {
if (!simulate) {
ItemStack copy_self = self_stack.copy();
self_stack.grow(diff);
container.setChanged(slot, self_stack, copy_self);
}
ItemStack copy = stack.copy();
copy.shrink(diff);
return copy;
}
}
return stack;
}
@Nonnull
@Override
public ItemStack extractItem(int slot, int amount, boolean simulate) {
if (amount == 0)
return ItemStack.EMPTY;
if (amount < 0)
throw new IllegalArgumentException("Can not extract negative amount of items");
ItemStack self_stack = container.getItem(slot);
if (self_stack.isEmpty())
return ItemStack.EMPTY;
if (!extract_validator.apply(slot, amount, self_stack))
return ItemStack.EMPTY;
int minimal = Math.min(amount, self_stack.getCount());
ItemStack copy = self_stack.copy();
copy.setCount(minimal);
if (!simulate) {
ItemStack copy_self = self_stack.copy();
self_stack.shrink(minimal);
container.setChanged(slot, self_stack, copy_self);
}
return copy;
}
@Override
public int getSlotLimit(int slot) {
return container.getMaxStackSize();
}
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
return insert_validator.apply(slot, stack);
}
MatteryContainerHandler cloneOf(MatteryContainer container) {
return new MatteryContainerHandler(container, insert_validator, extract_validator);
}
}

View File

@ -0,0 +1,10 @@
package ru.dbotthepony.mc.otm.container;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
@FunctionalInterface
public interface MatteryContainerInsertValidator {
boolean apply(int slot, @Nonnull ItemStack stack);
}

View File

@ -4,18 +4,13 @@ import com.google.common.collect.ImmutableList;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fmlserverevents.FMLServerStartedEvent;
import net.minecraftforge.fmlserverevents.FMLServerStoppingEvent;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.capability.*;
import javax.annotation.Nullable;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class MatterGrid implements IMatterGridListener {
public static final Set<MatterGrid> NETWORKS = new HashSet<>();
@ -23,48 +18,8 @@ public class MatterGrid implements IMatterGridListener {
private final Set<IMatterGridCell> cells = new HashSet<>();
private final Set<IMatterGridListener> listeners = new HashSet<>();
private static final WeakHashMap<Level, Set<Supplier<Boolean>>> discovering_neighbours = new WeakHashMap<>();
public static void scheduleDiscoverNeighbours(IMatterGridCell cell, BlockPos pos, Level level) {
discovering_neighbours.computeIfAbsent(level, (key) -> new HashSet<>()).add(() -> !cell.isValidMatterCell() || cell.connectOrCreateMatterGrid(pos, level, true));
}
@SubscribeEvent
public void onServerStarting(FMLServerStartedEvent event) {
discovering_neighbours.clear();
}
@SubscribeEvent
public void onServerStopping(FMLServerStoppingEvent event) {
discovering_neighbours.clear();
}
@SubscribeEvent
public static void discoverNeighbours(TickEvent.WorldTickEvent event) {
if (event.phase != TickEvent.Phase.START)
return;
var set = discovering_neighbours.get(event.world);
if (set != null) {
ArrayList<Supplier<Boolean>> invalid = new ArrayList<>();
for (Supplier<Boolean> cell : set) {
if (cell.get()) {
invalid.add(cell);
}
}
if (invalid.size() != 0) {
for (Supplier<Boolean> cell : invalid) {
set.remove(cell);
}
}
if (set.size() == 0) {
discovering_neighbours.remove(event.world);
}
}
OverdriveThatMatters.tickUntil(level, () -> !cell.isValidMatterCell() || cell.connectOrCreateMatterGrid(pos, level, true));
}
public MatterGrid() {

View File

@ -0,0 +1,51 @@
package ru.dbotthepony.mc.otm.menu;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveRack;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
import javax.annotation.Nullable;
public class DriveRackMenu extends PoweredMatteryMenu {
public DriveRackMenu(int p_38852_, Inventory inventory) {
this(p_38852_, inventory, null);
}
public final MatterySlot[] drives = new MatterySlot[4];
public DriveRackMenu(int p_38852_, Inventory inventory, BlockEntityDriveRack tile) {
super(Registry.Menus.DRIVE_RACK, p_38852_, inventory, tile);
var container = tile != null ? tile.drives : new SimpleContainer(4);
for (int i = 0; i < container.getContainerSize(); i++) {
drives[i] = new MatterySlot(container, i) {
@Override
public boolean mayPlace(ItemStack p_40231_) {
return p_40231_.getCapability(MatteryCapability.DRIVE).isPresent();
}
};
addSlot(drives[i]);
}
addBatterySlot();
addInventorySlots();
}
@Override
protected int getWorkingSlotStart() {
return 0;
}
@Override
protected int getWorkingSlotEnd() {
return 5;
}
}

View File

@ -2,7 +2,6 @@ package ru.dbotthepony.mc.otm.menu;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
@ -12,28 +11,30 @@ import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.energy.CapabilityEnergy;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import ru.dbotthepony.mc.otm.Registry;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveViewer;
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
import ru.dbotthepony.mc.otm.capability.drive.IItemViewListener;
import ru.dbotthepony.mc.otm.capability.drive.IMatteryDrive;
import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive;
import ru.dbotthepony.mc.otm.menu.data.INetworkedItemViewSupplier;
import ru.dbotthepony.mc.otm.menu.data.InteractPacket;
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView;
import ru.dbotthepony.mc.otm.menu.slot.MatterySlot;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.SetCarriedPacket;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import java.util.function.Supplier;
public class DriveViewerMenu extends PoweredMatteryMenu {
public NetworkedItemView view;
public class DriveViewerMenu extends PoweredMatteryMenu implements INetworkedItemViewSupplier {
public final NetworkedItemView view;
public MatterySlot drive_slot;
protected IMatteryDrive last_drive;
public IMatteryDrive last_drive;
@Override
public NetworkedItemView getNetworkedItemView() {
return view;
}
public DriveViewerMenu(int containerID, Inventory inventory) {
this(containerID, inventory, null);
@ -51,9 +52,27 @@ public class DriveViewerMenu extends PoweredMatteryMenu {
}
};
addSlot(drive_slot);
view = new NetworkedItemView();
view = new NetworkedItemView(inventory.player, this, tile == null) {
@Override
public int calculateIOAmount(int desired_amount) {
if (tile != null)
return tile.getIOItemCount(desired_amount, true);
return super.calculateIOAmount(desired_amount);
}
@Override
public void doneIOAmount(int done_amount) {
if (tile != null) {
tile.getIOItemCount(done_amount, false);
return;
}
super.doneIOAmount(done_amount);
}
};
addSlot(drive_slot);
addBatterySlot();
addInventorySlots();
}
@ -66,35 +85,18 @@ public class DriveViewerMenu extends PoweredMatteryMenu {
var get = ((BlockEntityDriveViewer) tile).drive_slot.getItem(0);
if (get.isEmpty()) {
if (last_drive != null) {
last_drive.removeListener(view);
view.clear();
}
last_drive = null;
} else {
var get_cap = get.getCapability(MatteryCapability.DRIVE).resolve();
if (get_cap.isEmpty()) {
if (last_drive != null) {
last_drive.removeListener(view);
view.clear();
}
last_drive = null;
} else {
if (last_drive != get_cap.get()) {
if (last_drive != null)
last_drive.removeListener(view);
view.clear();
get_cap.get().addListener(view);
}
last_drive = get_cap.get();
}
}
view.setComponent(last_drive);
view.network();
}
}
@ -119,109 +121,6 @@ public class DriveViewerMenu extends PoweredMatteryMenu {
}
}
/**
* Creates a virtual, slotless container for Player's interaction with.
*/
public class NetworkedItemView implements IItemViewListener {
public record NetworkedItem(int id, ItemStack stack, UUID id_upstream) {
public NetworkedItem(int id, ItemStack stack) {
this(id, stack, null);
}
}
protected int next_stack_id = 0;
protected final HashMap<Integer, NetworkedItem> state = new HashMap<>();
protected final HashMap<UUID, NetworkedItem> upstream_state = new HashMap<>();
protected final ArrayList<Object> backlog = new ArrayList<>();
private List<NetworkedItem> view_cache;
public void clearCache() {
view_cache = null;
}
public List<NetworkedItem> getItems() {
if (view_cache != null)
return view_cache;
return view_cache = List.copyOf(state.values());
}
@Override
public void addViewItem(ItemStack stack, UUID id_upstream) {
if (upstream_state.containsKey(id_upstream))
throw new IllegalStateException("Already tracking ItemStack with upstream id " + id_upstream + "!");
var state = new NetworkedItem(next_stack_id++, stack.copy(), id_upstream);
this.state.put(state.id, state);
this.upstream_state.put(id_upstream, state);
if (tile != null) {
backlog.add(new StackAddPacket(containerId, state.id, stack));
}
clearCache();
}
@Override
public void changeViewItem(UUID id_upstream, int new_count) {
var get = upstream_state.get(id_upstream);
if (get == null)
throw new IllegalStateException("Unknown ItemStack with upstream id " + id_upstream + "!");
get.stack.setCount(new_count);
if (tile != null) {
backlog.add(new StackChangePacket(containerId, get.id, new_count));
}
clearCache();
}
@Override
public void removeViewItem(UUID id_upstream) {
var get = upstream_state.get(id_upstream);
if (get == null)
throw new IllegalStateException("Unknown ItemStack with upstream id " + id_upstream + "!");
upstream_state.remove(id_upstream);
state.remove(get.id);
if (tile != null) {
backlog.add(new StackRemovePacket(containerId, get.id));
}
clearCache();
}
public void clear() {
clearCache();
upstream_state.clear();
state.clear();
if (tile != null) {
backlog.clear();
backlog.add(new ClearPacket(containerId));
}
}
public void network() {
if (tile == null)
throw new IllegalStateException("Not a server");
var consumer = PacketDistributor.PLAYER.with(() -> (ServerPlayer) ply);
for (var packet : backlog) {
MatteryNetworking.CHANNEL.send(consumer, packet);
}
backlog.clear();
}
}
@Override
public ItemStack quickMoveStack(Player ply, int slot_index) {
var slot = slots.get(slot_index);
@ -276,283 +175,6 @@ public class DriveViewerMenu extends PoweredMatteryMenu {
return copy;
}
public record InteractPacket(int id, int stack_id, ClickType click, ClickAction action) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeEnum(click);
buffer.writeEnum(action);
}
public static InteractPacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack = buffer.readInt();
var click = buffer.readEnum(ClickType.class);
var action = buffer.readEnum(ClickAction.class);
return new InteractPacket(id, stack, click, action);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var ply = context.get().getSender();
if (ply.containerMenu instanceof DriveViewerMenu menu) {
var view = menu.containerId == id ? menu.view : null;
if (view == null) {
return;
}
ply.resetLastActionTime();
if (menu.last_drive == null)
return;
var tile = (BlockEntityDriveViewer) menu.tile;
if (click == ClickType.QUICK_MOVE && stack_id > -1) {
if (!tile.canIOItems())
return;
var get_state = view.state.get(stack_id);
if (get_state == null) {
return;
}
int amount = tile.getIOItemCount(action == ClickAction.PRIMARY ? get_state.stack.getMaxStackSize() : Math.max(1, get_state.stack.getMaxStackSize() / 2), true);
var extracted = menu.last_drive.extractItem(get_state.id_upstream, amount, true);
if (!extracted.isEmpty()) {
var move = menu.quickMoveToInventory(extracted, false);
if (move.remaining().getCount() != extracted.getCount()) {
menu.last_drive.extractItem(get_state.id_upstream, extracted.getCount() - move.remaining().getCount(), false);
tile.getIOItemCount(extracted.getCount() - move.remaining().getCount(), false);
}
}
return;
}
if (click == ClickType.CLONE) {
if (stack_id < 0 || !ply.getAbilities().instabuild)
return;
var get_state = view.state.get(stack_id);
if (get_state == null) {
return;
}
var copy = get_state.stack.copy();
copy.setCount(Math.min(copy.getCount(), copy.getMaxStackSize()));
menu.setCarried(copy);
MatteryNetworking.send(ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
return;
}
if (!tile.canIOItems())
return;
if (!menu.getCarried().isEmpty() && click != ClickType.QUICK_MOVE) {
// try to put
if (action == ClickAction.PRIMARY) {
var carried = menu.getCarried();
int amount = tile.getIOItemCount(carried.getCount(), true);
if (amount == carried.getCount()) {
var leftover = menu.last_drive.insertItem(menu.getCarried(), false);
menu.setCarried(leftover);
tile.getIOItemCount(amount - leftover.getCount(), false);
MatteryNetworking.send(ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
} else if (amount != 0) {
var copy = carried.copy();
copy.setCount(amount);
var leftover = menu.last_drive.insertItem(copy, false);
tile.getIOItemCount(amount - leftover.getCount(), false);
leftover.setCount(carried.getCount() - amount + leftover.getCount());
menu.setCarried(leftover);
MatteryNetworking.send(ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
} else {
var copy = menu.getCarried().copy();
copy.setCount(1);
if (tile.getIOItemCount(1, true) == 1 && menu.last_drive.insertItem(copy, false).isEmpty()) {
menu.getCarried().shrink(1);
tile.getIOItemCount(1, false);
MatteryNetworking.send(ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
}
} else if (stack_id > -1) {
var get_state = view.state.get(stack_id);
if (get_state == null) {
return;
}
int amount = tile.getIOItemCount(action == ClickAction.PRIMARY ? get_state.stack.getMaxStackSize() : Math.max(1, get_state.stack.getMaxStackSize() / 2), true);
var extracted = menu.last_drive.extractItem(get_state.id_upstream, amount, false);
tile.getIOItemCount(extracted.getCount(), false);
menu.setCarried(extracted);
MatteryNetworking.send(ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
}
});
}
}
public record ClearPacket(int id) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
}
public static ClearPacket read(FriendlyByteBuf buffer) {
return new ClearPacket(buffer.readInt());
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof DriveViewerMenu menu && menu.containerId == id ? menu.view : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
get.clear();
});
}
}
public record StackAddPacket(int id, int stack_id, ItemStack stack) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeRegistryId(stack.getItem());
buffer.writeInt(stack.getCount());
buffer.writeBoolean(stack.getTag() != null);
if (stack.getTag() != null)
buffer.writeNbt(stack.getShareTag());
}
public static StackAddPacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack = buffer.readInt();
var item = buffer.readRegistryIdSafe(Item.class);
var count = buffer.readInt();
var state = new ItemStack(item, count);
if (buffer.readBoolean())
state.readShareTag(buffer.readNbt());
return new StackAddPacket(id, stack, state);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof DriveViewerMenu menu && menu.containerId == id ? menu.view : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
if (get.state.containsKey(stack_id)) {
throw new IllegalStateException("Item tracker " + id + " already has stack with id of " + stack_id);
}
get.state.put(stack_id, new NetworkedItemView.NetworkedItem(stack_id, stack));
get.clearCache();
});
}
}
public record StackChangePacket(int id, int stack_id, int new_count) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeInt(new_count);
}
public static StackChangePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
var new_count = buffer.readInt();
return new StackChangePacket(id, stack_id, new_count);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof DriveViewerMenu menu && menu.containerId == id ? menu.view : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.get(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get_state.stack.setCount(new_count);
get.clearCache();
});
}
}
public record StackRemovePacket(int id, int stack_id) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
}
public static StackRemovePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
return new StackRemovePacket(id, stack_id);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof DriveViewerMenu menu && menu.containerId == id ? menu.view : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.remove(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get.clearCache();
});
}
}
public enum FilterSwitch {
MATCH_NBT,
MATCH_TAG,

View File

@ -0,0 +1,58 @@
package ru.dbotthepony.mc.otm.menu;
import net.minecraft.world.entity.player.Inventory;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
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.menu.data.INetworkedItemViewSupplier;
import ru.dbotthepony.mc.otm.menu.data.InteractPacket;
import ru.dbotthepony.mc.otm.menu.data.NetworkedItemView;
import ru.dbotthepony.mc.otm.storage.ItemStackObject;
import javax.annotation.Nonnull;
public class ItemMonitorMenu extends PoweredMatteryMenu implements INetworkedItemViewSupplier {
public final NetworkedItemView view;
@Nonnull
@Override
public NetworkedItemView getNetworkedItemView() {
return view;
}
public ItemMonitorMenu(int p_38852_, Inventory inventory) {
this(p_38852_, inventory, null);
}
public ItemMonitorMenu(int p_38852_, Inventory inventory, BlockEntityItemMonitor tile) {
super(Registry.Menus.ITEM_MONITOR, p_38852_, inventory, tile);
view = new NetworkedItemView(inventory.player, this, tile == null);
if (tile != null) {
OverdriveThatMatters.LOGGER.info("{}", tile.cell.getStorageGrid().global_item_view.getCombinedItemList());
view.setComponent(tile.cell.getStorageGrid().getVirtualComponent(ItemStackObject.class));
}
addBatterySlot();
addInventorySlots();
}
@Override
public void broadcastChanges() {
super.broadcastChanges();
view.network();
}
@Override
protected int getWorkingSlotStart() {
return 0;
}
@Override
protected int getWorkingSlotEnd() {
return 1;
}
}

View File

@ -18,10 +18,10 @@ import java.util.Set;
import java.util.function.Consumer;
public abstract class MatteryMenu extends AbstractContainerMenu {
protected BlockEntity tile;
public final BlockEntity tile;
protected Inventory inventory;
protected Player ply;
public final Inventory inventory;
public final Player ply;
public final ArrayList<AbstractWidget> mattery_widgets = new ArrayList<>();
public final ArrayList<MatterySlot> inventory_slots = new ArrayList<>();
@ -43,13 +43,13 @@ public abstract class MatteryMenu extends AbstractContainerMenu {
}
protected MatteryMenu(@Nullable MenuType<?> p_38851_, int p_38852_, Inventory inventory) {
super(p_38851_, p_38852_);
this.inventory = inventory;
this.ply = inventory.player;
this(p_38851_, p_38852_, inventory, null);
}
protected MatteryMenu(@Nullable MenuType<?> p_38851_, int p_38852_, Inventory inventory, BlockEntity tile) {
this(p_38851_, p_38852_, inventory);
super(p_38851_, p_38852_);
this.inventory = inventory;
this.ply = inventory.player;
this.tile = tile;
}
@ -157,22 +157,6 @@ public abstract class MatteryMenu extends AbstractContainerMenu {
abstract protected int getWorkingSlotStart();
abstract protected int getWorkingSlotEnd();
protected void notifySlotsWatchCurrentStack() {
for (var slot : slots) {
if (slot instanceof MatterySlot slot1) {
slot1.watchCurrentStack();
}
}
}
protected void notifySlotsUnwatchCurrentStack() {
for (var slot : slots) {
if (slot instanceof MatterySlot slot1) {
slot1.unwatchCurrentStack();
}
}
}
// This method receive Player interactor and slot_index where Shift + Right click occurred
// It shall return item stack that got moved
@Override
@ -193,7 +177,6 @@ public abstract class MatteryMenu extends AbstractContainerMenu {
ItemStack moved = ItemStack.EMPTY;
Slot get_slot = this.slots.get(slot_index);
notifySlotsWatchCurrentStack();
if (get_slot.hasItem()) {
ItemStack slot_item = get_slot.getItem();
@ -203,12 +186,10 @@ public abstract class MatteryMenu extends AbstractContainerMenu {
// Moving FROM machine TO inventory
if (!moveItemStackTo(slot_item, inventory_slot_index_start, inventory_slot_index_end + 1, false)) {
notifySlotsUnwatchCurrentStack();
return ItemStack.EMPTY;
}
} else if (!moveItemStackTo(slot_item, start, end, false)) {
// Moving FROM inventory TO machine
notifySlotsUnwatchCurrentStack();
return ItemStack.EMPTY;
}
@ -217,11 +198,8 @@ public abstract class MatteryMenu extends AbstractContainerMenu {
} else {
get_slot.setChanged();
}
notifySlotsWatchCurrentStack();
}
notifySlotsUnwatchCurrentStack();
return moved;
}

View File

@ -12,7 +12,7 @@ import ru.dbotthepony.mc.otm.menu.widget.BatteryLevelWidget;
import javax.annotation.Nullable;
abstract public class PoweredMatteryMenu extends MatteryMenu {
protected BlockEntityMatteryPowered tile;
public final BlockEntityMatteryPowered tile;
public BatteryLevelWidget battery_widget;
public BatterySlot battery_slot;

View File

@ -0,0 +1,31 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import java.util.function.Supplier;
public record ClearPacket(int id) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
}
public static ClearPacket read(FriendlyByteBuf buffer) {
return new ClearPacket(buffer.readInt());
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
get.clear();
});
}
}

View File

@ -0,0 +1,11 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface INetworkedItemViewSupplier {
NetworkedItemView getNetworkedItemView();
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.inventory.ClickAction;
import net.minecraft.world.inventory.ClickType;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.block.entity.BlockEntityDriveViewer;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.SetCarriedPacket;
import java.util.function.Supplier;
public record InteractPacket(int id, int stack_id, ClickType click, ClickAction action) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeEnum(click);
buffer.writeEnum(action);
}
public static InteractPacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack = buffer.readInt();
var click = buffer.readEnum(ClickType.class);
var action = buffer.readEnum(ClickAction.class);
return new InteractPacket(id, stack, click, action);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var ply = context.get().getSender();
if (ply.containerMenu instanceof INetworkedItemViewSupplier supplier && ply.containerMenu.containerId == id) {
ply.resetLastActionTime();
supplier.getNetworkedItemView().playerInteract(this);
}
});
}
}

View File

@ -0,0 +1,288 @@
package ru.dbotthepony.mc.otm.menu.data;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.ClickAction;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import ru.dbotthepony.mc.otm.menu.MatteryMenu;
import ru.dbotthepony.mc.otm.network.MatteryNetworking;
import ru.dbotthepony.mc.otm.network.SetCarriedPacket;
import ru.dbotthepony.mc.otm.storage.*;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
import static net.minecraft.client.gui.screens.Screen.hasShiftDown;
/**
* Creates a virtual, slotless container for Player's interaction with.
*/
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class NetworkedItemView implements IStorageListener<ItemStackObject> {
public final boolean remote;
public final MatteryMenu menu;
public final Player ply;
protected int next_stack_id = 0;
protected final HashMap<Integer, NetworkedItem> state = new HashMap<>();
protected final HashMap<UUID, NetworkedItem> upstream_state = new HashMap<>();
protected final ArrayList<Object> backlog = new ArrayList<>();
@Nullable
public NetworkedItem get(int id) {
return state.get(id);
}
private List<NetworkedItem> view_cache;
protected IStorageComponent<ItemStackObject> provider;
public NetworkedItemView(Player ply, MatteryMenu menu, boolean remote) {
this.remote = remote;
this.menu = menu;
this.ply = ply;
}
public void mouseClick(int index, int mouse_click_type) {
var list = getItems();
var action = mouse_click_type == InputConstants.MOUSE_BUTTON_LEFT ? ClickAction.PRIMARY : ClickAction.SECONDARY;
var type = mouse_click_type == InputConstants.MOUSE_BUTTON_MIDDLE ? ClickType.CLONE : hasShiftDown() ? ClickType.QUICK_MOVE : ClickType.PICKUP;
MatteryNetworking.send(null, new InteractPacket(menu.containerId, index >= list.size() ? -1 : list.get(index).id(), type, action));
}
public void setComponent(@Nullable IStorageComponent<ItemStackObject> provider) {
if (provider == this.provider)
return;
if (this.provider != null)
this.provider.removeListenerAuto(this);
this.provider = provider;
if (provider != null)
provider.addListenerAuto(this);
}
public record NetworkedItem(int id, ItemStack stack, @Nullable UUID id_upstream) {
public NetworkedItem(int id, ItemStack stack) {
this(id, stack, null);
}
}
public void clearCache() {
view_cache = null;
}
public List<NetworkedItem> getItems() {
if (view_cache != null)
return view_cache;
return view_cache = List.copyOf(state.values());
}
@Override
public void addObject(ItemStackObject stack, UUID id, IStorageView<ItemStackObject> provider) {
addObject(stack.stack(), id);
}
@Override
public void changeObject(UUID id, BigDecimal new_count) {
changeObject(id, new_count.intValue());
}
@Override
public void removeObject(UUID id) {
var get = upstream_state.get(id);
if (get == null)
throw new IllegalStateException("Unknown ItemStack with upstream id " + id + "!");
upstream_state.remove(id);
state.remove(get.id);
if (!remote) {
backlog.add(new StackRemovePacket(menu.containerId, get.id));
}
clearCache();
}
public void addObject(ItemStack stack, UUID id_upstream) {
if (upstream_state.containsKey(id_upstream))
throw new IllegalStateException("Already tracking ItemStack with upstream id " + id_upstream + "!");
var state = new NetworkedItem(next_stack_id++, stack.copy(), id_upstream);
this.state.put(state.id, state);
this.upstream_state.put(id_upstream, state);
if (!remote) {
backlog.add(new StackAddPacket(menu.containerId, state.id, stack));
}
clearCache();
}
public void changeObject(UUID id_upstream, int new_count) {
var get = upstream_state.get(id_upstream);
if (get == null)
throw new IllegalStateException("Unknown ItemStack with upstream id " + id_upstream + "!");
get.stack.setCount(new_count);
if (!remote) {
backlog.add(new StackChangePacket(menu.containerId, get.id, new_count));
}
clearCache();
}
public void clear() {
clearCache();
upstream_state.clear();
state.clear();
if (!remote) {
backlog.clear();
backlog.add(new ClearPacket(menu.containerId));
}
}
public void network() {
if (remote)
throw new IllegalStateException("Not a server");
var consumer = PacketDistributor.PLAYER.with(() -> (ServerPlayer) ply);
for (var packet : backlog) {
MatteryNetworking.CHANNEL.send(consumer, packet);
}
backlog.clear();
}
public int calculateIOAmount(int desired_amount) {
return desired_amount;
}
public void doneIOAmount(int done_amount) {
}
public void playerInteract(InteractPacket packet) {
if (provider == null)
return;
var click = packet.click();
var action = packet.action();
var stack_id = packet.stack_id();
if (click == ClickType.CLONE) {
if (stack_id < 0 || !ply.getAbilities().instabuild)
return;
var get_state = get(stack_id);
if (get_state == null) {
return;
}
var copy = get_state.stack().copy();
copy.setCount(Math.min(copy.getCount(), copy.getMaxStackSize()));
ply.containerMenu.setCarried(copy);
MatteryNetworking.send((ServerPlayer) ply, new SetCarriedPacket(ply.containerMenu.getCarried()));
ply.containerMenu.setRemoteCarried(ply.containerMenu.getCarried().copy());
return;
}
if (click == ClickType.QUICK_MOVE && stack_id > -1) {
var get_state = get(stack_id);
if (get_state == null) {
return;
}
int amount = calculateIOAmount(action == ClickAction.PRIMARY ? get_state.stack().getMaxStackSize() : Math.max(1, get_state.stack().getMaxStackSize() / 2));
var extracted = provider.extractObject(get_state.id_upstream(), new BigDecimal(amount), true);
if (!extracted.isEmpty()) {
var move = menu.quickMoveToInventory(extracted.stack(), false);
if (move.remaining().getCount() != extracted.stack().getCount()) {
provider.extractObject(get_state.id_upstream(), new BigDecimal(extracted.stack().getCount() - move.remaining().getCount()), false);
doneIOAmount(extracted.stack().getCount() - move.remaining().getCount());
}
}
return;
}
if (!menu.getCarried().isEmpty() && click != ClickType.QUICK_MOVE) {
// try to put
if (action == ClickAction.PRIMARY) {
var carried = menu.getCarried();
int amount = calculateIOAmount(carried.getCount());
if (amount == carried.getCount()) {
var leftover = provider.insertObject(new ItemStackObject(menu.getCarried()), false);
menu.setCarried(leftover.stack());
doneIOAmount(amount - leftover.stack().getCount());
MatteryNetworking.send((ServerPlayer) ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
} else if (amount != 0) {
var copy = carried.copy();
copy.setCount(amount);
var leftover = provider.insertObject(new ItemStackObject(copy), false);
doneIOAmount(amount - leftover.stack().getCount());
leftover.setCount(carried.getCount() - amount + leftover.getCountInt());
menu.setCarried(leftover.stack());
MatteryNetworking.send((ServerPlayer) ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
} else {
var copy = menu.getCarried().copy();
copy.setCount(1);
if (calculateIOAmount(1) == 1 && provider.insertObject(new ItemStackObject(copy), false).isEmpty()) {
menu.getCarried().shrink(1);
doneIOAmount(1);
MatteryNetworking.send((ServerPlayer) ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
}
} else if (stack_id > -1) {
var get_state = get(stack_id);
if (get_state == null) {
return;
}
int amount = calculateIOAmount(action == ClickAction.PRIMARY ? get_state.stack().getMaxStackSize() : Math.max(1, get_state.stack().getMaxStackSize() / 2));
var extracted = provider.extractObject(get_state.id_upstream(), new BigDecimal(amount), false);
doneIOAmount(extracted.getCountInt());
menu.setCarried(extracted.stack());
MatteryNetworking.send((ServerPlayer) ply, new SetCarriedPacket(menu.getCarried()));
menu.setRemoteCarried(menu.getCarried().copy());
}
}
}

View File

@ -0,0 +1,55 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import java.util.function.Supplier;
public record StackAddPacket(int id, int stack_id, ItemStack stack) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeRegistryId(stack.getItem());
buffer.writeInt(stack.getCount());
buffer.writeBoolean(stack.getTag() != null);
if (stack.getTag() != null)
buffer.writeNbt(stack.getShareTag());
}
public static StackAddPacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack = buffer.readInt();
var item = buffer.readRegistryIdSafe(Item.class);
var count = buffer.readInt();
var state = new ItemStack(item, count);
if (buffer.readBoolean())
state.readShareTag(buffer.readNbt());
return new StackAddPacket(id, stack, state);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
if (get.state.containsKey(stack_id)) {
throw new IllegalStateException("Item tracker " + id + " already has stack with id of " + stack_id);
}
get.state.put(stack_id, new NetworkedItemView.NetworkedItem(stack_id, stack));
get.clearCache();
});
}
}

View File

@ -0,0 +1,44 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import java.util.function.Supplier;
public record StackChangePacket(int id, int stack_id, int new_count) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
buffer.writeInt(new_count);
}
public static StackChangePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
var new_count = buffer.readInt();
return new StackChangePacket(id, stack_id, new_count);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.get(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get_state.stack().setCount(new_count);
get.clearCache();
});
}
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.otm.menu.data;
import net.minecraft.client.Minecraft;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import java.util.function.Supplier;
public record StackRemovePacket(int id, int stack_id) {
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(id);
buffer.writeInt(stack_id);
}
public static StackRemovePacket read(FriendlyByteBuf buffer) {
var id = buffer.readInt();
var stack_id = buffer.readInt();
return new StackRemovePacket(id, stack_id);
}
public void play(Supplier<NetworkEvent.Context> context) {
context.get().setPacketHandled(true);
context.get().enqueueWork(() -> {
var get = Minecraft.getInstance().player.containerMenu instanceof INetworkedItemViewSupplier supplier && Minecraft.getInstance().player.containerMenu.containerId == id ? supplier.getNetworkedItemView() : null;
if (get == null) {
throw new IllegalStateException("No such item tracker with id " + id);
}
var get_state = get.state.remove(stack_id);
if (get_state == null) {
throw new IllegalStateException("Item tracker " + id + " has no stack with id of " + stack_id);
}
get.clearCache();
});
}
}

View File

@ -30,56 +30,4 @@ public class MatterySlot extends Slot {
public MatterySlot(Container p_40223_, int index) {
this(p_40223_, index, 0, 0, true);
}
private ItemStack last_stack;
private ItemStack watching_stack;
public void watchCurrentStack() {
watching_stack = super.getItem().copy();
}
public void unwatchCurrentStack() {
watching_stack = null;
}
@Override
public ItemStack getItem() {
var get = super.getItem();
last_stack = get.copy();
return get;
}
@Override
public void setChanged() {
if (container instanceof MatteryContainer container1) {
ItemStack old = watching_stack != null ? watching_stack.copy() : last_stack.copy();
container1.setChanged(getSlotIndex(), super.getItem(), old);
} else {
super.setChanged();
}
}
@Override
public void set(ItemStack p_40240_) {
if (container instanceof MatteryContainer container1) {
ItemStack old = watching_stack != null ? watching_stack.copy() : container1.getItem(getSlotIndex()).copy();
container1.startIgnore();
container1.setItem(getSlotIndex(), p_40240_);
container1.stopIgnore();
container1.setChanged(getSlotIndex(), p_40240_, old);
} else {
super.set(p_40240_);
}
}
@Override
public void onTake(Player p_150645_, ItemStack p_150646_) {
if (container instanceof MatteryContainer container1) {
ItemStack old = watching_stack != null ? watching_stack.copy() : last_stack.copy();
container1.setChanged(getSlotIndex(), p_150646_.copy(), old);
last_stack = ItemStack.EMPTY;
} else {
super.onTake(p_150645_, p_150646_);
}
}
}

View File

@ -7,8 +7,8 @@ import net.minecraftforge.fmllegacy.network.NetworkRegistry;
import net.minecraftforge.fmllegacy.network.PacketDistributor;
import net.minecraftforge.fmllegacy.network.simple.SimpleChannel;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.item.ItemPortableCondensationDrive;
import ru.dbotthepony.mc.otm.menu.DriveViewerMenu;
import ru.dbotthepony.mc.otm.menu.data.*;
import ru.dbotthepony.mc.otm.network.android.*;
import java.util.Optional;
@ -26,6 +26,14 @@ public class MatteryNetworking {
);
public static void send(ServerPlayer ply, Object ...messages) {
if (ply == null) {
for (var message : messages) {
CHANNEL.sendToServer(message);
}
return;
}
var supplier = PacketDistributor.PLAYER.with(() -> ply);
for (var message : messages) {
@ -33,12 +41,6 @@ public class MatteryNetworking {
}
}
public static void send(Object ...messages) {
for (var message : messages) {
CHANNEL.sendToServer(message);
}
}
public static void register() {
CHANNEL.registerMessage(
next_network_id++,
@ -150,46 +152,46 @@ public class MatteryNetworking {
CHANNEL.registerMessage(
next_network_id++,
DriveViewerMenu.ClearPacket.class,
DriveViewerMenu.ClearPacket::write,
DriveViewerMenu.ClearPacket::read,
DriveViewerMenu.ClearPacket::play,
ClearPacket.class,
ClearPacket::write,
ClearPacket::read,
ClearPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
DriveViewerMenu.StackAddPacket.class,
DriveViewerMenu.StackAddPacket::write,
DriveViewerMenu.StackAddPacket::read,
DriveViewerMenu.StackAddPacket::play,
StackAddPacket.class,
StackAddPacket::write,
StackAddPacket::read,
StackAddPacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
DriveViewerMenu.StackChangePacket.class,
DriveViewerMenu.StackChangePacket::write,
DriveViewerMenu.StackChangePacket::read,
DriveViewerMenu.StackChangePacket::play,
StackChangePacket.class,
StackChangePacket::write,
StackChangePacket::read,
StackChangePacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
DriveViewerMenu.StackRemovePacket.class,
DriveViewerMenu.StackRemovePacket::write,
DriveViewerMenu.StackRemovePacket::read,
DriveViewerMenu.StackRemovePacket::play,
StackRemovePacket.class,
StackRemovePacket::write,
StackRemovePacket::read,
StackRemovePacket::play,
Optional.of(NetworkDirection.PLAY_TO_CLIENT)
);
CHANNEL.registerMessage(
next_network_id++,
DriveViewerMenu.InteractPacket.class,
DriveViewerMenu.InteractPacket::write,
DriveViewerMenu.InteractPacket::read,
DriveViewerMenu.InteractPacket::play,
InteractPacket.class,
InteractPacket::write,
InteractPacket::read,
InteractPacket::play,
Optional.of(NetworkDirection.PLAY_TO_SERVER)
);

View File

@ -0,0 +1,12 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageComponent<T extends IStorageObject> extends IStorageView<T>, IStorageConsumer<T> {
}

View File

@ -0,0 +1,11 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageConsumer<T extends IStorageObject> extends IStorageIdentity<T> {
T insertObject(T obj, boolean simulate);
}

View File

@ -0,0 +1,11 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageIdentity<T extends IStorageObject> {
Class<T> storageIdentity();
}

View File

@ -0,0 +1,16 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.world.item.ItemStack;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.UUID;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageListener<T extends IStorageObject> {
void addObject(T stack, UUID id, IStorageView<T> provider);
void changeObject(UUID id, BigDecimal new_count);
void removeObject(UUID id);
}

View File

@ -0,0 +1,17 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageObject {
IStorageObject copy();
void setCount(BigDecimal value);
BigDecimal getCount();
boolean isEmpty();
}

View File

@ -0,0 +1,12 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageTrigger<T extends IStorageObject> extends IStorageIdentity<T> {
boolean addListener(IStorageListener<T> listener);
boolean removeListener(IStorageListener<T> listener);
}

View File

@ -0,0 +1,8 @@
package ru.dbotthepony.mc.otm.storage;
import java.util.UUID;
public interface IStorageTuple<T extends IStorageObject> {
UUID id();
T object();
}

View File

@ -0,0 +1,66 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public interface IStorageView<T extends IStorageObject> extends IStorageTrigger<T> {
/**
* @param id identifier of object
* @return stored object (not a copy). Do not edit it.
*/
T getStoredObject(UUID id);
/**
* @param id identifier of object to extract
* @param amount amount of units to extract
* @param simulate whenever to simulate the action or not
* @return copy of object, with amount of units actually extracted
*/
T extractObject(UUID id, BigDecimal amount, boolean simulate);
/**
* Designed for views, for extraction with less computation overhead caused by
* copying object extracted
*
* @param id identifier of object to extract
* @param amount desired amount to extract
* @param simulate whenever to simulate the action or not
* @return amount extracted
*/
default BigDecimal extractObjectCount(UUID id, BigDecimal amount, boolean simulate) {
return extractObject(id, amount, simulate).getCount();
}
List<IStorageTuple<T>> getStorageObjects();
default boolean addListenerAuto(IStorageListener<T> listener) {
if (addListener(listener)) {
for (var stack : getStorageObjects()) {
listener.addObject(stack.object(), stack.id(), this);
}
return true;
}
return false;
}
default boolean removeListenerAuto(IStorageListener<T> listener) {
if (removeListener(listener)) {
for (var stack : getStorageObjects()) {
listener.removeObject(stack.id());
}
return true;
}
return false;
}
}

View File

@ -0,0 +1,41 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.world.item.ItemStack;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public record ItemStackObject(ItemStack stack) implements IStorageObject {
public static final ItemStackObject EMPTY = new ItemStackObject(ItemStack.EMPTY);
@Override
public IStorageObject copy() {
return new ItemStackObject(stack.copy());
}
@Override
public void setCount(BigDecimal value) {
stack.setCount(Math.max(value.intValue(), 0));
}
public void setCount(int value) {
stack.setCount(Math.max(value, 0));
}
@Override
public BigDecimal getCount() {
return new BigDecimal(stack.getCount());
}
public int getCountInt() {
return stack.getCount();
}
@Override
public boolean isEmpty() {
return stack.isEmpty();
}
}

View File

@ -0,0 +1,221 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.util.LazyOptional;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import ru.dbotthepony.mc.otm.capability.IStorageGridCell;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.*;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class StorageGrid {
public final StorageItemView global_item_view = new StorageItemView();
private final HashSet<IStorageGridCell> cells = new HashSet<>();
private final HashMap<Class<? extends IStorageObject>, IStorageView<? extends IStorageObject>> views = new HashMap<>();
private final HashMap<Class<? extends IStorageObject>, ArrayList<IStorageConsumer<? extends IStorageObject>>> consumers = new HashMap<>();
private final HashMap<Class<? extends IStorageObject>, IStorageComponent<? extends IStorageObject>> virtual_components = new HashMap<>();
public StorageGrid() {
views.put(ItemStackObject.class, global_item_view);
}
@SuppressWarnings("unchecked")
public <T extends IStorageObject> T insertObject(Class<T> type, T object, boolean simulate) {
var leftover = object;
for (var consumer : consumers.computeIfAbsent(type, (k) -> new ArrayList<>())) {
leftover = ((IStorageConsumer<T>) consumer).insertObject(leftover, simulate);
if (leftover.isEmpty()) {
return leftover;
}
}
return leftover;
}
/**
* @param type Class, representing identity of stored object
* @param <T> The identity itself
* @return a virtual IStorageComponent<T>, or null if storage grid contain no view for provided type
*/
@SuppressWarnings("unchecked")
@Nullable
public <T extends IStorageObject> IStorageComponent<T> getVirtualComponent(final Class<T> type) {
return (IStorageComponent<T>) virtual_components.computeIfAbsent(type, (k) -> {
final var get_view = (IStorageView<T>) views.get(type);
if (get_view == null)
return null;
return new IStorageComponent<T>() {
@Override
public T insertObject(T obj, boolean simulate) {
return StorageGrid.this.insertObject(type, obj, simulate);
}
@Override
public T getStoredObject(UUID id) {
return get_view.getStoredObject(id);
}
@Override
public T extractObject(UUID id, BigDecimal amount, boolean simulate) {
return get_view.extractObject(id, amount, simulate);
}
@Override
public List<IStorageTuple<T>> getStorageObjects() {
return get_view.getStorageObjects();
}
@Override
public Class<T> storageIdentity() {
return type;
}
@Override
public boolean addListener(IStorageListener<T> listener) {
return get_view.addListener(listener);
}
@Override
public boolean removeListener(IStorageListener<T> listener) {
return get_view.removeListener(listener);
}
};
});
}
public int size() {
return cells.size();
}
@SuppressWarnings("unchecked")
public <T extends IStorageObject> void add(IStorageIdentity<T> identity) {
final var view = (IStorageView<T>) views.get(identity.storageIdentity());
if (view != null && identity instanceof IStorageView provider) {
((IStorageView<T>) provider).addListenerAuto((IStorageListener<T>) view);
} else if (view != null && identity instanceof IStorageTrigger provider) {
((IStorageTrigger<T>) provider).addListener((IStorageListener<T>) view);
}
if (identity instanceof IStorageConsumer provider) {
consumers.computeIfAbsent(identity.storageIdentity(), (k) -> new ArrayList<>()).add(provider);
}
}
public boolean add(LazyOptional<IStorageGridCell> cell) {
if (!cell.isPresent())
return false;
var resolved = cell.resolve();
if (resolved.isPresent()) {
final var value = resolved.get();
if (add(value)) {
cell.addListener((lazy) -> remove(value));
return true;
}
}
return false;
}
public boolean add(IStorageGridCell cell) {
if (cells.add(cell)) {
cell.setStorageGrid(this);
for (var identity : cell.getComponents()) {
add(identity);
}
return true;
}
return false;
}
@SuppressWarnings("unchecked")
public <T extends IStorageObject> void remove(IStorageIdentity<T> identity) {
final var view = (IStorageView<T>) views.get(identity.storageIdentity());
if (view != null && identity instanceof IStorageView provider) {
((IStorageView<T>) provider).removeListenerAuto((IStorageListener<T>) view);
} else if (view != null && identity instanceof IStorageTrigger provider) {
((IStorageTrigger<T>) provider).removeListener((IStorageListener<T>) view);
}
if (identity instanceof IStorageConsumer provider) {
consumers.computeIfAbsent(identity.storageIdentity(), (k) -> new ArrayList<>()).remove(provider);
}
}
public boolean remove(IStorageGridCell cell) {
if (cells.remove(cell)) {
cell.setStorageGrid(null);
for (var identity : cell.getComponents()) {
remove(identity);
}
return true;
}
return false;
}
public void detach(IStorageGridCell cell) {
if (cells.contains(cell)) {
for (var identity : cell.getComponents()) {
remove(identity);
}
}
}
public void attach(IStorageGridCell cell) {
if (cells.contains(cell)) {
for (var identity : cell.getComponents()) {
add(identity);
}
}
}
public StorageGrid merge(StorageGrid other) {
if (other == this)
return this;
if (size() < other.size()) {
var copy = List.copyOf(cells);
for (var cell : copy) {
remove(cell);
other.add(cell);
}
return other;
} else {
var copy = List.copyOf(other.cells);
for (var cell : copy) {
other.remove(cell);
add(cell);
}
return this;
}
}
public static void scheduleDiscoverNeighbours(IStorageGridCell cell, Level level, BlockPos pos) {
OverdriveThatMatters.tickUntil(level, () -> !cell.isValidStorageCell() || cell.connectOrCreateStorageGrid(level, pos, true));
}
}

View File

@ -0,0 +1,278 @@
package ru.dbotthepony.mc.otm.storage;
import net.minecraft.MethodsReturnNonnullByDefault;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import ru.dbotthepony.mc.otm.OverdriveThatMatters;
import javax.annotation.ParametersAreNonnullByDefault;
import java.math.BigDecimal;
import java.util.*;
@MethodsReturnNonnullByDefault
@ParametersAreNonnullByDefault
public class StorageItemView implements IStorageListener<ItemStackObject>, IStorageView<ItemStackObject> {
public record LocalTuple(ItemStackObject object, UUID id, List<RemoteTuple> tuples) implements IStorageTuple<ItemStackObject> {
public static final LocalTuple EMPTY = new LocalTuple(ItemStackObject.EMPTY, new UUID(0, 0), List.of());
@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();
}
}
@Override
public Class<ItemStackObject> storageIdentity() {
return ItemStackObject.class;
}
public record RemoteTuple(ItemStack stack, UUID remote_id, IStorageView<ItemStackObject> provider, LocalTuple local) {
public static final RemoteTuple EMPTY = new RemoteTuple(ItemStack.EMPTY, new UUID(0, 0), new IStorageView<>() {
@Override
public Class<ItemStackObject> storageIdentity() {
return ItemStackObject.class;
}
@Override
public boolean addListener(IStorageListener<ItemStackObject> listener) {
return false;
}
@Override
public boolean removeListener(IStorageListener<ItemStackObject> listener) {
return false;
}
@Override
public ItemStackObject getStoredObject(UUID id) {
return ItemStackObject.EMPTY;
}
@Override
public ItemStackObject extractObject(UUID id, BigDecimal amount, boolean simulate) {
return ItemStackObject.EMPTY;
}
@Override
public List<IStorageTuple<ItemStackObject>> getStorageObjects() {
return List.of();
}
}, LocalTuple.EMPTY);
public ItemStackObject extract(BigDecimal amount, boolean simulate) {
return provider.extractObject(remote_id, amount, simulate);
}
public BigDecimal extractCount(BigDecimal amount, boolean simulate) {
return provider.extractObjectCount(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();
}
}
// удаленный UUID -> Кортеж
private final HashMap<UUID, RemoteTuple> indexed_by_remote_uuid = new HashMap<>();
// локальный UUID -> Локальный кортеж
private final HashMap<UUID, LocalTuple> indexed_by_local_uuid = new HashMap<>();
// Item -> Список Локальных кортежей
private final HashMap<Item, ArrayList<LocalTuple>> indexed_by_item = new HashMap<>();
public List<ItemStack> getCopyRawItemList() {
final var list = new ArrayList<ItemStack>(indexed_by_remote_uuid.size());
for (var state : indexed_by_remote_uuid.values())
list.add(state.stack.copy());
return list;
}
public List<ItemStack> getRawItemList() {
final var list = new ArrayList<ItemStack>(indexed_by_remote_uuid.size());
for (var state : indexed_by_remote_uuid.values())
list.add(state.stack);
return list;
}
@Override
public List<IStorageTuple<ItemStackObject>> getStorageObjects() {
int capacity = 0;
for (var list : indexed_by_item.values())
capacity += list.size();
final var output = new ArrayList<IStorageTuple<ItemStackObject>>(capacity);
for (var listing : indexed_by_item.values())
output.addAll(listing);
return output;
}
public List<ItemStack> getCombinedItemList() {
int capacity = 0;
for (var list : indexed_by_item.values())
capacity += list.size();
final var output = new ArrayList<ItemStack>(capacity);
for (var listing : indexed_by_item.values())
for (var combined : listing)
output.add(combined.object.stack());
return output;
}
@Override
public void addObject(ItemStackObject _stack, UUID id, IStorageView<ItemStackObject> provider) {
if (indexed_by_remote_uuid.containsKey(id)) {
throw new IllegalStateException("Already tracking tuple with id " + id);
}
var stack = _stack.stack();
var items = indexed_by_item.computeIfAbsent(stack.getItem(), (k) -> new ArrayList<>());
LocalTuple local_tuple = null;
for (var item : items) {
if (ItemStack.tagMatches(item.object.stack(), stack)) {
item.object.stack().grow(stack.getCount());
local_tuple = item;
break;
}
}
boolean added = local_tuple == null;
if (added) {
local_tuple = new LocalTuple((ItemStackObject) _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(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.object, local_tuple.id, this);
}
} else {
for (var listener : listeners) {
listener.changeObject(local_tuple.id, local_tuple.object.getCount());
}
}
}
@Override
public void changeObject(UUID id, BigDecimal new_count) {
assert new_count.compareTo(BigDecimal.ZERO) > 0;
var tuple = indexed_by_remote_uuid.get(id);
if (tuple == null)
throw new IllegalStateException("No such tuple with id " + id);
var delta = new_count.intValue() - tuple.stack.getCount();
tuple.stack.setCount(new_count.intValue());
tuple.local.object.stack().grow(delta);
for (var listener : listeners) {
listener.changeObject(tuple.local.id, tuple.local.object.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);
var item = tuple.local.object.stack().getItem();
tuple.local.object.stack().shrink(tuple.stack.getCount());
tuple.local.tuples.remove(tuple);
indexed_by_remote_uuid.remove(id);
if (tuple.local.object.stack().getCount() <= 0 || tuple.local.tuples.size() == 0) {
assert tuple.local.object.stack().getCount() == tuple.local.tuples.size();
indexed_by_local_uuid.remove(tuple.local.id);
indexed_by_item.get(item).remove(tuple.local);
for (var listener : listeners) {
listener.removeObject(tuple.local.id);
}
}
}
private final Set<IStorageListener<ItemStackObject>> listeners = new HashSet<>();
@Override
public boolean addListener(IStorageListener<ItemStackObject> listener) {
return listeners.add(listener);
}
@Override
public boolean removeListener(IStorageListener<ItemStackObject> listener) {
return listeners.remove(listener);
}
@Override
public ItemStackObject getStoredObject(UUID id) {
return indexed_by_local_uuid.getOrDefault(id, LocalTuple.EMPTY).object;
}
@Override
public ItemStackObject extractObject(UUID id, BigDecimal _amount, boolean simulate) {
var tuple = indexed_by_local_uuid.get(id);
if (tuple == null)
return ItemStackObject.EMPTY;
int amount = _amount.intValue();
if (amount == 0)
return ItemStackObject.EMPTY;
if (amount == -1)
amount = tuple.object.stack().getMaxStackSize();
int extract = Math.min(amount, tuple.object.stack().getCount());
int extracted = 0;
var copy = tuple.object.stack().copy();
for (var remote_tuple : tuple.tuples) {
extracted += remote_tuple.extractCount(new BigDecimal(extract - extracted), simulate).intValue();
if (extracted >= extract)
break;
}
if (extracted > 0) {
copy.setCount(extracted);
return new ItemStackObject(copy);
}
return ItemStackObject.EMPTY;
}
}

View File

@ -0,0 +1,6 @@
package ru.dbotthepony.mc.otm.storage;
import java.util.UUID;
public record StorageTuple<T extends IStorageObject>(UUID id, T object) implements IStorageTuple<T> {
}