Skip to content

Commit 006e17a

Browse files
author
passplease
committed
Fix bugs, and add new function
1 parent 86e98a3 commit 006e17a

File tree

9 files changed

+269
-57
lines changed

9 files changed

+269
-57
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ At the early developing time, there is some problems, I will fix that in the lat
99
- [ ] Internal missing slot draw.
1010
## Planning
1111
- [x] Allow set different package address: add categories (name starts with "#") as add a normal category. When you request a recipe which output is filtered in one category, the address box will be automatically filled with category name ("#" be deleted)
12-
- [ ] Different package address depending on recipe type such as iron ore send to furnace automatically.
12+
- [x] Different package address depending on recipe type such as iron ore send to furnace automatically.
1313
## License
1414
[MIT_License](https://mit-license.org/)
15+
## What it could do
16+
- Request recipe ingredient from EMI, including recipe tree or craft button. This will search in stock looking for lost items.
17+
- Send items automatically (could turn off in config)
18+
- Copy and set stock filter config by using clipboard (just for newly placed stock, and with no network setting).
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.emiforcreatestock;
2+
3+
public @interface ForMoreParameter {
4+
Class<?>[] usingClass();
5+
6+
String[] dataFrom() default "";
7+
8+
String reason() default "";
9+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.emiforcreatestock;
2+
3+
public class ForMoreParameters {
4+
public static final long playerNeedCountDefault = -1;
5+
@ForMoreParameter(usingClass = StockRequestHandler.class,dataFrom = "EmiScreenManagerMixin")
6+
public static long playerNeedCount;
7+
}

src/main/java/com/emiforcreatestock/Mixin/CategoryEntryMixin.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,4 @@
88
public interface CategoryEntryMixin {
99
@Accessor("name")
1010
String getName();
11-
12-
@Accessor("y")
13-
void setY(int y);
14-
15-
@Accessor("y")
16-
int getY();
1711
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.emiforcreatestock.Mixin;
2+
3+
import com.emiforcreatestock.ForMoreParameter;
4+
import com.emiforcreatestock.ForMoreParameters;
5+
import com.emiforcreatestock.StockRequestHandler;
6+
import com.llamalad7.mixinextras.sugar.Local;
7+
import dev.emi.emi.api.recipe.EmiRecipe;
8+
import dev.emi.emi.api.stack.EmiIngredient;
9+
import dev.emi.emi.api.stack.EmiStackInteraction;
10+
import dev.emi.emi.input.EmiBind;
11+
import dev.emi.emi.runtime.EmiFavorite;
12+
import dev.emi.emi.screen.EmiScreenManager;
13+
import org.spongepowered.asm.mixin.Mixin;
14+
import org.spongepowered.asm.mixin.injection.At;
15+
import org.spongepowered.asm.mixin.injection.Inject;
16+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
17+
18+
import java.util.function.Function;
19+
import java.util.function.Supplier;
20+
21+
@Mixin(EmiScreenManager.class)
22+
public class EmiScreenManagerMixin {
23+
@ForMoreParameter(usingClass = StockRequestHandler.class,reason = "OrElse EMI gives bad recipe amount, and causes request count wrong (Problems caused by inability to divide evenly)")
24+
@Inject(method = "craftInteraction",at = @At(value = "INVOKE", target = "Ldev/emi/emi/runtime/EmiFavorite$Synthetic;getRecipe()Ldev/emi/emi/api/recipe/EmiRecipe;"))
25+
private static void setRecipe(EmiIngredient ingredient, Supplier<EmiRecipe> contextSupplier, EmiStackInteraction stack, Function<EmiBind, Boolean> function, CallbackInfoReturnable<Boolean> cir, @Local EmiFavorite.Synthetic syn) {
26+
ForMoreParameters.playerNeedCount = syn.total;
27+
}
28+
29+
@ForMoreParameter(usingClass = StockRequestHandler.class)
30+
@Inject(method = "craftInteraction",at = @At(value = "RETURN"))
31+
private static void clearData(EmiIngredient ingredient, Supplier<EmiRecipe> contextSupplier, EmiStackInteraction stack, Function<EmiBind, Boolean> function, CallbackInfoReturnable<Boolean> cir) {
32+
ForMoreParameters.playerNeedCount = ForMoreParameters.playerNeedCountDefault;
33+
}
34+
}
Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.emiforcreatestock.Mixin;
22

3+
import com.emiforcreatestock.StockCloneBehaviour;
34
import com.emiforcreatestock.StockRequestHandler;
45
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
56
import com.llamalad7.mixinextras.sugar.Local;
@@ -8,47 +9,67 @@
89
import com.simibubi.create.content.logistics.filter.FilterItemStack;
910
import com.simibubi.create.content.logistics.stockTicker.StockCheckingBlockEntity;
1011
import com.simibubi.create.content.logistics.stockTicker.StockTickerBlockEntity;
12+
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
1113
import net.minecraft.core.BlockPos;
1214
import net.minecraft.world.item.ItemStack;
1315
import net.minecraft.world.level.block.entity.BlockEntityType;
1416
import net.minecraft.world.level.block.state.BlockState;
1517
import org.spongepowered.asm.mixin.Mixin;
1618
import org.spongepowered.asm.mixin.Shadow;
19+
import org.spongepowered.asm.mixin.gen.Accessor;
1720
import org.spongepowered.asm.mixin.injection.At;
1821
import org.spongepowered.asm.mixin.injection.Inject;
1922
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
2023

2124
import java.util.ArrayList;
22-
import java.util.Iterator;
2325
import java.util.List;
2426

2527
@Mixin(StockTickerBlockEntity.class)
26-
public abstract class StockTickerBlockEntityMixin extends StockCheckingBlockEntity implements IHaveHoveringInformation {
27-
@Shadow protected List<ItemStack> categories;
28+
public interface StockTickerBlockEntityMixin {
29+
@Accessor("lastClientsideStockSnapshot")
30+
List<List<BigItemStack>> getLastClientsideStockSnapshot();
2831

29-
@Shadow protected List<List<BigItemStack>> lastClientsideStockSnapshot;
32+
@Accessor("categories")
33+
List<ItemStack> getCategories();
3034

31-
public StockTickerBlockEntityMixin(BlockEntityType<?> type, BlockPos pos, BlockState state) {
32-
super(type, pos, state);
33-
}
35+
@Accessor("activeLinks")
36+
int getActiveLinks();
37+
38+
@Mixin(StockTickerBlockEntity.class)
39+
abstract class StockTickerBlockEntityMixin_ extends StockCheckingBlockEntity implements IHaveHoveringInformation {
40+
@Shadow
41+
protected List<ItemStack> categories;
42+
43+
@Shadow protected List<List<BigItemStack>> lastClientsideStockSnapshot;
44+
45+
public StockTickerBlockEntityMixin_(BlockEntityType<?> type, BlockPos pos, BlockState state) {
46+
super(type, pos, state);
47+
}
3448

35-
@Inject(method = "receiveStockPacket",at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z",ordinal = 2))
36-
public void initCraftCategories(List<BigItemStack> stacks, boolean endOfTransmission, CallbackInfo ci){
37-
for (int index = 0; index < categories.size(); index++) {
38-
ItemStack filter = categories.get(index);
39-
if(!filter.isEmpty() && StockRequestHandler.forAddress(filter.getHoverName().getString())){
40-
List<BigItemStack> inCategory = new ArrayList<>();
41-
FilterItemStack filterItemStack = FilterItemStack.of(filter);
42-
if(filterItemStack.isFilterItem()){
43-
inCategory.add(new BigItemStack(filterItemStack.item()));
44-
lastClientsideStockSnapshot.set(index, inCategory);
49+
@Inject(method = "receiveStockPacket",at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z",ordinal = 2))
50+
public void initCraftCategories(List<BigItemStack> stacks, boolean endOfTransmission, CallbackInfo ci){
51+
for (int index = 0; index < categories.size(); index++) {
52+
ItemStack filter = categories.get(index);
53+
if(!filter.isEmpty() && StockRequestHandler.forAddress(filter.getHoverName().getString())){
54+
List<BigItemStack> inCategory = new ArrayList<>();
55+
FilterItemStack filterItemStack = FilterItemStack.of(filter);
56+
if(filterItemStack.isFilterItem()){
57+
inCategory.add(new BigItemStack(filterItemStack.item()));
58+
lastClientsideStockSnapshot.set(index, inCategory);
59+
}
4560
}
4661
}
4762
}
48-
}
4963

50-
@ModifyExpressionValue(method = "receiveStockPacket",at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z"))
51-
public boolean test(boolean original, @Local ItemStack filter){
52-
return original || StockRequestHandler.forAddress(filter.getHoverName().getString());
64+
@ModifyExpressionValue(method = "receiveStockPacket",at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;isEmpty()Z"))
65+
public boolean test(boolean original, @Local ItemStack filter){
66+
return original || StockRequestHandler.forAddress(filter.getHoverName().getString());
67+
}
68+
69+
@Override
70+
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
71+
super.addBehaviours(behaviours);
72+
behaviours.add(new StockCloneBehaviour(this));
73+
}
5374
}
5475
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
package com.emiforcreatestock;
2+
3+
import com.emiforcreatestock.Mixin.StockTickerBlockEntityMixin;
4+
import com.simibubi.create.content.equipment.clipboard.ClipboardCloneable;
5+
import com.simibubi.create.content.logistics.filter.FilterItem;
6+
import com.simibubi.create.content.logistics.stockTicker.StockTickerBlockEntity;
7+
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
8+
import com.simibubi.create.foundation.blockEntity.behaviour.BehaviourType;
9+
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
10+
import net.createmod.catnip.nbt.NBTHelper;
11+
import net.minecraft.core.Direction;
12+
import net.minecraft.core.HolderLookup;
13+
import net.minecraft.core.registries.BuiltInRegistries;
14+
import net.minecraft.nbt.CompoundTag;
15+
import net.minecraft.nbt.ListTag;
16+
import net.minecraft.world.entity.player.Inventory;
17+
import net.minecraft.world.entity.player.Player;
18+
import net.minecraft.world.item.Item;
19+
import net.minecraft.world.item.ItemStack;
20+
import org.jetbrains.annotations.NotNull;
21+
22+
import java.util.*;
23+
24+
public class StockCloneBehaviour extends BlockEntityBehaviour implements ClipboardCloneable {
25+
public static final String KEY = "StockCloneBehaviour";
26+
27+
public static final BehaviourType<StockCloneBehaviour> TYPE = new BehaviourType<>();
28+
29+
private static final Map<FilterItem,Integer> FILTERS = new HashMap<>();
30+
31+
static {
32+
List<Item> list = BuiltInRegistries.ITEM.stream().filter(FilterItem.class::isInstance).toList();
33+
list.forEach(item -> FILTERS.put((FilterItem) item,0));
34+
}
35+
36+
public StockCloneBehaviour(SmartBlockEntity be) {
37+
super(be);
38+
}
39+
40+
@Override
41+
public String getClipboardKey() {
42+
return KEY;
43+
}
44+
45+
@Override
46+
public boolean writeToClipboard(HolderLookup.@NotNull Provider provider, CompoundTag tag, Direction direction) {
47+
CompoundTag nbt = new CompoundTag();
48+
getBlockEntity().saveAdditional(nbt,provider);
49+
tag.put(KEY, Objects.requireNonNull(nbt));
50+
return true;
51+
}
52+
53+
@Override
54+
public boolean readFromClipboard(HolderLookup.@NotNull Provider provider, CompoundTag tag, Player player, Direction direction, boolean simulate) {
55+
if(legalBlockEntity() && tag.contains(KEY)) {
56+
List<ItemStack> stacks = NBTHelper.readItemList((ListTag) tag.getCompound(KEY).get("Categories"), provider);
57+
List<Item> items = stacks.stream().map(ItemStack::getItem).toList();
58+
Map<FilterItem, Integer> needFilters = getFilters();
59+
Inventory inventory = player.getInventory();
60+
needFilters.forEach((filterItem, integer) -> needFilters.replace(filterItem, Math.toIntExact(items.stream().filter(filterItem::equals).count())));
61+
if (checkEnoughFilterItem(player, inventory, needFilters)) {
62+
if(!simulate) {
63+
getBlockEntity().loadCustomOnly(tag.getCompound(KEY), provider);
64+
getBlockEntity().notifyUpdate();
65+
removeFilterItem(player,inventory, needFilters);
66+
}
67+
return true;
68+
}
69+
}
70+
return false;
71+
}
72+
73+
protected boolean legalBlockEntity(){
74+
StockTickerBlockEntityMixin entity = (StockTickerBlockEntityMixin) getBlockEntity();
75+
return entity.getActiveLinks() == 0 && entity.getLastClientsideStockSnapshot() == null && entity.getCategories().isEmpty();// newly placed
76+
}
77+
78+
protected boolean checkEnoughFilterItem(Player player,Inventory inventory,Map<FilterItem, Integer> needFilters) {
79+
if(player.isCreative())
80+
return true;
81+
Map<FilterItem, Integer> existFilters = getFilters();
82+
existFilters.forEach((filterItem, integer) -> existFilters.replace(filterItem, inventory.countItem(filterItem)));
83+
boolean result = true;
84+
List<Integer> needCount = new ArrayList<>(needFilters.values());
85+
List<Integer> existCount = new ArrayList<>(existFilters.values());
86+
for (int index = 0; index < needCount.size(); index++) {
87+
result &= existCount.get(index) >= needCount.get(index);
88+
}
89+
return result;
90+
}
91+
92+
protected void removeFilterItem(Player player,Inventory inventory, Map<FilterItem, Integer> items) {
93+
if(player.isCreative())
94+
return;
95+
items.forEach((filterItem, integer) -> {
96+
for (int slot = 0; slot < inventory.getContainerSize(); slot++) {
97+
ItemStack stack = inventory.getItem(slot);
98+
if(stack.getItem() == filterItem) {
99+
if (stack.getCount() >= integer) {
100+
stack.shrink(integer);
101+
return;
102+
} else {
103+
integer -= stack.getCount();
104+
stack.setCount(0);
105+
}
106+
}
107+
}
108+
});
109+
inventory.setChanged();
110+
}
111+
112+
@Override
113+
public BehaviourType<?> getType() {
114+
return TYPE;
115+
}
116+
117+
public StockTickerBlockEntity getBlockEntity() {
118+
return (StockTickerBlockEntity) blockEntity;
119+
}
120+
121+
public static Map<FilterItem,Integer> getFilters() {
122+
return new HashMap<>(FILTERS);
123+
}
124+
}

src/main/java/com/emiforcreatestock/StockRequestHandler.java

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,36 +73,54 @@ public boolean craft(EmiRecipe recipe, EmiCraftContext<StockKeeperRequestMenu> c
7373
return StandardRecipeHandler.super.craft(recipe, context);
7474
}
7575

76+
protected boolean searchOutput(int requiredAmount,EmiPlayerInventory playerInventory,StockKeeperRequestScreen screen,EmiIngredient ingredient,@Nullable TriConsumer<EmiStack,Long,@Nullable BigItemStack> action,boolean findSome){
77+
if(ForMoreParameters.playerNeedCount != ForMoreParameters.playerNeedCountDefault){
78+
for(EmiStack stack : ingredient.getEmiStacks()){
79+
if(searchSingleStack(requiredAmount, playerInventory, screen, action, findSome, stack, ForMoreParameters.playerNeedCount))
80+
return true;
81+
}
82+
}
83+
return false;
84+
}
85+
7686
protected boolean enoughIngredients(int requiredAmount,EmiPlayerInventory playerInventory,StockKeeperRequestScreen screen,EmiIngredient ingredient,@Nullable TriConsumer<EmiStack,Long,@Nullable BigItemStack> action,boolean findSome){
7787
if(ingredient.isEmpty())
7888
return true;
7989
for (EmiStack stack : ingredient.getEmiStacks()) {
8090
long amount = ingredient.getAmount() * requiredAmount;
81-
if (requiredAmount < Integer.MAX_VALUE && playerInventory.inventory.containsKey(stack)) {
82-
amount -= playerInventory.inventory.get(stack).getAmount();
83-
if (amount <= 0) {
84-
if(action != null)
85-
action.accept(stack, amount,null);
86-
return true;
87-
}else if(findSome && action != null)
91+
return searchSingleStack(requiredAmount, playerInventory, screen, action, findSome, stack, amount);
92+
}
93+
return requiredAmount == Integer.MAX_VALUE;
94+
}
95+
96+
protected boolean searchSingleStack(int requiredAmount, EmiPlayerInventory playerInventory, StockKeeperRequestScreen screen, @Nullable TriConsumer<EmiStack, Long, @Nullable BigItemStack> action, boolean findSome, EmiStack stack, long amount) {
97+
if (requiredAmount < Integer.MAX_VALUE && playerInventory.inventory.containsKey(stack)) {
98+
long playerCount = playerInventory.inventory.get(stack).getAmount();
99+
if (amount <= playerCount) {
100+
if(action != null)
88101
action.accept(stack, amount,null);
102+
return true;
103+
}else {
104+
amount -= playerCount;
105+
if (findSome && action != null)
106+
action.accept(stack, playerCount, null);
89107
}
90-
for(List<BigItemStack> items : getItemSource(screen)){
91-
Optional<BigItemStack> optional = items.stream().filter(bigItemStack -> bigItemStack.stack.is(stack.getItemStack().getItem())).findFirst();
92-
if(optional.isPresent()){
93-
BigItemStack bigItemStack = optional.get();
94-
if(bigItemStack.count >= amount){
95-
if(action != null)
96-
action.accept(stack, amount,bigItemStack);
97-
return true;
98-
}else if(requiredAmount == Integer.MAX_VALUE){
99-
if(action != null)// Max extract 9 * 64
100-
action.accept(stack, Math.min(bigItemStack.stack.getMaxStackSize() * 9L,amount),bigItemStack);
101-
}else {// No duplicate items in different List
102-
if(findSome && action != null)
103-
action.accept(stack, (long) bigItemStack.count,bigItemStack);
104-
break;
105-
}
108+
}
109+
for(List<BigItemStack> items : getItemSource(screen)){
110+
Optional<BigItemStack> optional = items.stream().filter(bigItemStack -> bigItemStack.stack.is(stack.getItemStack().getItem())).findFirst();
111+
if(optional.isPresent()){
112+
BigItemStack bigItemStack = optional.get();
113+
if(bigItemStack.count >= amount){
114+
if(action != null)
115+
action.accept(stack, amount,bigItemStack);
116+
return true;
117+
}else if(requiredAmount == Integer.MAX_VALUE){
118+
if(action != null)// Max extract 9 * 64
119+
action.accept(stack, Math.min(bigItemStack.stack.getMaxStackSize() * 9L,amount),bigItemStack);
120+
}else {// No duplicate items in different List
121+
if(findSome && action != null)
122+
action.accept(stack, (long) bigItemStack.count,bigItemStack);
123+
break;
106124
}
107125
}
108126
}
@@ -116,9 +134,9 @@ protected boolean enoughIngredients(int requiredAmount,EmiPlayerInventory player
116134
protected boolean enoughIngredients(EmiRecipe recipe, StockKeeperRequestScreen screen, int craftTimes, EmiPlayerInventory playerInventory, @Nullable TriConsumer<EmiStack,Long,@Nullable BigItemStack> action){
117135
Map<EmiIngredient,Integer> foundIngredients = new HashMap<>();
118136
List<EmiStack> outputs = recipe.getOutputs();
119-
if(!outputs.isEmpty() && enoughIngredients(craftTimes,playerInventory,screen,outputs.getFirst(),(emiStack, amount, bigItemStack) -> {
137+
if(!outputs.isEmpty() && searchOutput(craftTimes,playerInventory,screen,outputs.getFirst(),(emiStack, amount, bigItemStack) -> {
120138
if(amount >= 0) {
121-
foundIngredients.put(outputs.getFirst(), Math.toIntExact(amount));
139+
foundIngredients.compute(outputs.getFirst(),(ingredient,count) -> count == null ? Math.toIntExact(amount) : count + Math.toIntExact(amount));
122140
if(action != null)
123141
action.accept(emiStack, amount,bigItemStack);
124142
}
@@ -194,8 +212,7 @@ protected void moveItems(EmiRecipe recipe,@NotNull StockKeeperRequestScreen scre
194212
return filter.test(screen.getMinecraft().level, output.getItemStack());
195213
}
196214
return false;
197-
}
198-
)) {
215+
})) {
199216
return getAvailableAddress(((CategoryEntryMixin) screen.categories.get(ordinary)).getName());
200217
}
201218
}

0 commit comments

Comments
 (0)