mirror of
https://github.com/LeavesMC/Leaves.git
synced 2025-12-19 14:59:32 +00:00
feat: finish MOVE_ITEM_NEW_PACKET, fix rei protocol packet transformer (#615)
This commit is contained in:
@@ -37,6 +37,7 @@ public class PacketTransformer {
|
||||
public void inbound(ResourceLocation id, RegistryFriendlyByteBuf buf, ServerPlayer player, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
UUID key = player.getUUID();
|
||||
PartData data;
|
||||
buf.readVarInt();
|
||||
switch (buf.readByte()) {
|
||||
case START -> {
|
||||
int partsNum = buf.readInt();
|
||||
|
||||
@@ -5,6 +5,11 @@ import com.google.common.collect.ImmutableMap;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@@ -46,8 +51,15 @@ import org.leavesmc.leaves.protocol.rei.display.SmeltingDisplay;
|
||||
import org.leavesmc.leaves.protocol.rei.display.SmokingDisplay;
|
||||
import org.leavesmc.leaves.protocol.rei.display.StoneCuttingDisplay;
|
||||
import org.leavesmc.leaves.protocol.rei.payload.DisplaySyncPayload;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.InputSlotCrafter;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.NewInputSlotCrafter;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.PlayerInventorySlotAccessor;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.VanillaSlotAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
@@ -63,9 +75,11 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
public static final String CHEAT_PERMISSION = "leaves.protocol.rei.cheat";
|
||||
public static final ResourceLocation DELETE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "delete_item");
|
||||
public static final ResourceLocation CREATE_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item");
|
||||
public static final ResourceLocation CREATE_ITEMS_GRAB_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_grab");
|
||||
public static final ResourceLocation CREATE_ITEMS_HOTBAR_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_hotbar");
|
||||
public static final ResourceLocation CREATE_ITEMS_GRAB_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "create_item_grab");
|
||||
public static final ResourceLocation CREATE_ITEMS_MESSAGE_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "ci_msg");
|
||||
public static final ResourceLocation MOVE_ITEMS_NEW_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "move_items_new");
|
||||
public static final ResourceLocation NOT_ENOUGH_ITEMS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "og_not_enough"); // this pack is under to-do at rei-client, so we don't handle it
|
||||
public static final ResourceLocation SYNC_DISPLAYS_PACKET = ResourceLocation.fromNamespaceAndPath("roughlyenoughitems", "sync_displays");
|
||||
|
||||
public static final Map<ResourceLocation, PacketTransformer> TRANSFORMERS = Util.make(() -> {
|
||||
@@ -75,6 +89,7 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
builder.put(CREATE_ITEMS_PACKET, new PacketTransformer());
|
||||
builder.put(CREATE_ITEMS_GRAB_PACKET, new PacketTransformer());
|
||||
builder.put(CREATE_ITEMS_HOTBAR_PACKET, new PacketTransformer());
|
||||
builder.put(MOVE_ITEMS_NEW_PACKET, new PacketTransformer());
|
||||
return builder.build();
|
||||
});
|
||||
private static final Set<ServerPlayer> enabledPlayers = new HashSet<>();
|
||||
@@ -294,7 +309,48 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
|
||||
@ProtocolHandler.BytebufReceiver(key = "move_items_new")
|
||||
public static void handleMoveItem(ServerPlayer player, RegistryFriendlyByteBuf buf) {
|
||||
// TODO handle to disable REI client warning
|
||||
BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer = (ignored, c2sWholeBuf) -> {
|
||||
FriendlyByteBuf tmpBuf = new FriendlyByteBuf(Unpooled.buffer()).writeBytes(c2sWholeBuf.readByteArray());
|
||||
AbstractContainerMenu container = player.containerMenu;
|
||||
tmpBuf.readResourceLocation();
|
||||
try {
|
||||
boolean shift = tmpBuf.readBoolean();
|
||||
try {
|
||||
CompoundTag nbt = tmpBuf.readNbt();
|
||||
if (nbt == null) {
|
||||
throw new IllegalStateException("NBT data is null");
|
||||
}
|
||||
int version = nbt.getInt("Version").orElse(-1);
|
||||
if (version != 1) {
|
||||
throw new IllegalStateException("Server and client REI protocol version mismatch! Server: 1, Client: " + version);
|
||||
}
|
||||
|
||||
List<List<ItemStack>> recipes = readInputs(player.registryAccess(), nbt.getListOrEmpty("Inputs"));
|
||||
List<SlotAccessor> input = readSlots(container, player, nbt.getListOrEmpty("InputSlots"));
|
||||
List<SlotAccessor> inventory = readSlots(container, player, nbt.getListOrEmpty("InventorySlots"));
|
||||
NewInputSlotCrafter<AbstractContainerMenu> crafter = new NewInputSlotCrafter<>(container, input, inventory, recipes);
|
||||
Bukkit.getScheduler().runTask(MinecraftInternalPlugin.INSTANCE, () -> {
|
||||
try {
|
||||
crafter.fillInputSlots(player, shift);
|
||||
} catch (InputSlotCrafter.NotEnoughMaterialsException ignored1) {
|
||||
} catch (IllegalStateException e) {
|
||||
player.sendSystemMessage(Component.translatable(e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
player.sendSystemMessage(Component.translatable("error.rei.internal.error", e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (IllegalStateException e) {
|
||||
player.sendSystemMessage(Component.translatable(e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
} catch (Exception e) {
|
||||
player.sendSystemMessage(Component.translatable("error.rei.internal.error", e.getMessage()).withStyle(ChatFormatting.RED));
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
};
|
||||
inboundTransform(player, MOVE_ITEMS_NEW_PACKET, buf, consumer);
|
||||
}
|
||||
|
||||
private static void inboundTransform(ServerPlayer player, ResourceLocation id, RegistryFriendlyByteBuf buf, BiConsumer<ResourceLocation, RegistryFriendlyByteBuf> consumer) {
|
||||
@@ -332,4 +388,44 @@ public class REIServerProtocol implements LeavesProtocol {
|
||||
public int tickerInterval(String tickerID) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
private static List<List<ItemStack>> readInputs(RegistryAccess registryAccess, ListTag tag) {
|
||||
List<List<ItemStack>> items = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
compoundTag.getInt("Index").orElseThrow();
|
||||
ListTag ingredientList = compoundTag.getListOrEmpty("Ingredient");
|
||||
List<ItemStack> slotItems = new ArrayList<>();
|
||||
for (Tag ingredient : ingredientList) {
|
||||
CompoundTag ingredientTag = (CompoundTag) ingredient;
|
||||
ItemStack stack = ItemStack.OPTIONAL_CODEC.parse(
|
||||
registryAccess.createSerializationContext(NbtOps.INSTANCE),
|
||||
ingredientTag.get("value")
|
||||
).getOrThrow();
|
||||
slotItems.add(stack);
|
||||
}
|
||||
items.add(slotItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
private static List<SlotAccessor> readSlots(AbstractContainerMenu menu, ServerPlayer player, ListTag tag) {
|
||||
List<SlotAccessor> slots = new ArrayList<>();
|
||||
for (Tag t : tag) {
|
||||
CompoundTag compoundTag = (CompoundTag) t;
|
||||
String id = compoundTag.getString("id").orElseThrow();
|
||||
if (!id.startsWith(PROTOCOL_ID + ":")) {
|
||||
throw new IllegalStateException("Invalid slot id: " + id + ", expected to start with '" + PROTOCOL_ID + ":'");
|
||||
}
|
||||
id = id.substring((PROTOCOL_ID + ":").length());
|
||||
int slot = compoundTag.getInt("Slot").orElseThrow();
|
||||
SlotAccessor accessor = switch (id) {
|
||||
case "vanilla" -> new VanillaSlotAccessor(menu.slots.get(slot));
|
||||
case "player" -> new PlayerInventorySlotAccessor(player, slot);
|
||||
default -> throw new IllegalStateException("Unknown container id: " + id);
|
||||
};
|
||||
slots.add(accessor);
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class InputSlotCrafter<T extends AbstractContainerMenu> {
|
||||
protected T container;
|
||||
private Iterable<SlotAccessor> inputStacks;
|
||||
private Iterable<SlotAccessor> inventoryStacks;
|
||||
protected ServerPlayer player;
|
||||
|
||||
protected InputSlotCrafter(T container) {
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
public void fillInputSlots(ServerPlayer player, boolean hasShift) {
|
||||
this.player = player;
|
||||
this.inventoryStacks = this.getInventorySlots();
|
||||
this.inputStacks = this.getInputSlots();
|
||||
|
||||
// Return the already placed items on the grid
|
||||
this.cleanInputs();
|
||||
|
||||
ItemRecipeFinder recipeFinder = new ItemRecipeFinder();
|
||||
this.populateRecipeFinder(recipeFinder);
|
||||
List<List<ItemStack>> ingredients = new ArrayList<>(this.getInputs());
|
||||
|
||||
if (recipeFinder.findRecipe(ingredients, 1, null)) {
|
||||
this.fillInputSlots(recipeFinder, ingredients, hasShift);
|
||||
} else {
|
||||
this.cleanInputs();
|
||||
this.markDirty();
|
||||
throw new NotEnoughMaterialsException();
|
||||
}
|
||||
|
||||
this.markDirty();
|
||||
}
|
||||
|
||||
protected abstract Iterable<SlotAccessor> getInputSlots();
|
||||
|
||||
protected abstract Iterable<SlotAccessor> getInventorySlots();
|
||||
|
||||
protected abstract List<List<ItemStack>> getInputs();
|
||||
|
||||
protected abstract void populateRecipeFinder(ItemRecipeFinder recipeFinder);
|
||||
|
||||
protected abstract void markDirty();
|
||||
|
||||
public void alignRecipeToGrid(Iterable<SlotAccessor> inputStacks, Iterator<ItemStack> recipeItems, int craftsAmount) {
|
||||
for (SlotAccessor inputStack : inputStacks) {
|
||||
if (!recipeItems.hasNext()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.acceptAlignedInput(recipeItems.next(), inputStack, craftsAmount);
|
||||
}
|
||||
}
|
||||
|
||||
public void acceptAlignedInput(ItemStack toBeTakenStack, SlotAccessor inputStack, int craftsAmount) {
|
||||
if (!toBeTakenStack.isEmpty()) {
|
||||
for (int i = 0; i < craftsAmount; ++i) {
|
||||
this.fillInputSlot(inputStack, toBeTakenStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillInputSlot(SlotAccessor slot, ItemStack toBeTakenStack) {
|
||||
SlotAccessor takenSlot = this.takeInventoryStack(toBeTakenStack);
|
||||
if (takenSlot != null) {
|
||||
ItemStack takenStack = takenSlot.getItemStack().copy();
|
||||
if (!takenStack.isEmpty()) {
|
||||
if (takenStack.getCount() > 1) {
|
||||
takenSlot.takeStack(1);
|
||||
} else {
|
||||
takenSlot.setItemStack(ItemStack.EMPTY);
|
||||
}
|
||||
|
||||
takenStack.setCount(1);
|
||||
if (!slot.canPlace(takenStack)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot.getItemStack().isEmpty()) {
|
||||
slot.setItemStack(takenStack);
|
||||
} else {
|
||||
slot.getItemStack().grow(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void fillInputSlots(ItemRecipeFinder recipeFinder, List<List<ItemStack>> ingredients, boolean hasShift) {
|
||||
int recipeCrafts = recipeFinder.countRecipeCrafts(ingredients, Integer.MAX_VALUE, null);
|
||||
int amountToFill = hasShift ? recipeCrafts : 1;
|
||||
List<ItemStack> recipeItems = new ArrayList<>();
|
||||
if (recipeFinder.findRecipe(ingredients, amountToFill, recipeItems::add)) {
|
||||
int finalCraftsAmount = amountToFill;
|
||||
|
||||
for (ItemStack itemId : recipeItems) {
|
||||
// Fix issue with empty item id (grid slot) [shift-click issue]
|
||||
if (itemId.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
finalCraftsAmount = Math.min(finalCraftsAmount, itemId.getMaxStackSize());
|
||||
}
|
||||
|
||||
recipeItems.clear();
|
||||
|
||||
if (recipeFinder.findRecipe(ingredients, finalCraftsAmount, recipeItems::add)) {
|
||||
this.cleanInputs();
|
||||
this.alignRecipeToGrid(inputStacks, recipeItems.iterator(), finalCraftsAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void cleanInputs();
|
||||
|
||||
@Nullable
|
||||
public SlotAccessor takeInventoryStack(ItemStack itemStack) {
|
||||
boolean rejectedModification = false;
|
||||
for (SlotAccessor inventoryStack : inventoryStacks) {
|
||||
ItemStack itemStack1 = inventoryStack.getItemStack();
|
||||
if (!itemStack1.isEmpty() && areItemsEqual(itemStack, itemStack1) && !itemStack1.isDamaged() && !itemStack1.isEnchanted() && !itemStack1.has(DataComponents.CUSTOM_NAME)) {
|
||||
if (!inventoryStack.allowModification(player)) {
|
||||
rejectedModification = true;
|
||||
} else {
|
||||
return inventoryStack;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rejectedModification) {
|
||||
throw new IllegalStateException("Unable to take item from inventory due to slot not allowing modification! Item requested: " + itemStack);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
|
||||
return ItemStack.isSameItemSameComponents(stack1, stack2);
|
||||
}
|
||||
|
||||
public static class NotEnoughMaterialsException extends RuntimeException {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import com.google.common.collect.Interner;
|
||||
import com.google.common.collect.Interners;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ItemRecipeFinder {
|
||||
private final Interner<ItemKey> keys = Interners.newWeakInterner();
|
||||
private final RecipeFinder<ItemKey, Ingredient> finder = new RecipeFinder<>();
|
||||
|
||||
public boolean contains(ItemStack item) {
|
||||
return finder.contains(ofKey(item));
|
||||
}
|
||||
|
||||
public void take(ItemStack item, int amount) {
|
||||
finder.take(ofKey(item), amount);
|
||||
}
|
||||
|
||||
public void put(ItemStack item, int amount) {
|
||||
finder.put(ofKey(item), amount);
|
||||
}
|
||||
|
||||
public void addNormalItem(ItemStack itemStack) {
|
||||
if (Inventory.isUsableForCrafting(itemStack)) {
|
||||
this.addItem(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItem(ItemStack itemStack) {
|
||||
this.addItem(itemStack, itemStack.getMaxStackSize());
|
||||
}
|
||||
|
||||
public void addItem(ItemStack itemStack, int i) {
|
||||
if (!itemStack.isEmpty()) {
|
||||
int j = Math.min(i, itemStack.getCount());
|
||||
this.finder.put(ofKey(itemStack), j);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean findRecipe(List<List<ItemStack>> list, int maxCrafts, @Nullable Consumer<ItemStack> output) {
|
||||
return finder.findRecipe(toIngredients(list), maxCrafts, flatten(itemStack -> {
|
||||
if (output != null) {
|
||||
output.accept(itemStack);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public int countRecipeCrafts(List<List<ItemStack>> list, int maxCrafts, @Nullable Consumer<ItemStack> output) {
|
||||
return finder.countRecipeCrafts(toIngredients(list), maxCrafts, flatten(itemStack -> {
|
||||
if (output != null) {
|
||||
output.accept(itemStack);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private ItemKey ofKey(ItemStack itemStack) {
|
||||
return keys.intern(new ItemKey(itemStack.getItemHolder(), itemStack.getComponentsPatch()));
|
||||
}
|
||||
|
||||
private Ingredient ofKeys(int index, List<ItemStack> itemStack) {
|
||||
return new Ingredient(index, itemStack.stream().map(this::ofKey).toList());
|
||||
}
|
||||
|
||||
private List<Ingredient> toIngredients(List<List<ItemStack>> list) {
|
||||
List<Ingredient> ingredients = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
List<ItemStack> stacks = list.get(i);
|
||||
if (!stacks.isEmpty()) {
|
||||
ingredients.add(ofKeys(i, stacks));
|
||||
}
|
||||
}
|
||||
|
||||
return ingredients;
|
||||
}
|
||||
|
||||
private static BiConsumer<ItemKey, Ingredient> flatten(Consumer<ItemStack> consumer) {
|
||||
int[] lastIndex = {-1};
|
||||
return (itemKey, ingredient) -> {
|
||||
for (int i = lastIndex[0] + 1; i < ingredient.index(); i++) {
|
||||
consumer.accept(ItemStack.EMPTY);
|
||||
}
|
||||
consumer.accept(new ItemStack(itemKey.item(), 1, itemKey.patch()));
|
||||
lastIndex[0] = ingredient.index();
|
||||
};
|
||||
}
|
||||
|
||||
private record Ingredient(int index, List<ItemKey> elements) implements RecipeFinder.Ingredient<ItemKey> {
|
||||
}
|
||||
|
||||
private record ItemKey(Holder<Item> item, DataComponentPatch patch) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.leavesmc.leaves.protocol.rei.transfer.slot.SlotAccessor;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class NewInputSlotCrafter<T extends AbstractContainerMenu> extends InputSlotCrafter<T> {
|
||||
protected final List<SlotAccessor> inputSlots;
|
||||
protected final List<SlotAccessor> inventorySlots;
|
||||
protected final List<List<ItemStack>> inputs;
|
||||
|
||||
public NewInputSlotCrafter(T container, List<SlotAccessor> inputSlots, List<SlotAccessor> inventorySlots, List<List<ItemStack>> inputs) {
|
||||
super(container);
|
||||
this.inputSlots = inputSlots;
|
||||
this.inventorySlots = inventorySlots;
|
||||
this.inputs = inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<SlotAccessor> getInputSlots() {
|
||||
return this.inputSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<SlotAccessor> getInventorySlots() {
|
||||
return this.inventorySlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<List<ItemStack>> getInputs() {
|
||||
return this.inputs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void populateRecipeFinder(ItemRecipeFinder recipeFinder) {
|
||||
for (SlotAccessor slot : getInventorySlots()) {
|
||||
recipeFinder.addNormalItem(slot.getItemStack());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void markDirty() {
|
||||
player.getInventory().setChanged();
|
||||
container.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanInputs() {
|
||||
for (SlotAccessor slot : getInputSlots()) {
|
||||
org.bukkit.inventory.ItemStack bukkitStack = slot.getItemStack().getBukkitStack();
|
||||
if (bukkitStack.getType().isAir()) {
|
||||
return;
|
||||
}
|
||||
HashMap<Integer, org.bukkit.inventory.ItemStack> notAdded = player.getBukkitEntity().getInventory().addItem(bukkitStack);
|
||||
if (notAdded.isEmpty()) {
|
||||
slot.setItemStack(ItemStack.EMPTY);
|
||||
} else {
|
||||
org.bukkit.inventory.ItemStack remain = notAdded.values().iterator().next();
|
||||
slot.setItemStack(ItemStack.fromBukkitCopy(remain));
|
||||
throw new IllegalStateException("rei.rei.no.slot.in.inv");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class RecipeFinder<T, I extends RecipeFinder.Ingredient<T>> {
|
||||
public final Reference2IntOpenHashMap<T> amounts = new Reference2IntOpenHashMap<>();
|
||||
|
||||
public boolean contains(T item) {
|
||||
return this.amounts.getInt(item) > 0;
|
||||
}
|
||||
|
||||
boolean containsAtLeast(T object, int i) {
|
||||
return this.amounts.getInt(object) >= i;
|
||||
}
|
||||
|
||||
public void take(T item, int amount) {
|
||||
int taken = this.amounts.addTo(item, -amount);
|
||||
if (taken < amount) {
|
||||
throw new IllegalStateException("Took " + amount + " items, but only had " + taken);
|
||||
}
|
||||
}
|
||||
|
||||
public void put(T item, int amount) {
|
||||
this.amounts.addTo(item, amount);
|
||||
}
|
||||
|
||||
public boolean findRecipe(List<I> list, int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
return new Filter(list).tryPick(maxCrafts, output);
|
||||
}
|
||||
|
||||
public int countRecipeCrafts(List<I> list, int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
return new Filter(list).tryPickAll(maxCrafts, output);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.amounts.clear();
|
||||
}
|
||||
|
||||
class Filter {
|
||||
private final List<I> ingredients;
|
||||
private final int ingredientCount;
|
||||
private final List<T> items;
|
||||
private final int itemCount;
|
||||
private final BitSet data;
|
||||
private final IntList path = new IntArrayList();
|
||||
|
||||
public Filter(final List<I> list) {
|
||||
this.ingredients = list;
|
||||
this.ingredientCount = this.ingredients.size();
|
||||
this.items = this.getUniqueAvailableIngredientItems();
|
||||
this.itemCount = this.items.size();
|
||||
this.data = new BitSet(this.visitedIngredientCount() + this.visitedItemCount() + this.satisfiedCount() + this.connectionCount() + this.residualCount());
|
||||
this.setInitialConnections();
|
||||
}
|
||||
|
||||
private void setInitialConnections() {
|
||||
for (int i = 0; i < this.ingredientCount; i++) {
|
||||
List<T> list = this.ingredients.get(i).elements();
|
||||
|
||||
for (int j = 0; j < this.itemCount; j++) {
|
||||
if (list.contains(this.items.get(j))) {
|
||||
this.setConnection(j, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryPick(int maxCrafts, @Nullable BiConsumer<T, I> output) {
|
||||
if (maxCrafts <= 0) {
|
||||
return true;
|
||||
} else {
|
||||
int j = 0;
|
||||
|
||||
while (true) {
|
||||
IntList intList = this.tryAssigningNewItem(maxCrafts);
|
||||
if (intList == null) {
|
||||
boolean bl = j == this.ingredientCount;
|
||||
boolean bl2 = bl && output != null;
|
||||
this.clearAllVisited();
|
||||
this.clearSatisfied();
|
||||
|
||||
for (int l = 0; l < this.ingredientCount; l++) {
|
||||
for (int m = 0; m < this.itemCount; m++) {
|
||||
if (this.isAssigned(m, l)) {
|
||||
this.unassign(m, l);
|
||||
put(this.items.get(m), maxCrafts);
|
||||
if (bl2) {
|
||||
output.accept(this.items.get(m), this.ingredients.get(l));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert this.data.get(this.residualOffset(), this.residualOffset() + this.residualCount()).isEmpty();
|
||||
|
||||
return bl;
|
||||
}
|
||||
|
||||
int k = intList.getInt(0);
|
||||
take(this.items.get(k), maxCrafts);
|
||||
int l = intList.size() - 1;
|
||||
this.setSatisfied(intList.getInt(l));
|
||||
j++;
|
||||
|
||||
for (int mx = 0; mx < intList.size() - 1; mx++) {
|
||||
if (isPathIndexItem(mx)) {
|
||||
int n = intList.getInt(mx);
|
||||
int o = intList.getInt(mx + 1);
|
||||
this.assign(n, o);
|
||||
} else {
|
||||
int n = intList.getInt(mx + 1);
|
||||
int o = intList.getInt(mx);
|
||||
this.unassign(n, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPathIndexItem(int i) {
|
||||
return (i & 1) == 0;
|
||||
}
|
||||
|
||||
private List<T> getUniqueAvailableIngredientItems() {
|
||||
Set<T> set = new ReferenceOpenHashSet<>();
|
||||
|
||||
for (Ingredient<T> ingredient : this.ingredients) {
|
||||
set.addAll(ingredient.elements());
|
||||
}
|
||||
|
||||
set.removeIf(object -> !contains(object));
|
||||
return List.copyOf(set);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IntList tryAssigningNewItem(int i) {
|
||||
this.clearAllVisited();
|
||||
|
||||
for (int j = 0; j < this.itemCount; j++) {
|
||||
if (containsAtLeast(this.items.get(j), i)) {
|
||||
IntList intList = this.findNewItemAssignmentPath(j);
|
||||
if (intList != null) {
|
||||
return intList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IntList findNewItemAssignmentPath(int i) {
|
||||
this.path.clear();
|
||||
this.visitItem(i);
|
||||
this.path.add(i);
|
||||
|
||||
while (!this.path.isEmpty()) {
|
||||
int j = this.path.size();
|
||||
int k = this.path.getInt(j - 1);
|
||||
if (isPathIndexItem(j - 1)) {
|
||||
for (int l = 0; l < this.ingredientCount; l++) {
|
||||
if (!this.hasVisitedIngredient(l) && this.hasConnection(k, l) && !this.isAssigned(k, l)) {
|
||||
this.visitIngredient(l);
|
||||
this.path.add(l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!this.isSatisfied(k)) {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
for (int lx = 0; lx < this.itemCount; lx++) {
|
||||
if (!this.hasVisitedItem(lx) && this.isAssigned(lx, k)) {
|
||||
assert this.hasConnection(lx, k);
|
||||
|
||||
this.visitItem(lx);
|
||||
this.path.add(lx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int l = this.path.size();
|
||||
if (l == j) {
|
||||
this.path.removeInt(l - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int visitedIngredientOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int visitedIngredientCount() {
|
||||
return this.ingredientCount;
|
||||
}
|
||||
|
||||
private int visitedItemOffset() {
|
||||
return this.visitedIngredientOffset() + this.visitedIngredientCount();
|
||||
}
|
||||
|
||||
private int visitedItemCount() {
|
||||
return this.itemCount;
|
||||
}
|
||||
|
||||
private int satisfiedOffset() {
|
||||
return this.visitedItemOffset() + this.visitedItemCount();
|
||||
}
|
||||
|
||||
private int satisfiedCount() {
|
||||
return this.ingredientCount;
|
||||
}
|
||||
|
||||
private int connectionOffset() {
|
||||
return this.satisfiedOffset() + this.satisfiedCount();
|
||||
}
|
||||
|
||||
private int connectionCount() {
|
||||
return this.ingredientCount * this.itemCount;
|
||||
}
|
||||
|
||||
private int residualOffset() {
|
||||
return this.connectionOffset() + this.connectionCount();
|
||||
}
|
||||
|
||||
private int residualCount() {
|
||||
return this.ingredientCount * this.itemCount;
|
||||
}
|
||||
|
||||
private boolean isSatisfied(int i) {
|
||||
return this.data.get(this.getSatisfiedIndex(i));
|
||||
}
|
||||
|
||||
private void setSatisfied(int i) {
|
||||
this.data.set(this.getSatisfiedIndex(i));
|
||||
}
|
||||
|
||||
private int getSatisfiedIndex(int i) {
|
||||
assert i >= 0 && i < this.ingredientCount;
|
||||
|
||||
return this.satisfiedOffset() + i;
|
||||
}
|
||||
|
||||
private void clearSatisfied() {
|
||||
this.clearRange(this.satisfiedOffset(), this.satisfiedCount());
|
||||
}
|
||||
|
||||
private void setConnection(int i, int j) {
|
||||
this.data.set(this.getConnectionIndex(i, j));
|
||||
}
|
||||
|
||||
private boolean hasConnection(int i, int j) {
|
||||
return this.data.get(this.getConnectionIndex(i, j));
|
||||
}
|
||||
|
||||
private int getConnectionIndex(int i, int j) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
assert j >= 0 && j < this.ingredientCount;
|
||||
|
||||
return this.connectionOffset() + i * this.ingredientCount + j;
|
||||
}
|
||||
|
||||
private boolean isAssigned(int i, int j) {
|
||||
return this.data.get(this.getResidualIndex(i, j));
|
||||
}
|
||||
|
||||
private void assign(int i, int j) {
|
||||
int k = this.getResidualIndex(i, j);
|
||||
|
||||
assert !this.data.get(k);
|
||||
|
||||
this.data.set(k);
|
||||
}
|
||||
|
||||
private void unassign(int i, int j) {
|
||||
int k = this.getResidualIndex(i, j);
|
||||
|
||||
assert this.data.get(k);
|
||||
|
||||
this.data.clear(k);
|
||||
}
|
||||
|
||||
private int getResidualIndex(int i, int j) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
assert j >= 0 && j < this.ingredientCount;
|
||||
|
||||
return this.residualOffset() + i * this.ingredientCount + j;
|
||||
}
|
||||
|
||||
private void visitIngredient(int i) {
|
||||
this.data.set(this.getVisitedIngredientIndex(i));
|
||||
}
|
||||
|
||||
private boolean hasVisitedIngredient(int i) {
|
||||
return this.data.get(this.getVisitedIngredientIndex(i));
|
||||
}
|
||||
|
||||
private int getVisitedIngredientIndex(int i) {
|
||||
assert i >= 0 && i < this.ingredientCount;
|
||||
|
||||
return this.visitedIngredientOffset() + i;
|
||||
}
|
||||
|
||||
private void visitItem(int i) {
|
||||
this.data.set(this.getVisitiedItemIndex(i));
|
||||
}
|
||||
|
||||
private boolean hasVisitedItem(int i) {
|
||||
return this.data.get(this.getVisitiedItemIndex(i));
|
||||
}
|
||||
|
||||
private int getVisitiedItemIndex(int i) {
|
||||
assert i >= 0 && i < this.itemCount;
|
||||
|
||||
return this.visitedItemOffset() + i;
|
||||
}
|
||||
|
||||
private void clearAllVisited() {
|
||||
this.clearRange(this.visitedIngredientOffset(), this.visitedIngredientCount());
|
||||
this.clearRange(this.visitedItemOffset(), this.visitedItemCount());
|
||||
}
|
||||
|
||||
private void clearRange(int i, int j) {
|
||||
this.data.clear(i, i + j);
|
||||
}
|
||||
|
||||
public int tryPickAll(int i, @Nullable BiConsumer<T, I> output) {
|
||||
int j = 0;
|
||||
int k = Math.min(i, this.getMinIngredientCount()) + 1;
|
||||
|
||||
while (true) {
|
||||
int l = (j + k) / 2;
|
||||
if (this.tryPick(l, null)) {
|
||||
if (k - j <= 1) {
|
||||
if (l > 0) {
|
||||
this.tryPick(l, output);
|
||||
}
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
j = l;
|
||||
} else {
|
||||
k = l;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getMinIngredientCount() {
|
||||
int i = Integer.MAX_VALUE;
|
||||
|
||||
for (Ingredient<T> ingredient : this.ingredients) {
|
||||
int j = 0;
|
||||
|
||||
for (T object : ingredient.elements()) {
|
||||
j = Math.max(j, amounts.getInt(object));
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
i = Math.min(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Ingredient<T> {
|
||||
List<T> elements();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class PlayerInventorySlotAccessor implements SlotAccessor {
|
||||
protected Player player;
|
||||
protected int index;
|
||||
|
||||
public PlayerInventorySlotAccessor(Player player, int index) {
|
||||
this.player = player;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
return player.getInventory().getItem(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(ItemStack stack) {
|
||||
this.player.getInventory().setItem(index, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeStack(int amount) {
|
||||
this.player.getInventory().removeItem(index, amount);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public interface SlotAccessor {
|
||||
ItemStack getItemStack();
|
||||
|
||||
void setItemStack(ItemStack stack);
|
||||
|
||||
void takeStack(int amount);
|
||||
|
||||
default boolean allowModification(Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
default boolean canPlace(ItemStack stack) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License, part of Roughly Enough Items.
|
||||
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.rei.transfer.slot;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class VanillaSlotAccessor implements SlotAccessor {
|
||||
protected Slot slot;
|
||||
|
||||
public VanillaSlotAccessor(Slot slot) {
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItemStack() {
|
||||
return slot.getItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItemStack(ItemStack stack) {
|
||||
this.slot.set(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takeStack(int amount) {
|
||||
slot.remove(amount);
|
||||
}
|
||||
|
||||
public Slot getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowModification(Player player) {
|
||||
return slot.allowModification(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlace(ItemStack stack) {
|
||||
return slot.mayPlace(stack);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user