Skip to content

Commit

Permalink
feat: ✨ Add Storage IO blocks
Browse files Browse the repository at this point in the history
- Storage IO when placed in controller's multiblock allows input / outputing items from its sides
- Storage Input is similar to IO, but only allows piping items into the multiblock. Is very optimized and preferred way to pipe items into multiblock especially in cases where there's a lot piped and even more when the storage could be getting full and there are still items to pipe in
- Storage Output is similar to IO again, only allows to output items
- None of these connect other blocks to the storage multiblock so there can be another storage on their side that pushes / pulls items to / from multiblock using hopper upgrade
- Storage controller changed to be inline with IO blocks
Thanks Ridanisaurus for all the new textures
  • Loading branch information
P3pp3rF1y committed Mar 12, 2024
1 parent 9ed7f47 commit c5ffb7a
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 55 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ org.gradle.daemon=false

mod_id=sophisticatedcore
mod_group_id=sophisticatedcore
mod_version=0.6.8
mod_version=0.6.9
sonar_project_key=sophisticatedcore:SophisticatedCore
github_package_url=https://maven.pkg.github.com/P3pp3rF1y/SophisticatedCore

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
import java.util.*;
import java.util.function.Function;

public abstract class ControllerBlockEntityBase extends BlockEntity implements IItemHandlerModifiable {
public abstract class ControllerBlockEntityBase extends BlockEntity implements IItemHandlerSimpleInserter {
public static final int SEARCH_RANGE = 15;
private List<BlockPos> storagePositions = new ArrayList<>();
private List<Integer> baseIndexes = new ArrayList<>();
Expand All @@ -52,9 +52,12 @@ public abstract class ControllerBlockEntityBase extends BlockEntity implements I
private final Map<BlockPos, Set<Item>> storageFilterItems = new HashMap<>();
private Set<BlockPos> linkedBlocks = new TreeSet<>(distanceComparator);
private Set<BlockPos> connectingBlocks = new TreeSet<>(distanceComparator);
private Set<BlockPos> nonConnectingBlocks = new TreeSet<>(distanceComparator);

@Nullable
private LazyOptional<IItemHandler> itemHandlerCap;
@Nullable
private LazyOptional<IItemHandler> noSideItemHandlerCap;

public boolean addLinkedBlock(BlockPos linkedPos) {
if (level != null && !level.isClientSide() && isWithinRange(linkedPos) && !linkedBlocks.contains(linkedPos) && !storagePositions.contains(linkedPos)) {
Expand All @@ -66,10 +69,10 @@ public boolean addLinkedBlock(BlockPos linkedPos) {
if (l.connectLinkedSelf()) {
Set<BlockPos> positionsToCheck = new LinkedHashSet<>();
positionsToCheck.add(linkedPos);
searchAndAddStorages(positionsToCheck, true);
searchAndAddBoundables(positionsToCheck, true);
}

searchAndAddStorages(new LinkedHashSet<>(l.getConnectablePositions()), false);
searchAndAddBoundables(new LinkedHashSet<>(l.getConnectablePositions()), false);
});
WorldHelper.notifyBlockUpdate(this);
return true;
Expand Down Expand Up @@ -101,12 +104,12 @@ public boolean isStorageConnected(BlockPos storagePos) {
return storagePositions.contains(storagePos);
}

public void searchAndAddStorages() {
public void searchAndAddBoundables() {
Set<BlockPos> positionsToCheck = new HashSet<>();
for (Direction dir : Direction.values()) {
positionsToCheck.add(getBlockPos().offset(dir.getNormal()));
}
searchAndAddStorages(positionsToCheck, false);
searchAndAddBoundables(positionsToCheck, false);
}

public void changeSlots(BlockPos storagePos, int newSlots, boolean hasEmptySlots) {
Expand Down Expand Up @@ -148,7 +151,7 @@ public int getSlots(int storageIndex) {
return getStorageSlots(storageIndex);
}

private void searchAndAddStorages(Set<BlockPos> positionsToCheck, boolean addingLinkedSelf) {
private void searchAndAddBoundables(Set<BlockPos> positionsToCheck, boolean addingLinkedSelf) {
Set<BlockPos> positionsChecked = new HashSet<>();

boolean first = true;
Expand All @@ -158,26 +161,30 @@ private void searchAndAddStorages(Set<BlockPos> positionsToCheck, boolean adding
it.remove();

final boolean finalFirst = first;
WorldHelper.getLoadedBlockEntity(level, posToCheck, IControllableStorage.class).ifPresentOrElse(storage ->
tryToConnectStorageAndAddPositionsToCheckAround(positionsToCheck, addingLinkedSelf, positionsChecked, posToCheck, finalFirst, storage),
WorldHelper.getLoadedBlockEntity(level, posToCheck, IControllerBoundable.class).ifPresentOrElse(boundable ->
tryToConnectStorageAndAddPositionsToCheckAround(positionsToCheck, addingLinkedSelf, positionsChecked, posToCheck, finalFirst, boundable),
() -> positionsChecked.add(posToCheck)
);
first = false;
}
}

private void tryToConnectStorageAndAddPositionsToCheckAround(Set<BlockPos> positionsToCheck, boolean addingLinkedSelf, Set<BlockPos> positionsChecked, BlockPos posToCheck, boolean finalFirst, IControllableStorage storage) {
if (storage.canBeConnected() || (addingLinkedSelf && finalFirst)) {
if (storage instanceof ILinkable linkable && linkable.isLinked() && (!addingLinkedSelf || !finalFirst)) {
private void tryToConnectStorageAndAddPositionsToCheckAround(Set<BlockPos> positionsToCheck, boolean addingLinkedSelf, Set<BlockPos> positionsChecked, BlockPos posToCheck, boolean finalFirst, IControllerBoundable boundable) {
if (boundable.canBeConnected() || (addingLinkedSelf && finalFirst)) {
if (boundable instanceof ILinkable linkable && linkable.isLinked() && (!addingLinkedSelf || !finalFirst)) {
linkedBlocks.remove(posToCheck);
linkable.setNotLinked();
} else if (storage.hasStorageData()) {
} else if (boundable instanceof IControllableStorage storage && storage.hasStorageData()) {
addStorageData(posToCheck);
} else {
connectingBlocks.add(posToCheck);
storage.registerController(this);
if (boundable.canConnectStorages()) {
connectingBlocks.add(posToCheck);
} else {
nonConnectingBlocks.add(posToCheck);
}
boundable.registerController(this);
}
if (storage.canConnectStorages()) {
if (boundable.canConnectStorages()) {
addUncheckedPositionsAround(positionsToCheck, positionsChecked, posToCheck);
}
}
Expand All @@ -186,7 +193,7 @@ private void tryToConnectStorageAndAddPositionsToCheckAround(Set<BlockPos> posit
private void addUncheckedPositionsAround(Set<BlockPos> positionsToCheck, Set<BlockPos> positionsChecked, BlockPos currentPos) {
for (Direction dir : Direction.values()) {
BlockPos pos = currentPos.offset(dir.getNormal());
if (!positionsChecked.contains(pos) && ((!storagePositions.contains(pos) && !connectingBlocks.contains(pos)) || linkedBlocks.contains(pos)) && isWithinRange(pos)) {
if (!positionsChecked.contains(pos) && ((!storagePositions.contains(pos) && !connectingBlocks.contains(pos) && !nonConnectingBlocks.contains(pos)) || linkedBlocks.contains(pos)) && isWithinRange(pos)) {
positionsToCheck.add(pos);
}
}
Expand All @@ -204,7 +211,7 @@ public void addStorage(BlockPos storagePos) {
if (isWithinRange(storagePos)) {
HashSet<BlockPos> positionsToCheck = new LinkedHashSet<>();
positionsToCheck.add(storagePos);
searchAndAddStorages(positionsToCheck, false);
searchAndAddBoundables(positionsToCheck, false);
}
}

Expand Down Expand Up @@ -353,6 +360,12 @@ private void removeConnectingBlock(BlockPos storagePos) {
}
}

public void removeNonConnectingBlock(BlockPos storagePos) {
if (nonConnectingBlocks.remove(storagePos)) {
WorldHelper.getLoadedBlockEntity(level, storagePos, IControllerBoundable.class).ifPresent(IControllerBoundable::unregisterController);
}
}

private void removeStorageInventoryDataAndUnregisterController(BlockPos storagePos) {
if (!storagePositions.contains(storagePos)) {
return;
Expand Down Expand Up @@ -429,6 +442,7 @@ private void removeStorageMemorizedStacks(BlockPos storagePos) {
private void verifyStoragesConnected() {
HashSet<BlockPos> toVerify = new HashSet<>(storagePositions);
toVerify.addAll(connectingBlocks);
toVerify.addAll(nonConnectingBlocks);

Set<BlockPos> positionsToCheck = new HashSet<>();
for (Direction dir : Direction.values()) {
Expand All @@ -439,7 +453,7 @@ private void verifyStoragesConnected() {
}
Set<BlockPos> positionsChecked = new HashSet<>();

verifyDirectlyConnected(toVerify, positionsToCheck, positionsChecked);
verifyConnected(toVerify, positionsToCheck, positionsChecked);

linkedBlocks.forEach(linkedPosition -> WorldHelper.getBlockEntity(getLevel(), linkedPosition, ILinkable.class).ifPresent(l -> {
if (l.connectLinkedSelf() && toVerify.contains(linkedPosition)) {
Expand All @@ -452,22 +466,23 @@ private void verifyStoragesConnected() {
});
}));

verifyDirectlyConnected(toVerify, positionsToCheck, positionsChecked);
verifyConnected(toVerify, positionsToCheck, positionsChecked);

toVerify.forEach(storagePos -> {
removeConnectingBlock(storagePos);
removeNonConnectingBlock(storagePos);
removeStorageInventoryDataAndUnregisterController(storagePos);
});
}

private void verifyDirectlyConnected(HashSet<BlockPos> toVerify, Set<BlockPos> positionsToCheck, Set<BlockPos> positionsChecked) {
private void verifyConnected(HashSet<BlockPos> toVerify, Set<BlockPos> positionsToCheck, Set<BlockPos> positionsChecked) {
while (!positionsToCheck.isEmpty()) {
Iterator<BlockPos> it = positionsToCheck.iterator();
BlockPos posToCheck = it.next();
it.remove();

positionsChecked.add(posToCheck);
WorldHelper.getLoadedBlockEntity(level, posToCheck, IControllableStorage.class).ifPresent(h -> {
WorldHelper.getLoadedBlockEntity(level, posToCheck, IControllerBoundable.class).ifPresent(h -> {
toVerify.remove(posToCheck);
if (h.canConnectStorages()) {
for (Direction dir : Direction.values()) {
Expand Down Expand Up @@ -500,10 +515,17 @@ protected ControllerBlockEntityBase(BlockEntityType<?> blockEntityType, BlockPos
@Override
public <T> LazyOptional<T> getCapability(Capability<T> cap, @Nullable Direction side) {
if (cap == ForgeCapabilities.ITEM_HANDLER) {
if (itemHandlerCap == null) {
itemHandlerCap = LazyOptional.of(() -> new CachedFailedInsertInventoryHandler(() -> this, () -> level != null ? level.getGameTime() : 0));
if (side == null) {
if (noSideItemHandlerCap == null) {
noSideItemHandlerCap = LazyOptional.of(() -> this).cast();
}
return noSideItemHandlerCap.cast();
} else {
if (itemHandlerCap == null) {
itemHandlerCap = LazyOptional.of(() -> new CachedFailedInsertInventoryHandler(() -> this, () -> level != null ? level.getGameTime() : 0));
}
return itemHandlerCap.cast();
}
return itemHandlerCap.cast();
}
return super.getCapability(cap, side);
}
Expand All @@ -515,6 +537,10 @@ public void invalidateCaps() {
itemHandlerCap.invalidate();
itemHandlerCap = null;
}
if (noSideItemHandlerCap != null) {
noSideItemHandlerCap.invalidate();
noSideItemHandlerCap = null;
}
}

@Override
Expand Down Expand Up @@ -591,6 +617,11 @@ public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
return insertItem(stack, simulate, true);
}

@Override
public ItemStack insertItem(ItemStack stack, boolean simulate) {
return insertItem(stack, simulate, true);
}

protected ItemStack insertItem(ItemStack stack, boolean simulate, boolean insertIntoAnyEmpty) {
ItemStackKey stackKey = ItemStackKey.of(stack);
ItemStack remaining = stack;
Expand Down Expand Up @@ -740,6 +771,7 @@ public void onChunkUnloaded() {
public void detachFromStoragesAndUnlinkBlocks() {
storagePositions.forEach(pos -> WorldHelper.getLoadedBlockEntity(level, pos, IControllableStorage.class).ifPresent(IControllableStorage::unregisterController));
connectingBlocks.forEach(pos -> WorldHelper.getLoadedBlockEntity(level, pos, IControllableStorage.class).ifPresent(IControllableStorage::unregisterController));
nonConnectingBlocks.forEach(pos -> WorldHelper.getLoadedBlockEntity(level, pos, IControllerBoundable.class).ifPresent(IControllerBoundable::unregisterController));
new HashSet<>(linkedBlocks).forEach(linkedPos -> WorldHelper.getLoadedBlockEntity(level, linkedPos, ILinkable.class).ifPresent(ILinkable::unlinkFromController)); //copying into new hashset to prevent CME when these are removed
}

Expand All @@ -753,6 +785,7 @@ protected void saveAdditional(CompoundTag tag) {
private CompoundTag saveData(CompoundTag tag) {
NBTHelper.putList(tag, "storagePositions", storagePositions, p -> LongTag.valueOf(p.asLong()));
NBTHelper.putList(tag, "connectingBlocks", connectingBlocks, p -> LongTag.valueOf(p.asLong()));
NBTHelper.putList(tag, "nonConnectingBlocks", nonConnectingBlocks, p -> LongTag.valueOf(p.asLong()));
NBTHelper.putList(tag, "linkedBlocks", linkedBlocks, p -> LongTag.valueOf(p.asLong()));
NBTHelper.putList(tag, "baseIndexes", baseIndexes, IntTag::valueOf);
tag.putInt("totalSlots", totalSlots);
Expand All @@ -766,6 +799,7 @@ public void load(CompoundTag tag) {

storagePositions = NBTHelper.getCollection(tag, "storagePositions", Tag.TAG_LONG, t -> Optional.of(BlockPos.of(((LongTag) t).getAsLong())), ArrayList::new).orElseGet(ArrayList::new);
connectingBlocks = NBTHelper.getCollection(tag, "connectingBlocks", Tag.TAG_LONG, t -> Optional.of(BlockPos.of(((LongTag) t).getAsLong())), LinkedHashSet::new).orElseGet(LinkedHashSet::new);
nonConnectingBlocks = NBTHelper.getCollection(tag, "nonConnectingBlocks", Tag.TAG_LONG, t -> Optional.of(BlockPos.of(((LongTag) t).getAsLong())), LinkedHashSet::new).orElseGet(LinkedHashSet::new);
baseIndexes = NBTHelper.getCollection(tag, "baseIndexes", Tag.TAG_INT, t -> Optional.of(((IntTag) t).getAsInt()), ArrayList::new).orElseGet(ArrayList::new);
totalSlots = tag.getInt("totalSlots");
linkedBlocks = NBTHelper.getCollection(tag, "linkedBlocks", Tag.TAG_LONG, t -> Optional.of(BlockPos.of(((LongTag) t).getAsLong())), LinkedHashSet::new).orElseGet(LinkedHashSet::new);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.p3pp3rf1y.sophisticatedcore.controller;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.Level;
import net.p3pp3rf1y.sophisticatedcore.api.IStorageWrapper;
import net.p3pp3rf1y.sophisticatedcore.settings.memory.MemorySettingsCategory;
Expand All @@ -11,16 +10,11 @@ public interface IControllableStorage extends IControllerBoundable {

IStorageWrapper getStorageWrapper();

default boolean canBeConnected() {
return getControllerPos().isEmpty();
}

default boolean hasStorageData() {
default boolean canConnectStorages() {
return true;
}

@Override
default boolean canConnectStorages() {
default boolean hasStorageData() {
return true;
}

Expand All @@ -36,28 +30,8 @@ default void removeFromController() {
}
}

private void addToAdjacentController() {
Level level = getStorageBlockLevel();
if (!level.isClientSide()) {
BlockPos pos = getStorageBlockPos();
for (Direction dir : Direction.values()) {
BlockPos offsetPos = pos.offset(dir.getNormal());
WorldHelper.getBlockEntity(level, offsetPos, IControllerBoundable.class).ifPresentOrElse(
s -> {
if (s.canConnectStorages()) {
s.getControllerPos().ifPresent(controllerPos -> addToController(level, pos, controllerPos));
}
},
() -> addToController(level, pos, offsetPos)
);
if (getControllerPos().isPresent()) {
break;
}
}
}
}

private void addToController(Level level, BlockPos pos, BlockPos controllerPos) {
@Override
default void addToController(Level level, BlockPos pos, BlockPos controllerPos) {
WorldHelper.getBlockEntity(level, controllerPos, ControllerBlockEntityBase.class).ifPresent(c -> c.addStorage(pos));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.p3pp3rf1y.sophisticatedcore.controller;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.Level;
import net.p3pp3rf1y.sophisticatedcore.util.NBTHelper;
Expand All @@ -22,6 +23,13 @@ public interface IControllerBoundable {

Level getStorageBlockLevel();

default boolean canBeConnected() {
return getControllerPos().isEmpty();
}

void registerController(ControllerBlockEntityBase controllerBlockEntity);
void unregisterController();

boolean canConnectStorages();

default void runOnController(Level level, Consumer<ControllerBlockEntityBase> toRun) {
Expand All @@ -38,4 +46,29 @@ default void loadControllerPos(CompoundTag tag) {
setControllerPos(controllerPos);
});
}

default void addToController(Level level, BlockPos pos, BlockPos controllerPos) {
//noop by default
}

default void addToAdjacentController() {
Level level = getStorageBlockLevel();
if (!level.isClientSide()) {
BlockPos pos = getStorageBlockPos();
for (Direction dir : Direction.values()) {
BlockPos offsetPos = pos.offset(dir.getNormal());
WorldHelper.getBlockEntity(level, offsetPos, IControllerBoundable.class).ifPresentOrElse(
s -> {
if (s.canConnectStorages()) {
s.getControllerPos().ifPresent(controllerPos -> addToController(level, pos, controllerPos));
}
},
() -> addToController(level, pos, offsetPos)
);
if (getControllerPos().isPresent()) {
break;
}
}
}
}
}

0 comments on commit c5ffb7a

Please sign in to comment.