diff --git a/build.gradle b/build.gradle
index 13f98166918..57c7727d561 100644
--- a/build.gradle
+++ b/build.gradle
@@ -322,6 +322,7 @@ legacyForge {
}
client {
client()
+ jvmArguments.add("-XX:+AllowEnhancedClassRedefinition")
}
gametestWorld {
client()
diff --git a/src/generated/resources/assets/ae2/lang/en_us.json b/src/generated/resources/assets/ae2/lang/en_us.json
index 08d853ce93f..5827c79dd0b 100644
--- a/src/generated/resources/assets/ae2/lang/en_us.json
+++ b/src/generated/resources/assets/ae2/lang/en_us.json
@@ -963,6 +963,7 @@
"item.ae2.spatial_storage_cell_16": "16³ Spatial Storage Cell",
"item.ae2.spatial_storage_cell_2": "2³ Spatial Storage Cell",
"item.ae2.speed_card": "Acceleration Card",
+ "item.ae2.sticky_card": "Sticky Card",
"item.ae2.stonecutting_pattern": "Stonecutting Pattern",
"item.ae2.storage_bus": "ME Storage Bus",
"item.ae2.storage_monitor": "ME Storage Monitor",
diff --git a/src/generated/resources/assets/ae2/models/item/sticky_card.json b/src/generated/resources/assets/ae2/models/item/sticky_card.json
new file mode 100644
index 00000000000..e353f6e2a28
--- /dev/null
+++ b/src/generated/resources/assets/ae2/models/item/sticky_card.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:item/generated",
+ "textures": {
+ "layer0": "ae2:item/sticky_card"
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/appeng/api/ids/AEItemIds.java b/src/main/java/appeng/api/ids/AEItemIds.java
index 0fd6d65f31a..973fa4a7014 100644
--- a/src/main/java/appeng/api/ids/AEItemIds.java
+++ b/src/main/java/appeng/api/ids/AEItemIds.java
@@ -228,6 +228,7 @@ public final class AEItemIds {
public static final ResourceLocation ENERGY_CARD = id("energy_card");
public static final ResourceLocation EQUAL_DISTRIBUTION_CARD = id("equal_distribution_card");
public static final ResourceLocation AUTO_COMPLETE_CARD = id("auto_complete_card");
+ public static final ResourceLocation STICKY_CARD = id("sticky_card");
public static final ResourceLocation SPATIAL_2_CELL_COMPONENT = id("spatial_cell_component_2");
public static final ResourceLocation SPATIAL_16_CELL_COMPONENT = id("spatial_cell_component_16");
public static final ResourceLocation SPATIAL_128_CELL_COMPONENT = id("spatial_cell_component_128");
diff --git a/src/main/java/appeng/api/storage/MEStorage.java b/src/main/java/appeng/api/storage/MEStorage.java
index 65c6253ce81..51c59da8e01 100644
--- a/src/main/java/appeng/api/storage/MEStorage.java
+++ b/src/main/java/appeng/api/storage/MEStorage.java
@@ -34,6 +34,7 @@
import appeng.api.networking.security.IActionSource;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.KeyCounter;
+import org.jetbrains.annotations.Nullable;
/**
* AE's Equivalent to IInventory, used to reading contents, and manipulating contents of ME Inventories.
@@ -95,6 +96,22 @@ default long extract(AEKey what, long amount, Actionable mode, IActionSource sou
default void getAvailableStacks(KeyCounter out) {
}
+ /**
+ * If that key has any inventory marked as sticky it should only be able to insert into that one or other
+ * inventories that have it marked as sticky
+ *
+ * If for example a storage bus has a sticky card with a filter for stone, then on that network it should be the
+ * only place where stone can be stored.
+ *
+ * @apiNote Pass a null value to check if the inventory is sticky at all.
+ *
+ * @return Whether the specified stack is sticky for this inventory.
+ * @param what what to check.
+ */
+ default boolean isSticky(@Nullable AEKey what) {
+ return false;
+ }
+
/**
* @return The type of storage represented by this object.
*/
diff --git a/src/main/java/appeng/core/definitions/AEItems.java b/src/main/java/appeng/core/definitions/AEItems.java
index 9836ee62718..b46e9cce8f6 100644
--- a/src/main/java/appeng/core/definitions/AEItems.java
+++ b/src/main/java/appeng/core/definitions/AEItems.java
@@ -224,6 +224,7 @@ private static ItemDefinition makePortableFluidCell(ResourceLo
public static final ItemDefinition- EQUAL_DISTRIBUTION_CARD = item("Equal Distribution Card", AEItemIds.EQUAL_DISTRIBUTION_CARD, Upgrades::createUpgradeCardItem);
public static final ItemDefinition ENERGY_CARD = item("Energy Card", AEItemIds.ENERGY_CARD, p -> new EnergyCardItem(p, 1));
public static final ItemDefinition
- AUTO_COMPLETE_CARD = item("Auto Complete Card", AEItemIds.AUTO_COMPLETE_CARD, Upgrades::createUpgradeCardItem);
+ public static final ItemDefinition
- STICKY_CARD = item("Sticky Card", AEItemIds.STICKY_CARD, Upgrades::createUpgradeCardItem);
public static final ItemDefinition SPATIAL_2_CELL_COMPONENT = item("2³ Spatial Component", AEItemIds.SPATIAL_2_CELL_COMPONENT, MaterialItem::new);
public static final ItemDefinition SPATIAL_16_CELL_COMPONENT = item("16³ Spatial Component", AEItemIds.SPATIAL_16_CELL_COMPONENT, MaterialItem::new);
public static final ItemDefinition SPATIAL_128_CELL_COMPONENT = item("128³ Spatial Component", AEItemIds.SPATIAL_128_CELL_COMPONENT, MaterialItem::new);
diff --git a/src/main/java/appeng/datagen/providers/models/ItemModelProvider.java b/src/main/java/appeng/datagen/providers/models/ItemModelProvider.java
index 6f40e65a84b..59b6411cbf8 100644
--- a/src/main/java/appeng/datagen/providers/models/ItemModelProvider.java
+++ b/src/main/java/appeng/datagen/providers/models/ItemModelProvider.java
@@ -66,6 +66,7 @@ protected void registerModels() {
flatSingleLayer(AEItems.ENGINEERING_PROCESSOR_PRINT, "item/printed_engineering_processor");
flatSingleLayer(AEItems.EQUAL_DISTRIBUTION_CARD, "item/card_equal_distribution");
flatSingleLayer(AEItems.AUTO_COMPLETE_CARD, "item/auto_complete_card");
+ flatSingleLayer(AEItems.STICKY_CARD, "item/sticky_card");
storageCell(AEItems.FLUID_CELL_1K, "item/fluid_storage_cell_1k");
storageCell(AEItems.FLUID_CELL_4K, "item/fluid_storage_cell_4k");
storageCell(AEItems.FLUID_CELL_16K, "item/fluid_storage_cell_16k");
diff --git a/src/main/java/appeng/init/internal/InitUpgrades.java b/src/main/java/appeng/init/internal/InitUpgrades.java
index 0b5940909a3..242068ece02 100644
--- a/src/main/java/appeng/init/internal/InitUpgrades.java
+++ b/src/main/java/appeng/init/internal/InitUpgrades.java
@@ -82,6 +82,7 @@ public static void init() {
Upgrades.add(AEItems.INVERTER_CARD, itemCell, 1, storageCellGroup);
Upgrades.add(AEItems.EQUAL_DISTRIBUTION_CARD, itemCell, 1, storageCellGroup);
Upgrades.add(AEItems.VOID_CARD, itemCell, 1, storageCellGroup);
+ Upgrades.add(AEItems.STICKY_CARD, itemCell, 1, storageCellGroup);
}
var fluidCells = List.of(
@@ -91,6 +92,7 @@ public static void init() {
Upgrades.add(AEItems.INVERTER_CARD, fluidCell, 1, storageCellGroup);
Upgrades.add(AEItems.EQUAL_DISTRIBUTION_CARD, fluidCell, 1, storageCellGroup);
Upgrades.add(AEItems.VOID_CARD, fluidCell, 1, storageCellGroup);
+ Upgrades.add(AEItems.STICKY_CARD, fluidCell, 1, storageCellGroup);
}
var portableCells = List.of(
@@ -127,6 +129,7 @@ public static void init() {
Upgrades.add(AEItems.INVERTER_CARD, AEParts.STORAGE_BUS, 1);
Upgrades.add(AEItems.CAPACITY_CARD, AEParts.STORAGE_BUS, 5);
Upgrades.add(AEItems.VOID_CARD, AEParts.STORAGE_BUS, 1);
+ Upgrades.add(AEItems.STICKY_CARD, AEParts.STORAGE_BUS, 1);
// Formation Plane
Upgrades.add(AEItems.FUZZY_CARD, AEParts.FORMATION_PLANE, 1);
diff --git a/src/main/java/appeng/me/cells/BasicCellInventory.java b/src/main/java/appeng/me/cells/BasicCellInventory.java
index 8509ac34c35..502e4bd5c64 100644
--- a/src/main/java/appeng/me/cells/BasicCellInventory.java
+++ b/src/main/java/appeng/me/cells/BasicCellInventory.java
@@ -463,4 +463,10 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour
public Component getDescription() {
return i.getHoverName();
}
+
+ @Override
+ public boolean isSticky(AEKey what) {
+ return getUpgradesInventory().isInstalled(AEItems.STICKY_CARD)
+ && (what == null || partitionList.matchesFilter(what, this.partitionListMode));
+ }
}
diff --git a/src/main/java/appeng/me/storage/DriveWatcher.java b/src/main/java/appeng/me/storage/DriveWatcher.java
index 148073e178e..89c83da5846 100644
--- a/src/main/java/appeng/me/storage/DriveWatcher.java
+++ b/src/main/java/appeng/me/storage/DriveWatcher.java
@@ -28,9 +28,11 @@ public class DriveWatcher extends MEInventoryHandler {
private CellState oldStatus = CellState.EMPTY;
private final Runnable activityCallback;
+ private final StorageCell cell;
public DriveWatcher(StorageCell i, Runnable activityCallback) {
super(i);
+ this.cell = i;
this.activityCallback = activityCallback;
this.oldStatus = getStatus();
}
@@ -74,4 +76,9 @@ public long extract(AEKey what, long amount, Actionable mode, IActionSource sour
return extracted;
}
+
+ @Override
+ public boolean isSticky(AEKey what) {
+ return cell.isSticky(what);
+ }
}
diff --git a/src/main/java/appeng/me/storage/NetworkStorage.java b/src/main/java/appeng/me/storage/NetworkStorage.java
index 9e6d61d0595..8f88cb2cff5 100644
--- a/src/main/java/appeng/me/storage/NetworkStorage.java
+++ b/src/main/java/appeng/me/storage/NetworkStorage.java
@@ -48,6 +48,7 @@ public class NetworkStorage implements MEStorage {
private boolean mountsInUse;
private final NavigableMap> priorityInventory;
+ private final NavigableMap> priorityStickyInventory;
private final List secondPassInventories = new ArrayList<>();
// Queued mount/unmount operations that occurred while an insert/extract was ongoing
@@ -57,6 +58,7 @@ public class NetworkStorage implements MEStorage {
public NetworkStorage() {
this.priorityInventory = new TreeMap<>(PRIORITY_SORTER);
+ this.priorityStickyInventory = new TreeMap<>(PRIORITY_SORTER);
}
public void mount(int priority, MEStorage inventory) {
@@ -66,8 +68,13 @@ public void mount(int priority, MEStorage inventory) {
}
queuedOperations.add(new MountOperation(priority, inventory));
} else {
- this.priorityInventory.computeIfAbsent(priority, k -> new ArrayList<>())
- .add(inventory);
+ if (!inventory.isSticky(null)) {
+ this.priorityInventory.computeIfAbsent(priority, k -> new ArrayList<>())
+ .add(inventory);
+ } else {
+ this.priorityStickyInventory.computeIfAbsent(priority, k -> new ArrayList<>())
+ .add(inventory);
+ }
}
}
@@ -96,14 +103,25 @@ public long insert(AEKey what, long amount, Actionable type, IActionSource src)
}
var remaining = amount;
+ boolean hasSticky = false;
mountsInUse = true;
try {
+ // first, we need to check if any inventory has this key marked as sticky
+ for (var invList : this.priorityStickyInventory.values()) {
+ for (var inv : invList) {
+ if (inv.isSticky(what)) {
+ hasSticky = true;
+ remaining -= inv.insert(what, remaining, type, src);
+ }
+ }
+ }
+
for (var invList : this.priorityInventory.values()) {
secondPassInventories.clear();
- // First give every inventory a chance to accept the item if it's preferential storage for the given
- // stack
+ // then we give every inventory a chance to accept the item if it's preferential storage for the given
+ // stack and taking into account its sticky status
var ii = invList.iterator();
while (ii.hasNext() && remaining > 0) {
var inv = ii.next();
@@ -112,6 +130,11 @@ public long insert(AEKey what, long amount, Actionable type, IActionSource src)
continue;
}
+ // skip this inventory if we're looking for a sticky one and it is not
+ if (hasSticky && !inv.isSticky(what)) {
+ continue;
+ }
+
if (inv.isPreferredStorageFor(what, src)) {
remaining -= inv.insert(what, remaining, type, src);
} else {
diff --git a/src/main/java/appeng/parts/storagebus/StorageBusPart.java b/src/main/java/appeng/parts/storagebus/StorageBusPart.java
index 4bef0305a3c..dfe1a67e532 100644
--- a/src/main/java/appeng/parts/storagebus/StorageBusPart.java
+++ b/src/main/java/appeng/parts/storagebus/StorageBusPart.java
@@ -55,6 +55,7 @@
import appeng.api.parts.IPartHost;
import appeng.api.parts.IPartItem;
import appeng.api.parts.IPartModel;
+import appeng.api.stacks.AEKey;
import appeng.api.stacks.AEKeyType;
import appeng.api.storage.IStorageMounts;
import appeng.api.storage.IStorageProvider;
@@ -456,7 +457,7 @@ public final void setPriority(int newValue) {
/**
* This inventory forwards to the actual external inventory and allows the inventory to be swapped out underneath.
*/
- private static class StorageBusInventory extends MEInventoryHandler {
+ private class StorageBusInventory extends MEInventoryHandler {
public StorageBusInventory(MEStorage inventory) {
super(inventory);
}
@@ -475,6 +476,12 @@ public void setAccessRestriction(AccessRestriction setting) {
setAllowExtraction(setting.isAllowExtraction());
setAllowInsertion(setting.isAllowInsertion());
}
+
+ @Override
+ public boolean isSticky(AEKey what) {
+ return isUpgradedWith(AEItems.STICKY_CARD)
+ && (what == null || this.getPartitionList().matchesFilter(what, this.getWhitelist()));
+ }
}
@Override
diff --git a/src/main/resources/assets/ae2/textures/item/sticky_card.png b/src/main/resources/assets/ae2/textures/item/sticky_card.png
new file mode 100644
index 00000000000..e9797729cc6
Binary files /dev/null and b/src/main/resources/assets/ae2/textures/item/sticky_card.png differ