Compare commits

...
Author SHA1 Message Date
sbancuz 2169ba03d0 Merge remote-tracking branch 'origin/multi-result' into multi-result 2026-08-02 21:16:56 +02:00
sbancuz 7f182757c3 De-dupe draw calls 2026-08-02 21:16:43 +02:00
sbancuz 83ee2e0d53 FurnaceHandler 2026-08-02 21:01:08 +02:00
Luca Bancale ff518c81b3 Merge branch 'master' into multi-result 2026-08-02 20:50:13 +02:00
slprime 5fb5ab6698 Added Bookmark Chain Visitor (#991) 2026-08-01 20:35:19 +03:00
slprime f47a8766a9 Create EmptyContainer (#990) 2026-08-01 20:19:54 +03:00
slprime 4cc72444d9 Fix Tooltip with Component in first line (#989) 2026-08-01 20:00:17 +03:00
slprime b48956ef94 Bookmarks Chain: fix container item multiplier limit (#988) 2026-07-29 00:57:12 +03:00
Algentandslprime 583d28ed12 Fix GL reset (#984)
Co-authored-by: slprime <31038811+slprime@users.noreply.github.com>
2026-07-26 23:36:37 +03:00
koolkrafter5 384813347c Overlay Button moves one item if the recipe stack is size zero (#985) 2026-07-26 23:28:39 +03:00
slprime 2660460c87 Merge branch 'master' into multi-result 2026-07-25 02:14:33 +03:00
slprime 2b28068382 Simplify the keybinding (#982) 2026-07-25 02:14:12 +03:00
sbancuz 1767d4be63 Fix isEmpty conditions, and add catalysts for not breaking old APIs 2026-07-17 12:33:04 +02:00
sbancuz e6684ddebb Fix isEmpty conditions, and add extraInputs for not breaking old APIs 2026-07-17 11:54:34 +02:00
sbancuz 89110ccb7e Check if new behavior is supported (no fallbacks for now) 2026-07-16 16:47:56 +02:00
sbancuz 434b4ac879 Review 2026-07-16 16:05:32 +02:00
sbancuz 24e6f7c57d Allow proper representation of recipes with multiple results 2026-07-16 12:06:40 +02:00
slprime 2c204c25aa Rework chat item link to support bookmarking recipes and groups (#971) 2026-07-11 20:38:01 +03:00
Jordan Rey 5e6ff1d63b fix: spawner placement logic (#973) 2026-07-11 20:33:51 +03:00
Algent 53ce50b35a oregenPattern this time for sure (#974) 2026-07-11 19:53:43 +03:00
slprime 835c4f7e01 Fix Dynamic Keybinding (#972) 2026-07-11 14:20:03 +03:00
RecursivePineapple f8c1f39faf Add Cubic Chunks cube overlay & fix chunk grid above y=256, below y=0. (#970) 2026-07-10 11:02:43 +03:00
slprime 4c4716e64f Fix duplicate keybinding handling by patching KeyBinding static handlers (#968) 2026-07-08 09:19:48 +03:00
47 changed files with 1184 additions and 241 deletions

No files matched your search

@@ -1294,7 +1294,7 @@ public class BookmarkPanel extends PanelWidget<BookmarkGrid> {
public BookmarkRecipeId getBookmarkRecipeId(int itemIndex) {
BookmarkItem item = this.grid.getCalculatedItem(itemIndex);
return item == null || item.type == BookmarkItemType.INGREDIENT || item.recipeId == null ? null
: new BookmarkRecipeId(item.recipeId.getHandleName(), item.recipeId.getIngredients());
: new BookmarkRecipeId(item.recipeId.getHandlerName(), item.recipeId.getIngredients());
}
@Deprecated
@@ -8,7 +8,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -129,8 +128,8 @@ public class FavoriteRecipes {
}
protected List<PositionedStack> getOutputs(IRecipeHandler handler, int recipeIndex) {
final PositionedStack pStackResult = handler.getResultStack(recipeIndex);
return pStackResult != null ? Collections.singletonList(pStackResult) : handler.getOtherStacks(recipeIndex);
final List<PositionedStack> pStackResults = handler.getResultStacks(recipeIndex);
return !pStackResults.isEmpty() ? pStackResults : handler.getOtherStacks(recipeIndex);
}
};
@@ -225,7 +224,7 @@ public class FavoriteRecipes {
continue;
}
if (FAVORITE_ITEM.equals(recipeId.getHandleName()) || RecipeHandlerRef.of(recipeId) != null) {
if (FAVORITE_ITEM.equals(recipeId.getHandlerName()) || RecipeHandlerRef.of(recipeId) != null) {
manualFavorites.add(stack, recipeId);
} else {
hasErrors = true;
@@ -23,6 +23,7 @@ import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.common.util.ForgeDirection;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -65,26 +66,40 @@ public class ItemMobSpawner extends ItemBlock {
@Override
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int side,
float hitX, float hitY, float hitZ) {
int targetX = x;
int targetY = y;
int targetZ = z;
placedX = x;
placedY = y;
placedZ = z;
if (!world.getBlock(x, y, z).isReplaceable(world, x, y, z)) {
ForgeDirection direction = ForgeDirection.getOrientation(side);
targetX += direction.offsetX;
targetY += direction.offsetY;
targetZ += direction.offsetZ;
}
placedX = targetX;
placedY = targetY;
placedZ = targetZ;
String mobType = getMobTypeFromItemStack(itemstack);
boolean placed = super.onItemUse(itemstack, entityplayer, world, x, y, z, side, hitX, hitY, hitZ);
if (placed && !world.isRemote) {
TileEntity tileEntity = world.getTileEntity(x, y, z);
if (tileEntity instanceof TileEntityMobSpawner) {
TileEntityMobSpawner spawner = (TileEntityMobSpawner) tileEntity;
String mobType = getMobTypeFromItemStack(itemstack);
if (mobType != null) {
spawner.func_145881_a().setEntityName(mobType);
world.markBlockForUpdate(x, y, z);
NEICPH.sendMobSpawnerID(x, y, z, mobType);
if (placed) {
TileEntity tileEntity = world.getTileEntity(targetX, targetY, targetZ);
if (tileEntity instanceof TileEntityMobSpawner spawner && mobType != null) {
spawner.func_145881_a().setEntityName(mobType);
if (!world.isRemote) {
world.markBlockForUpdate(targetX, targetY, targetZ);
NEICPH.sendMobSpawnerID(targetX, targetY, targetZ, mobType);
}
}
return true;
}
return placed;
}
+4 -4
View File
@@ -42,7 +42,7 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
if (this.stack != null && this.availableAreaWidth >= SLOT_SIZE * 2
&& (NEIClientConfig.getBooleanSetting("inventory.itemzoom.enabled")
|| KeyManager.isHashDown("itemzoom.hold"))
|| KeyManager.isKeyDown("itemzoom.hold"))
&& GuiContainerManager.shouldShowTooltip(NEIClientUtils.getGuiContainer())) {
final float inverseScaleFactor = 1.0f / this.scale;
final float screenScale = getScreenScale();
@@ -204,18 +204,18 @@ public class ItemZoom extends Widget implements IContainerInputHandler {
return true;
}
if (this.stack != null && KeyManager.isHashDown("itemzoom.hold")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.hold")) {
return true;
}
if (this.stack != null && KeyManager.isHashDown("itemzoom.zoom_in")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_in")) {
increaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
return true;
}
if (this.stack != null && KeyManager.isHashDown("itemzoom.zoom_out")) {
if (this.stack != null && KeyManager.isKeyDown("itemzoom.zoom_out")) {
decreaseZoom(gui);
previousKeyboardRepeatEnabled = Keyboard.areRepeatEventsEnabled();
Keyboard.enableRepeatEvents(true);
+56 -12
View File
@@ -5,9 +5,11 @@ import java.util.LinkedList;
import java.util.Map;
import java.util.StringJoiner;
import net.minecraft.client.Minecraft;
import net.minecraft.client.settings.KeyBinding;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import codechicken.nei.util.NEIKeyboardUtils;
import codechicken.nei.util.NEIMouseUtils;
@@ -24,8 +26,16 @@ public class KeyManager {
}
public static LinkedList<IKeyStateTracker> trackers = new LinkedList<>();
private static final Map<String, KeyBinding> keyBindings = new HashMap<>();
private static final Map<String, String> keyAliases = new HashMap<>();
private static volatile boolean pendingOptionsReload = false;
static {
// Aliases for legacy keybinds
keyAliases.put("gui.recipe", "recipe.recipe");
keyAliases.put("gui.usage", "recipe.usage");
keyAliases.put("gui.hide_bookmarks", "bookmark.hide");
}
public static KeyBinding registerKeyBinding(String ident, int defaultKey) {
return keyBindings.computeIfAbsent(ident, id -> {
@@ -39,18 +49,37 @@ public class KeyManager {
}
public static KeyBinding getKeyBinding(String ident) {
return keyBindings.get(ident);
return keyBindings.get(keyAliases.getOrDefault(ident, ident));
}
public static boolean isRegistered(String description) {
for (KeyBinding binding : keyBindings.values()) {
if (binding.getKeyDescription().equals(description)) {
return true;
}
}
return false;
}
public static int getKeyCode(String ident) {
final KeyBinding binding = keyBindings.get(ident);
final KeyBinding binding = getKeyBinding(ident);
return binding == null ? Keyboard.KEY_NONE : binding.getKeyCode();
}
public static boolean isKeyDown(String ident) {
final KeyBinding binding = keyBindings.get(ident);
final int keyCode = binding == null ? Keyboard.KEY_NONE : binding.getKeyCode();
return keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE && Keyboard.isKeyDown(keyCode);
final KeyBinding binding = getKeyBinding(ident);
if (binding != null) {
final int keyCode = binding.getKeyCode();
if (keyCode < 0) {
return Mouse.isButtonDown(keyCode + 100);
} else if (keyCode > Keyboard.KEY_NONE && keyCode < Keyboard.KEYBOARD_SIZE) {
return Keyboard.isKeyDown(keyCode);
}
}
return false;
}
public static boolean isHashDown(String ident) {
@@ -58,13 +87,18 @@ public class KeyManager {
}
public static boolean isHashDown(String ident, int modifierMask) {
if (isKeyDown(ident)) {
return ((modifierMask & NEIKeyboardUtils.CTRL_HASH) != 0) == NEIClientUtils.controlKey()
&& ((modifierMask & NEIKeyboardUtils.SHIFT_HASH) != 0) == NEIClientUtils.shiftKey()
&& ((modifierMask & NEIKeyboardUtils.ALT_HASH) != 0) == NEIClientUtils.altKey();
if (!isKeyDown(ident)) {
return false;
}
return false;
if (NEIKeyboardUtils.isHashKey(getKeyCode(ident))) {
return true;
}
return ((modifierMask & NEIKeyboardUtils.CTRL_HASH) != 0) == NEIClientUtils.controlKey()
&& ((modifierMask & NEIKeyboardUtils.SHIFT_HASH) != 0) == NEIClientUtils.shiftKey()
&& ((modifierMask & NEIKeyboardUtils.ALT_HASH) != 0) == NEIClientUtils.altKey();
}
public static String getKeyName(String ident) {
@@ -99,7 +133,7 @@ public class KeyManager {
}
public static boolean isPressed(String ident) {
final KeyBinding binding = keyBindings.get(ident);
final KeyBinding binding = getKeyBinding(ident);
return binding != null && binding.isPressed();
}
@@ -108,7 +142,17 @@ public class KeyManager {
return dot < 0 ? "nei.options.keys" : "nei.options.keys." + ident.substring(0, dot);
}
public static void requestOptionsReload() {
pendingOptionsReload = true;
}
public static void tickKeyStates() {
if (pendingOptionsReload) {
pendingOptionsReload = false;
Minecraft.getMinecraft().gameSettings.loadOptions();
}
for (IKeyStateTracker tracker : trackers) tracker.tickKeyStates();
}
}
@@ -224,12 +224,12 @@ public class LayoutManager implements IContainerInputHandler, IContainerTooltipH
@Override
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyID) {
if (KeyManager.isHashDown("gui.hide")) {
if (KeyManager.isKeyDown("gui.hide")) {
toggleBooleanSetting("inventory.hidden");
return true;
}
if (KeyManager.isHashDown("bookmark.hide")) {
if (KeyManager.isKeyDown("bookmark.hide")) {
toggleBooleanSetting("inventory.bookmarks.enabled");
return true;
}
+16 -2
View File
@@ -6,6 +6,7 @@ import static codechicken.nei.PacketIDs.S2C;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.INetHandlerPlayClient;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.world.World;
@@ -14,6 +15,7 @@ import codechicken.core.ClientUtils;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
import codechicken.nei.commands.CommandBookmarkAdd;
import cpw.mods.fml.relauncher.Side;
public class NEICPH implements IClientPacketHandler {
@@ -56,6 +58,18 @@ public class NEICPH implements IClientPacketHandler {
case S2C.OPEN_POTION_GUI:
ClientUtils.openSMPGui(packet.readUByte(), new GuiPotionCreator(mc.thePlayer.inventory));
break;
case S2C.CACHE_BOOKMARK_DATA:
handleCacheBookmarkData(packet);
break;
}
}
private void handleCacheBookmarkData(PacketCustom packet) {
final String payloadId = packet.readString();
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
if (payloadTag != null) {
CommandBookmarkAdd.cachePayload(payloadId, payloadTag);
}
}
@@ -186,9 +200,9 @@ public class NEICPH implements IClientPacketHandler {
packet.sendToServer();
}
public static void sendChatLink(ItemStack stackover) {
public static void sendChatLink(NBTTagCompound payloadTag) {
PacketCustom packet = new PacketCustom(channel, C2S.SEND_CHAT_ITEM_LINK);
packet.writeItemStack(stackover);
packet.writeNBTTagCompound(payloadTag);
packet.sendToServer();
}
@@ -839,6 +839,7 @@ public class NEIClientConfig {
API.addKeyBind("bookmark.favorite_item", Keyboard.KEY_F);
API.addKeyBind("bookmark.remove_recipe", Keyboard.KEY_A);
API.addKeyBind("bookmark.pull_items", Keyboard.KEY_V);
API.addKeyBind("bookmark.chat_link", Keyboard.KEY_L);
API.addKeyBind("bookmark.hide", Keyboard.KEY_B);
API.addKeyBind("itemzoom.toggle", Keyboard.KEY_Z);
@@ -860,7 +861,6 @@ public class NEIClientConfig {
API.addKeyBind("copy.name", Keyboard.KEY_C);
API.addKeyBind("copy.oredict", Keyboard.KEY_D);
API.addKeyBind("copy.identifier", Keyboard.KEY_X);
API.addKeyBind("copy.chat_link_item", Keyboard.KEY_L);
}
public static OptionList getOptionList() {
@@ -987,6 +987,7 @@ public class NEIClientConfig {
}
});
KeyManager.requestOptionsReload();
RecipeCatalysts.loadCatalystInfo();
SubsetWidget.loadCustomSubsets();
SubsetWidget.loadHidden();
@@ -38,6 +38,7 @@ import net.minecraft.inventory.Container;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.ResourceLocation;
@@ -379,8 +380,8 @@ public class NEIClientUtils extends NEIServerUtils {
else sendCommand(getStringSetting("command.heal"), mc().thePlayer.getCommandSenderName());
}
public static void sendChatItemLink(ItemStack stackover) {
if (hasSMPCounterPart()) NEICPH.sendChatLink(stackover);
public static void sendChatItemLink(NBTTagCompound payload) {
if (hasSMPCounterPart()) NEICPH.sendChatLink(payload);
}
public static void toggleMagnetMode() {
@@ -27,7 +27,7 @@ import codechicken.nei.commands.CommandUntranslator;
import codechicken.nei.guihook.GuiContainerManager;
import codechicken.nei.guihook.IContainerInputHandler;
import codechicken.nei.guihook.IContainerSlotClickHandler;
import codechicken.nei.recipe.GuiRecipe;
import codechicken.nei.util.EmptyContainer;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@@ -252,7 +252,7 @@ public class NEIController implements IContainerSlotClickHandler, IContainerInpu
// slot in GuiRecipe does not contain actual stack
// and scroll should be handled by GuiRecipe, not here
if (gui instanceof GuiRecipe) return false;
if (gui.inventorySlots instanceof EmptyContainer) return false;
Point mousePos = getMousePosition();
Slot mouseover = manager.window.getSlotAtPosition(mousePos.x, mousePos.y);
@@ -43,6 +43,7 @@ public class NEIModContainer extends DummyModContainer {
private static boolean gregTech5Loaded;
private static boolean gtnhLibLoaded;
private static boolean cubicChunksLoaded;
private static ASMDataTable asmDataTable;
@@ -69,6 +70,10 @@ public class NEIModContainer extends DummyModContainer {
return gtnhLibLoaded;
}
public static boolean isCCLoaded() {
return cubicChunksLoaded;
}
@Override
public Set<ArtifactVersion> getRequirements() {
Set<ArtifactVersion> deps = new HashSet<>();
@@ -96,6 +101,7 @@ public class NEIModContainer extends DummyModContainer {
public void preInit(FMLPreInitializationEvent event) {
gregTech5Loaded = Loader.isModLoaded("gregtech") && !Loader.isModLoaded("gregapi_post");
gtnhLibLoaded = Loader.isModLoaded("gtnhlib");
cubicChunksLoaded = Loader.isModLoaded("cubicchunks");
if (CommonUtils.isClient()) ClientHandler.preInit();
asmDataTable = event.getAsmData();
}
+3 -1
View File
@@ -11,6 +11,7 @@ import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.inventory.InventoryBasic;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.play.INetHandlerPlayServer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityMobSpawner;
@@ -95,7 +96,8 @@ public class NEISPH implements IServerPacketHandler {
}
private void handleSendChatItemLink(EntityPlayerMP sender, PacketCustom packet) {
NEIServerUtils.sendChatItemLink(sender, packet.readItemStack());
final NBTTagCompound payloadTag = packet.readNBTTagCompound();
NEIServerUtils.sendChatItemLink(sender, payloadTag);
}
private void handleDummySlotSet(EntityPlayerMP sender, PacketCustom packet) {
@@ -10,6 +10,7 @@ import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.zip.ZipException;
import net.minecraft.command.ICommandSender;
@@ -28,6 +29,7 @@ import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
@@ -44,6 +46,7 @@ import codechicken.lib.inventory.InventoryRange;
import codechicken.lib.inventory.InventoryUtils;
import codechicken.lib.packet.PacketCustom;
import codechicken.nei.PacketIDs.S2C;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTHelper;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.registry.GameRegistry;
@@ -86,16 +89,46 @@ public class NEIServerUtils {
player.extinguish();
}
public static void sendChatItemLink(EntityPlayerMP sender, ItemStack stackover) {
ServerUtils.sendChatToAll(
new ChatComponentTranslation(
"nei.chat.item_link.text",
sender.getDisplayName(),
stackover.func_151000_E().setChatStyle(
stackover.func_151000_E().getChatStyle().setChatClickEvent(
new ClickEvent(
ClickEvent.Action.RUN_COMMAND,
"/nei_bookmark " + stackover.writeToNBT(new NBTTagCompound()))))));
public static void sendChatItemLink(EntityPlayerMP sender, NBTTagCompound payload) {
if (payload == null) {
sender.addChatMessage(new ChatComponentText("Invalid bookmark payload"));
return;
}
final String linkType = payload.getString("linkType");
final String payloadId = UUID.randomUUID().toString().substring(0, 8);
final PacketCustom packet = new PacketCustom(NEISPH.channel, S2C.CACHE_BOOKMARK_DATA).writeString(payloadId)
.writeNBTTagCompound((NBTTagCompound) payload.copy());
for (EntityPlayer player : ServerUtils.getPlayers()) {
if (player instanceof EntityPlayerMP playerMP) {
packet.sendToPlayer(playerMP);
}
}
try {
final IChatComponent clickable;
if (payload.hasKey("item")) {
clickable = StackInfo.loadFromNBT(payload.getCompoundTag("item")).func_151000_E();
} else {
clickable = new ChatComponentText("[")
.appendSibling(new ChatComponentTranslation("nei.chat.item_link." + linkType + ".text"))
.appendText("]");
}
if (clickable == null) {
throw new Exception("Invalid ItemStack NBT");
}
clickable.getChatStyle()
.setChatClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/nei_bookmark " + payloadId));
ServerUtils.sendChatToAll(
new ChatComponentTranslation("nei.chat.item_link.text", sender.getDisplayName(), clickable));
} catch (Exception e) {
sender.addChatMessage(new ChatComponentText(e.toString()));
}
}
public static long getTime(World world) {
@@ -37,5 +37,6 @@ public final class PacketIDs {
public static final int OPEN_ENCHANTMENT_GUI = 21;
public static final int SET_CREATIVE_PLUS_MODE = 23;
public static final int OPEN_POTION_GUI = 24;
public static final int CACHE_BOOKMARK_DATA = 25;
}
}
@@ -89,9 +89,9 @@ public class PresetsList {
return false;
}
final PositionedStack result = handler.getResultStack(recipeIndex);
final List<PositionedStack> results = handler.getResultStacks(recipeIndex);
if (result != null && matchPositionedStack(result)) {
if (!results.isEmpty() && matchPositionedStack(results, true)) {
return true;
}
@@ -101,7 +101,13 @@ public class PresetsList {
return true;
}
return result == null && others.isEmpty();
final List<PositionedStack> extraInputs = handler.getCatalystStacks(recipeIndex);
if (!extraInputs.isEmpty() && matchPositionedStack(extraInputs, true)) {
return true;
}
return results.isEmpty() && others.isEmpty();
}
private boolean matchPositionedStack(List<PositionedStack> items, boolean dir) {
@@ -1,5 +1,7 @@
package codechicken.nei;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.Callable;
@@ -58,6 +60,7 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
}
public static void render(float frame) {
GL11.glPushAttrib(GL11.GL_ENABLE_BIT | GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_LINE_BIT);
GL11.glPushMatrix();
Entity entity = Minecraft.getMinecraft().renderViewEntity;
@@ -71,9 +74,12 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
GL11.glTranslated(-interpPosX + intOffsetX, -interpPosY + intOffsetY, -interpPosZ + intOffsetZ);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glDepthFunc(GL11.GL_LEQUAL);
renderChunkBounds(entity, intOffsetX, intOffsetY, intOffsetZ);
renderMobSpawnOverlay(entity, intOffsetX, intOffsetY, intOffsetZ);
GL11.glPopMatrix();
GL11.glPopAttrib();
}
private static void buildMobSpawnOverlay(Entity entity) {
@@ -231,6 +237,9 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
private static void renderChunkBounds(Entity entity, int intOffsetX, int intOffsetY, int intOffsetZ) {
if (chunkOverlay == 0) return;
int worldMinY = worldMinHeight(entity.worldObj);
int worldMaxY = worldMaxHeight(entity.worldObj);
GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glEnable(GL11.GL_BLEND);
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
@@ -249,14 +258,13 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
double dy = 128;
double y1 = Math.floor(entity.posY - dy / 2);
double y2 = y1 + dy;
if (y1 < 0) {
y1 = 0;
y2 = dy;
if (y1 < worldMinY) {
y1 = worldMinY;
y2 = worldMinY + dy;
}
if (y1 > entity.worldObj.getHeight()) {
y2 = entity.worldObj.getHeight();
y1 = y2 - dy;
if (y2 > worldMaxY) {
y2 = worldMaxY;
y1 = Math.max(worldMaxY - dy, worldMinY);
}
y1 -= intOffsetY;
@@ -282,19 +290,34 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
tess.addVertex(x1, y2, z1);
}
if (NEIModContainer.isCCLoaded()) {
tess.setColorRGBA_F(0, 0.5F, 0.9F, (float) dist * 0.5F);
int cubeBase = (int) Math.floor((y1 + intOffsetY) / 16.0) * 16;
for (int cubeY = cubeBase; cubeY <= y2 + intOffsetY; cubeY += 16) {
double ry = cubeY - intOffsetY;
tess.addVertex(x1, ry, z1);
tess.addVertex(x2, ry, z1);
tess.addVertex(x1, ry, z2);
tess.addVertex(x2, ry, z2);
tess.addVertex(x1, ry, z1);
tess.addVertex(x1, ry, z2);
tess.addVertex(x2, ry, z1);
tess.addVertex(x2, ry, z2);
}
}
if (cx == 0 && cz == 0) {
if (chunkOverlay == 2) {
dy = 32;
y1 = Math.floor(entity.posY - dy / 2);
y2 = y1 + dy;
if (y1 < 0) {
y1 = 0;
y2 = dy;
if (y1 < worldMinY) {
y1 = worldMinY;
y2 = worldMinY + dy;
}
if (y1 > entity.worldObj.getHeight()) {
y2 = entity.worldObj.getHeight();
y1 = y2 - dy;
if (y2 > worldMaxY) {
y2 = worldMaxY;
y1 = Math.max(worldMaxY - dy, worldMinY);
}
y1 -= intOffsetY;
@@ -393,6 +416,42 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
GL11.glDisable(GL11.GL_BLEND);
}
/** Holder defers class loading until Cubic Chunks is confirmed present. */
private static final class CCWorldBounds {
static final MethodHandle GET_MIN_HEIGHT;
static final MethodHandle GET_MAX_HEIGHT;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
// Mixined into World by CC, use reflection + MethodHandles to call these
GET_MIN_HEIGHT = lookup.unreflect(World.class.getMethod("getMinHeight"));
GET_MAX_HEIGHT = lookup.unreflect(World.class.getMethod("getMaxHeight"));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
}
private static int worldMinHeight(World world) {
if (!NEIModContainer.isCCLoaded()) return 0;
try {
return (int) CCWorldBounds.GET_MIN_HEIGHT.invoke(world);
} catch (Throwable ignored) {
return 0;
}
}
private static int worldMaxHeight(World world) {
if (!NEIModContainer.isCCLoaded()) return world.getHeight();
try {
return (int) CCWorldBounds.GET_MAX_HEIGHT.invoke(world);
} catch (Throwable ignored) {
return world.getHeight();
}
}
private static Callable<String> oregenPatternReader;
private static String getOregenPatternName() {
@@ -414,13 +473,9 @@ public class WorldOverlayRenderer implements IKeyStateTracker {
"gregtech.common.GT_Worldgenerator");
try {
// GT5-Unofficial >= 5.09.54.02 support
final Method server = gtWorldGenerator.getMethod("getServerOregenPattern");
final Method client = gtWorldGenerator.getMethod("getClientOregenPattern");
return () -> {
final Method accessor = Minecraft.getMinecraft().isSingleplayer() ? server : client;
return ((Enum<?>) accessor.invoke(null)).name();
};
// GT5-Unofficial >= 5.09.54.29 support
final Method accessor = gtWorldGenerator.getMethod("getOregenPattern");
return () -> ((Enum<?>) accessor.invoke(null)).name();
} catch (NoSuchMethodException legacy) {
// Older GT
final Field oregenPattern = gtWorldGenerator.getDeclaredField("oregenPattern");
@@ -224,6 +224,7 @@ public class API {
KeyManager.registerKeyBinding(ident, defaultKey);
}
@Deprecated
public static void addHashBind(String ident, int defaultKey) {
KeyManager.registerKeyBinding(ident, defaultKey);
}
@@ -30,6 +30,7 @@ import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
import codechicken.nei.bookmark.BookmarkItem;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.bookmark.BookmarkPayload;
import codechicken.nei.bookmark.BookmarksGridSlot;
import codechicken.nei.recipe.AutoCraftingManager;
import codechicken.nei.recipe.GuiCraftingRecipe;
@@ -95,6 +96,11 @@ public abstract class ShortcutInputHandler {
return ItemPanels.bookmarkPanel.pullBookmarkItems(groupId, NEIClientUtils.shiftKey());
}
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(groupId).toNBT());
return true;
}
if (NEIClientConfig.autocraftingEnabled() && KeyManager.isKeyDown("gui.craft_items")
&& NEIClientUtils.shiftKey()
&& ItemPanels.bookmarkPanel.getGrid().isCraftingMode(groupId)) {
@@ -134,15 +140,15 @@ public abstract class ShortcutInputHandler {
return copyItemStackOreDictionary(stackover);
}
if (KeyManager.isHashDown("copy.chat_link_item", NEIClientUtils.CTRL_HASH)) {
return sendItemStackChatLink(stackover);
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey()) {
return sendRecipeInChatLink(stackover, NEIClientUtils.shiftKey());
}
if (KeyManager.isHashDown("recipe.recipe")) {
if (KeyManager.isKeyDown("recipe.recipe")) {
return GuiCraftingRecipe.openRecipeGui("item", stackover);
}
if (KeyManager.isHashDown("recipe.usage")) {
if (KeyManager.isKeyDown("recipe.usage")) {
return GuiUsageRecipe.openRecipeGui("item", stackover);
}
@@ -198,10 +204,37 @@ public abstract class ShortcutInputHandler {
return true;
}
private static boolean sendItemStackChatLink(ItemStack stackover) {
if (stackover == null) return false;
private static boolean sendRecipeInChatLink(ItemStack stackover, boolean saveIngredients) {
final Point mousePos = GuiDraw.getMousePosition();
final BookmarksGridSlot slot = ItemPanels.bookmarkPanel.getSlotMouseOver(mousePos.x, mousePos.y);
NEIClientUtils.sendChatItemLink(stackover); // I wish clients could just send formatted messages
if (slot != null && saveIngredients && slot.getType() == BookmarkItemType.RESULT) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(slot.getBookmarkItem()).toNBT());
return true;
}
final Recipe recipe = getFocusedRecipe(stackover, mousePos.x, mousePos.y, saveIngredients);
if (recipe != null && saveIngredients) {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(recipe).toNBT());
return true;
}
final RecipeId recipeId;
if (slot != null) {
recipeId = slot.getType() == BookmarkItemType.INGREDIENT ? null : slot.getRecipeId();
} else {
recipeId = recipe != null ? recipe.getRecipeId() : null;
}
final boolean existsRecipe = NEIClientConfig.getBooleanSetting("inventory.bookmarks.bookmarkItemsWithRecipe");
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(stackover, existsRecipe ? recipeId : null).toNBT());
return true;
}
@@ -449,6 +482,10 @@ public abstract class ShortcutInputHandler {
KeyManager.getKeyName("bookmark.remove_recipe", NEIClientUtils.SHIFT_HASH),
NEIClientUtils.translate("bookmark.group.remove_recipe"));
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.group.chat_link"));
if (BookmarkContainerInfo.getBookmarkContainerHandler(gui) != null) {
hotkeys.put(
KeyManager.getKeyName("bookmark.pull_items"),
@@ -579,9 +616,10 @@ public abstract class ShortcutInputHandler {
hotkeys.put(
KeyManager.getKeyName("copy.identifier", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.copy_id"));
hotkeys.put(
KeyManager.getKeyName("copy.chat_link_item", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("itempanel.chat_link_item"));
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.item.chat_link"));
if (!(gui instanceof GuiRecipe) && NEIClientConfig.canCheatItem(stack)) {
hotkeys.put(
@@ -639,6 +677,9 @@ public abstract class ShortcutInputHandler {
}
}
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("bookmark.recipe.chat_link"));
}
hotkeysCache.update(mousex, mousey, stack, groupId, hotkeys);
@@ -0,0 +1,76 @@
package codechicken.nei.asm;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.settings.KeyBinding;
import cpw.mods.fml.relauncher.ReflectionHelper;
public final class KeyBindingFixHooks {
private static volatile boolean initialized;
private static Field keybindArrayField;
private static Field pressedField;
private static Field pressTimeField;
private KeyBindingFixHooks() {}
public static void onTickAll(int keyCode) {
if (keyCode == 0) {
return;
}
for (KeyBinding binding : getBindings()) {
if (binding.getKeyCode() == keyCode) {
try {
pressTimeField.setInt(binding, pressTimeField.getInt(binding) + 1);
} catch (Exception ignored) {}
}
}
}
public static void setKeyBindStateAll(int keyCode, boolean pressed) {
if (keyCode == 0) {
return;
}
for (KeyBinding binding : getBindings()) {
if (binding.getKeyCode() == keyCode) {
try {
pressedField.setBoolean(binding, pressed);
} catch (Exception ignored) {}
}
}
}
@SuppressWarnings("unchecked")
private static List<KeyBinding> getBindings() {
init();
try {
return (List<KeyBinding>) keybindArrayField.get(null);
} catch (Exception ignored) {}
return Collections.emptyList();
}
private static synchronized void init() {
if (initialized) {
return;
}
keybindArrayField = ReflectionHelper.findField(KeyBinding.class, "keybindArray", "field_74516_a");
pressedField = ReflectionHelper.findField(KeyBinding.class, "pressed", "field_74513_e");
pressTimeField = ReflectionHelper.findField(KeyBinding.class, "pressTime", "field_151474_i");
initialized = true;
}
}
@@ -56,7 +56,8 @@ public class NEITransformer implements IClassTransformer {
public NEITransformer() {
if (FMLLaunchHandler.side().isClient()) {
// Generates method to set the placed position of a mob spawner for the item callback. More portable than
// Generates method to set the placed position of a mob spawner for the item
// callback. More portable than
// copying vanilla placement code
transformer.add(
new MethodWriter(
@@ -82,7 +83,6 @@ public class NEITransformer implements IClassTransformer {
asmblocks.get("commaFix"),
true));
// fix workbench container losing items on shift click output without room for the full stack
transformer.add(
new MethodTransformer(
new ObfMapping(
@@ -105,6 +105,33 @@ public class NEITransformer implements IClassTransformer {
asmblocks.get("d_glintAlphaFix"),
asmblocks.get("glintAlphaFix")));
if (FMLLaunchHandler.side().isClient()) {
transformer.add(
new MethodWriter(
ACC_PUBLIC | ACC_STATIC,
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74507_a", "(I)V"),
asmblocks.get("m_keyBindingOnTickAll")));
transformer.add(
new MethodWriter(
ACC_PUBLIC | ACC_STATIC,
new ObfMapping("net/minecraft/client/settings/KeyBinding", "func_74510_a", "(IZ)V"),
asmblocks.get("m_keyBindingSetStateAll")));
transformer.add(
new MethodInjector(
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
asmblocks.get("saveOptionsHook"),
true));
transformer.add(
new MethodInjector(
new ObfMapping("net/minecraft/client/settings/GameSettings", "func_74303_b", "()V"),
asmblocks.get("n_saveOptionsSendSettings"),
asmblocks.get("saveOptionsRestoreHook"),
false));
}
String GuiContainer = "net/minecraft/client/gui/inventory/GuiContainer";
// add manager field
transformer.add(
@@ -0,0 +1,78 @@
package codechicken.nei.asm;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import codechicken.core.CommonUtils;
import codechicken.nei.KeyManager;
import codechicken.nei.NEIClientConfig;
public final class SaveOptionsFixHooks {
private static final String KEY_PREFIX = "key_nei.options.keys.";
private static List<String> pendingRestoreLines;
private SaveOptionsFixHooks() {}
public static void onBeforeSaveOptions() {
pendingRestoreLines = null;
if (NEIClientConfig.isLoaded()) {
return;
}
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
if (!optionsFile.exists()) {
return;
}
final List<String> missing = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(optionsFile))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.startsWith(KEY_PREFIX)) continue;
final int colon = line.indexOf(':');
if (colon <= 0) continue;
final String description = line.substring(4, colon);
if (!KeyManager.isRegistered(description)) {
missing.add(line);
}
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to read key bindings before options save", e);
return;
}
if (!missing.isEmpty()) {
pendingRestoreLines = missing;
}
}
public static void onAfterSaveOptions() {
final List<String> restore = pendingRestoreLines;
pendingRestoreLines = null;
if (restore == null) {
return;
}
final File optionsFile = new File(CommonUtils.getMinecraftDir(), "options.txt");
try (FileWriter writer = new FileWriter(optionsFile, true)) {
for (String line : restore) {
writer.write(line);
writer.write(System.lineSeparator());
}
} catch (IOException e) {
NEIClientConfig.logger.error("Failed to restore NEI key bindings after options save", e);
}
}
}
@@ -10,12 +10,16 @@ import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fluids.FluidStack;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class BookmarkItem {
@@ -245,6 +249,45 @@ public class BookmarkItem {
return builder(groupId, stack).build();
}
public static BookmarkItem of(JsonObject jsonObject) {
final NBTTagCompound itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
final ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
if (itemStack != null) {
final int groupId = jsonObject.has("groupId") ? jsonObject.get("groupId").getAsInt()
: BookmarkGrid.DEFAULT_GROUP_ID;
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
if (jsonObject.has("type")) {
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
builder.type(BookmarkItemType.INGREDIENT);
} else {
builder.type(BookmarkItemType.RESULT);
}
if (jsonObject.has("multiplier")) {
builder.multiplier(jsonObject.get("multiplier").getAsInt());
}
if (jsonObject.has("factor")) {
builder.factor(jsonObject.get("factor").getAsInt());
}
if (jsonObject.has("chance")) {
builder.chance(jsonObject.get("chance").getAsInt());
}
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
builder.recipeId(RecipeId.of(recipeJson));
}
return builder.build();
}
return null;
}
public BookmarkItem copyWithMultiplier(long multiplier) {
return new BookmarkItem(
this.groupId,
@@ -364,4 +407,27 @@ public class BookmarkItem {
return false;
}
public JsonObject toJsonObject() {
final JsonObject jsonObject = new JsonObject();
jsonObject.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(getItemStack())));
jsonObject.add("multiplier", new JsonPrimitive(this.multiplier));
jsonObject.add("factor", new JsonPrimitive(this.factor));
jsonObject.add("type", new JsonPrimitive(this.type.toInt()));
if (this.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
jsonObject.add("groupId", new JsonPrimitive(this.groupId));
}
if (this.recipeId != null) {
jsonObject.add("recipeId", this.recipeId.toJsonObject());
}
if (this.chance != PositionedStack.CHANCE_FULL) {
jsonObject.add("chance", new JsonPrimitive(this.chance));
}
return jsonObject;
}
}
@@ -0,0 +1,283 @@
package codechicken.nei.bookmark;
import java.awt.Point;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import com.google.gson.JsonObject;
import codechicken.lib.gui.GuiDraw;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.ItemPanels;
import codechicken.nei.ItemQuantityField;
import codechicken.nei.ItemStackSet;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.Recipe.RecipeIngredient;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class BookmarkPayload {
private String linkType;
private NBTTagCompound target;
private NBTTagCompound group;
private NBTTagList items = new NBTTagList();
private NBTTagList recipes = new NBTTagList();
private BookmarkPayload(String linkType, ItemStack stack) {
this.linkType = linkType;
this.target = stack != null ? StackInfo.itemStackToNBT(stack) : null;
}
public static BookmarkPayload of(NBTTagCompound nbt) {
final BookmarkPayload payload = new BookmarkPayload(nbt.getString("linkType"), null);
if (nbt.hasKey("item")) {
payload.target = nbt.getCompoundTag("item");
}
if (nbt.hasKey("group")) {
payload.group = nbt.getCompoundTag("group");
}
payload.items = nbt.getTagList("items", 10);
payload.recipes = nbt.getTagList("recipes", 10);
return payload;
}
public static BookmarkPayload of(BookmarkItem item) {
final BookmarkPayload payload = new BookmarkPayload("recipe", item.getItemStack());
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final RecipeId recipeId = item.recipeId;
final int groupId = item.groupId;
payload.items = new NBTTagList();
payload.recipes = new NBTTagList();
if (recipeId != null) {
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
final BookmarkItem currentItem = grid.getBookmarkItem(itemIndex);
if (currentItem != null && currentItem.groupId == groupId && recipeId.equals(currentItem.recipeId)) {
addBookmarkItemToPayload(payload, currentItem, recipesIndexes);
}
}
} else {
addBookmarkItemToPayload(payload, item, recipesIndexes);
}
return payload;
}
public static BookmarkPayload of(Recipe recipe) {
final BookmarkPayload payload = new BookmarkPayload("recipe", recipe.getResult());
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final ItemStackSet seenIngredients = new ItemStackSet();
final ItemStackSet seenResults = new ItemStackSet();
for (RecipeIngredient result : recipe.getResults()) {
ItemStack stack = result.getItemStack();
if (!seenResults.contains(stack)) {
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.RESULT).build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
seenResults.add(stack);
}
}
for (RecipeIngredient ingr : recipe.getIngredients()) {
ItemStack stack = ingr.getItemStack();
if (!seenIngredients.contains(stack)) {
final BookmarkItem item = BookmarkItem.builder(-1, stack, recipe, BookmarkItemType.INGREDIENT).build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
seenIngredients.add(stack);
}
}
return payload;
}
public static BookmarkPayload of(int groupId) {
final BookmarkPayload payload = new BookmarkPayload("group", null);
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
for (int itemIndex = 0; itemIndex < grid.size(); itemIndex++) {
final BookmarkItem item = grid.getBookmarkItem(itemIndex);
if (item != null && item.groupId == groupId) {
addBookmarkItemToPayload(payload, item, recipesIndexes);
}
}
payload.group = generateGroup(grid.getGroup(groupId));
return payload;
}
public static BookmarkPayload of(ItemStack stackover, RecipeId recipeId) {
final BookmarkPayload payload = new BookmarkPayload("item", stackover);
final Map<RecipeId, Integer> recipesIndexes = new HashMap<>();
final Point mousePos = GuiDraw.getMousePosition();
if (ItemPanels.itemPanel.containsWithSubpanels(mousePos.x, mousePos.y)) {
stackover = ItemQuantityField.prepareStackWithQuantity(stackover, 0);
}
final BookmarkItem item = BookmarkItem.builder(-1, stackover).recipeId(recipeId).type(BookmarkItemType.ITEM)
.build();
addBookmarkItemToPayload(payload, item, recipesIndexes);
return payload;
}
private static NBTTagCompound generateGroup(BookmarkGroup group) {
final NBTTagCompound groupTag = new NBTTagCompound();
final NBTTagList collapsedRecipes = new NBTTagList();
groupTag.setString("viewmode", group.viewMode.toString());
groupTag.setBoolean("crafting", group.crafting != null);
groupTag.setBoolean("collapsed", group.collapsed);
for (RecipeId recipeId : group.collapsedRecipes) {
collapsedRecipes.appendTag(NBTJson.toNbt(recipeId.toJsonObject()));
}
groupTag.setTag("collapsedRecipes", collapsedRecipes);
return groupTag;
}
private static void addBookmarkItemToPayload(BookmarkPayload payload, BookmarkItem item,
Map<RecipeId, Integer> recipesIndexes) {
final JsonObject itemTag = item.toJsonObject();
itemTag.remove("groupId");
if (item.recipeId != null) {
final int recipeIndex = recipesIndexes
.computeIfAbsent(item.recipeId, recipeId -> addRecipeToPayload(payload, recipeId, recipesIndexes));
itemTag.addProperty("recipeId", recipeIndex);
}
payload.items.appendTag(NBTJson.toNbt(itemTag));
}
private static int addRecipeToPayload(BookmarkPayload payload, RecipeId recipeId,
Map<RecipeId, Integer> recipesIndexes) {
final NBTTagCompound recipeTag = new NBTTagCompound();
final int recipeIndex = payload.recipes.tagCount();
recipeTag.setTag("recipeId", NBTJson.toNbt(recipeId.toJsonObject()));
recipeTag.setInteger("recipeIndex", recipeIndex);
payload.recipes.appendTag(recipeTag);
return recipeIndex;
}
public NBTTagCompound toNBT() {
final NBTTagCompound payloadTag = new NBTTagCompound();
payloadTag.setString("linkType", linkType);
if (target != null) {
payloadTag.setTag("item", target);
}
if (group != null) {
payloadTag.setTag("group", group);
}
payloadTag.setTag("items", items);
payloadTag.setTag("recipes", recipes);
return payloadTag;
}
public String getLinkType() {
return this.linkType;
}
public ItemStack getTargetStack() {
return this.target != null ? StackInfo.loadFromNBT(this.target) : null;
}
public BookmarkGroup getGroup() {
if (this.group == null) {
return null;
}
final boolean crafting = this.group.hasKey("crafting") && this.group.getBoolean("crafting");
final BookmarkViewMode viewMode;
if (this.group.hasKey("viewmode")) {
viewMode = BookmarkViewMode.valueOf(this.group.getString("viewmode"));
} else {
viewMode = BookmarkViewMode.TODO_LIST;
}
final BookmarkGroup group = new BookmarkGroup(viewMode, crafting);
if (this.group.hasKey("collapsed")) {
group.collapsed = this.group.getBoolean("collapsed");
}
if (this.group.hasKey("collapsedRecipes")) {
final NBTTagList collapsedRecipes = this.group.getTagList("collapsedRecipes", 10);
for (int i = 0; i < collapsedRecipes.tagCount(); i++) {
final NBTTagCompound recipeTag = collapsedRecipes.getCompoundTagAt(i);
final RecipeId recipeId = RecipeId.of((JsonObject) NBTJson.toJsonObject(recipeTag));
group.collapsedRecipes.add(recipeId);
}
}
return group;
}
public List<BookmarkItem> getBookmarkItems(int groupId) {
final List<BookmarkItem> bookmarkItems = new ArrayList<>();
final Map<Integer, RecipeId> recipesMap = new HashMap<>();
for (int i = 0; i < recipes.tagCount(); i++) {
final NBTTagCompound recipeTag = recipes.getCompoundTagAt(i);
final RecipeId recipeId = RecipeId
.of((JsonObject) NBTJson.toJsonObject(recipeTag.getCompoundTag("recipeId")));
recipesMap.put(recipeTag.getInteger("recipeIndex"), recipeId);
}
for (int i = 0; i < items.tagCount(); i++) {
final NBTTagCompound itemTag = items.getCompoundTagAt(i);
RecipeId recipeId = null;
if (itemTag.hasKey("recipeId")) {
recipeId = recipesMap.get(itemTag.getInteger("recipeId"));
itemTag.removeTag("recipeId");
}
final BookmarkItem item = BookmarkItem.of((JsonObject) NBTJson.toJsonObject(itemTag));
if (item != null) {
item.groupId = groupId;
item.recipeId = recipeId;
bookmarkItems.add(item);
}
}
return bookmarkItems;
}
}
@@ -10,7 +10,6 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import org.apache.commons.io.IOUtils;
@@ -25,10 +24,7 @@ import com.google.gson.JsonSyntaxException;
import codechicken.nei.BookmarkPanel.BookmarkViewMode;
import codechicken.nei.NEIClientConfig;
import codechicken.nei.PositionedStack;
import codechicken.nei.bookmark.BookmarkItem.BookmarkItemType;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.StackInfo;
import codechicken.nei.util.NBTJson;
public class BookmarkStorage {
@@ -199,7 +195,11 @@ public class BookmarkStorage {
continue;
}
if (!addItemToGrid(grid, parser.parse(itemStr).getAsJsonObject())) {
final BookmarkItem item = BookmarkItem.of(parser.parse(itemStr).getAsJsonObject());
if (item != null) {
grid.addItem(item, false);
} else {
wasError = true;
NEIClientConfig.logger.warn(
"Failed to load bookmarked ItemStack from json string, the item no longer exists:\n{}",
@@ -238,53 +238,6 @@ public class BookmarkStorage {
this.bookmarkFile = bookmarkFile;
}
private boolean addItemToGrid(BookmarkGrid grid, JsonObject jsonObject) {
NBTTagCompound itemStackNBT;
if (jsonObject.get("item") != null) {
itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject.get("item"));
} else { // old format
itemStackNBT = (NBTTagCompound) NBTJson.toNbt(jsonObject);
}
ItemStack itemStack = StackInfo.loadFromNBT(itemStackNBT);
if (itemStack != null) {
int groupId = jsonObject.has("groupId") && grid.groups.containsKey(jsonObject.get("groupId").getAsInt())
? jsonObject.get("groupId").getAsInt()
: BookmarkGrid.DEFAULT_GROUP_ID;
final BookmarkItem.Builder builder = BookmarkItem.builder(groupId, itemStack);
if (jsonObject.has("type")) {
builder.type(BookmarkItemType.fromInt(jsonObject.get("type").getAsInt()));
} else if (jsonObject.has("ingredient") && jsonObject.get("ingredient").getAsBoolean()) {
builder.type(BookmarkItemType.INGREDIENT);
} else {
builder.type(BookmarkItemType.RESULT);
}
if (jsonObject.has("multiplier")) {
builder.multiplier(jsonObject.get("multiplier").getAsInt());
}
if (jsonObject.has("factor")) {
builder.factor(jsonObject.get("factor").getAsInt());
}
if (jsonObject.has("chance")) {
builder.chance(jsonObject.get("chance").getAsInt());
}
if (jsonObject.get("recipeId") instanceof JsonObject recipeJson) {
builder.recipeId(RecipeId.of(recipeJson));
}
grid.addItem(builder.build(), false);
}
return itemStack != null;
}
public void save() {
if (this.bookmarkFile == null) {
@@ -325,26 +278,7 @@ public class BookmarkStorage {
for (BookmarkItem item : grid.bookmarkItems) {
try {
JsonObject row = new JsonObject();
row.add("item", NBTJson.toJsonObject(StackInfo.itemStackToNBT(item.getItemStack())));
row.add("multiplier", new JsonPrimitive(item.multiplier));
row.add("factor", new JsonPrimitive(item.factor));
row.add("type", new JsonPrimitive(item.type.toInt()));
if (item.groupId != BookmarkGrid.DEFAULT_GROUP_ID) {
row.add("groupId", new JsonPrimitive(item.groupId));
}
if (item.recipeId != null) {
row.add("recipeId", item.recipeId.toJsonObject());
}
if (item.chance != PositionedStack.CHANCE_FULL) {
row.add("chance", new JsonPrimitive(item.chance));
}
strings.add(NBTJson.toJson(row));
strings.add(NBTJson.toJson(item.toJsonObject()));
} catch (JsonSyntaxException e) {
NEIClientConfig.logger.error("Failed to stringify bookmarked ItemStack to json string");
}
@@ -193,7 +193,7 @@ public class BookmarksGridSlot extends ItemsGridSlot {
protected HandlerInfo getHandlerInfo() {
if (this.bookmarkItem.recipeId != null && this.handlerInfo == null) {
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.bookmarkItem.recipeId.getHandleName(), null);
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.bookmarkItem.recipeId.getHandlerName(), null);
}
return this.handlerInfo;
}
@@ -1,18 +1,39 @@
package codechicken.nei.commands;
import java.util.Arrays;
import net.minecraft.command.CommandBase;
import net.minecraft.command.ICommandSender;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.JsonToNBT;
import net.minecraft.nbt.NBTException;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import codechicken.nei.ItemPanels;
import codechicken.nei.LRUCache;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkGroup;
import codechicken.nei.bookmark.BookmarkPayload;
public class CommandBookmarkAdd extends CommandBase {
private static final LRUCache<String, NBTTagCompound> PAYLOAD_CACHE = new LRUCache<>(64);
public static void cachePayload(String payloadId, NBTTagCompound payload) {
if (payloadId == null || payloadId.isEmpty() || payload == null) {
return;
}
synchronized (PAYLOAD_CACHE) {
PAYLOAD_CACHE.put(payloadId, (NBTTagCompound) payload.copy());
}
}
private static NBTTagCompound getCachedPayload(String payloadId) {
synchronized (PAYLOAD_CACHE) {
return PAYLOAD_CACHE.get(payloadId);
}
}
@Override
public String getCommandName() {
return "nei_bookmark";
@@ -20,7 +41,7 @@ public class CommandBookmarkAdd extends CommandBase {
@Override
public String getCommandUsage(ICommandSender sender) {
return "/nei_bookmark <nbt>";
return "/nei_bookmark <payloadId>";
}
@Override
@@ -30,19 +51,37 @@ public class CommandBookmarkAdd extends CommandBase {
@Override
public void processCommand(ICommandSender sender, String[] args) {
if (args.length < 1 || args[0].length() < 3) {
sender.addChatMessage(new ChatComponentText("§c/nei_bookmark <nbt>"));
if (args.length < 1) {
sender.addChatMessage(new ChatComponentText("§c/nei_bookmark <payloadId>"));
return;
}
try {
ItemPanels.bookmarkPanel.addItem(
ItemStack.loadItemStackFromNBT((NBTTagCompound) JsonToNBT.func_150315_a(String.join(" ", args))));
final String payloadId = String.join(" ", Arrays.copyOfRange(args, 0, args.length));
final NBTTagCompound payload = getCachedPayload(payloadId);
if (payload == null) {
throw new IllegalArgumentException("Missing cached payload");
}
addBookmarkFromPayload((NBTTagCompound) payload.copy());
sender.addChatMessage(new ChatComponentTranslation("nei.chat.bookmark_added.text"));
} catch (NBTException e) {
} catch (Exception e) {
sender.addChatMessage(new ChatComponentText(e.toString()));
}
}
private static void addBookmarkFromPayload(NBTTagCompound nbt) {
final BookmarkPayload payload = BookmarkPayload.of(nbt);
final BookmarkGrid grid = ItemPanels.bookmarkPanel.getGrid();
final BookmarkGroup group = payload.getGroup();
int groupId = BookmarkGrid.DEFAULT_GROUP_ID;
if (group != null) {
groupId = grid.addGroup(group);
}
payload.getBookmarkItems(groupId).forEach(item -> grid.addItem(item, true));
}
}
@@ -85,11 +85,11 @@ public class RightPanel extends GuiWidget {
setFocus(true);
}
if (focused() && KeyManager.isHashDown("gui.getprevioussearch")) {
if (focused() && KeyManager.isKeyDown("gui.getprevioussearch")) {
handleNavigateHistory(TextHistory.Direction.PREVIOUS);
}
if (focused() && KeyManager.isHashDown("gui.getnextsearch")) {
if (focused() && KeyManager.isKeyDown("gui.getnextsearch")) {
handleNavigateHistory(TextHistory.Direction.NEXT);
}
}
@@ -24,6 +24,12 @@ public class AllOthersRecipeFilter implements IRecipeFilter {
}
}
for (PositionedStack pStack : handler.getCatalystStacks(recipeIndex)) {
if (!match(pStack)) {
return false;
}
}
return true;
}
@@ -24,6 +24,12 @@ public class AnyOthersRecipeFilter implements IRecipeFilter {
}
}
for (PositionedStack pStack : handler.getCatalystStacks(recipeIndex)) {
if (match(pStack)) {
return true;
}
}
return false;
}
@@ -48,9 +48,7 @@ public class RecipeFilter implements IRecipeFilter {
}
if (this.context == FilterContext.ANY || this.context == FilterContext.OUTPUT) {
final PositionedStack result = handler.getResultStack(recipeIndex);
if (result != null && matchPositionedStack(result) == this.anyMatch) {
if (matchPositionedStack(handler.getResultStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
@@ -58,6 +56,10 @@ public class RecipeFilter implements IRecipeFilter {
return this.anyMatch;
}
if (matchPositionedStack(handler.getCatalystStacks(recipeIndex), this.anyMatch)) {
return this.anyMatch;
}
}
return !this.anyMatch;
@@ -625,7 +625,7 @@ public class GuiContainerManager {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE);
}
} else if (!tooltip.isEmpty()) {
} else if (!tooltip.isEmpty() && !tooltip.get(0).startsWith(GuiDraw.TOOLTIP_HANDLER)) {
tooltip.set(0, tooltip.get(0) + GuiDraw.TOOLTIP_LINESPACE);
}
@@ -210,7 +210,7 @@ public class DefaultOverlayHandler implements IOverlayHandler {
for (IngredientDistribution distrib : assignedIngredients) {
if (distrib.slots.length == 0 || !slot.getHasStack() || !canStack(distrib.permutation, stack)) continue;
int transferCap = Math.min(slotTransferCap, multiplier * distrib.permutation.stackSize);
int transferCap = Math.min(slotTransferCap, Math.max(multiplier * distrib.permutation.stackSize, 1));
int stackSize = slot.getStack().stackSize;
boolean pickup = false;
@@ -38,6 +38,11 @@ public class FuelRecipeHandler extends FurnaceRecipeHandler {
public PositionedStack getOtherStack() {
return fuel.stack;
}
@Override
public PositionedStack getCatalyst() {
return fuel.stack;
}
}
private final ArrayList<SmeltingPair> mfurnace = new ArrayList<>();
@@ -82,8 +82,8 @@ public class GuiCraftingRecipe extends GuiRecipe<ICraftingHandler> {
} else if ("recipeId".equals(outputId)) {
ItemStack stack = (ItemStack) results[0];
RecipeId recipeId = (RecipeId) results[1];
craftinghandlers = filterByHandlerName(craftinghandlers, recipeId.getHandleName());
serialCraftingHandlers = filterByHandlerName(serialCraftingHandlers, recipeId.getHandleName());
craftinghandlers = filterByHandlerName(craftinghandlers, recipeId.getHandlerName());
serialCraftingHandlers = filterByHandlerName(serialCraftingHandlers, recipeId.getHandlerName());
recipeHandlerFunction = h -> h.getRecipeHandler("item", stack);
} else {
recipeHandlerFunction = h -> h.getRecipeHandler(outputId, results);
@@ -230,7 +230,7 @@ public class GuiFavoriteButton extends GuiRecipeButton {
}
}
if (recipeId != null && FavoriteRecipes.FAVORITE_ITEM.contains(recipeId.getHandleName())) {
if (recipeId != null && FavoriteRecipes.FAVORITE_ITEM.contains(recipeId.getHandlerName())) {
items.add(stack);
} else {
Recipe recipe = Recipe.of(recipeId);
@@ -22,6 +22,7 @@ import codechicken.nei.NEIClientUtils;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.IOverlayHandler;
import codechicken.nei.bookmark.BookmarkGrid;
import codechicken.nei.bookmark.BookmarkPayload;
import codechicken.nei.drawable.DrawableBuilder;
import codechicken.nei.drawable.DrawableResource;
import codechicken.nei.util.NEIMouseUtils;
@@ -171,6 +172,10 @@ public class GuiOverlayButton extends GuiRecipeButton {
KeyManager.getKeyName("bookmark.add", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("recipe.overlay.bookmarkRecipeAndCount"));
hotkeys.put(
KeyManager.getKeyName("bookmark.chat_link", NEIClientUtils.SHIFT_HASH + NEIClientUtils.CTRL_HASH),
NEIClientUtils.translate("recipe.overlay.sendRecipeToChat"));
return hotkeys;
}
@@ -329,6 +334,10 @@ public class GuiOverlayButton extends GuiRecipeButton {
if (KeyManager.isKeyDown("bookmark.add") && NEIClientUtils.shiftKey()) {
saveRecipeInBookmark();
}
if (KeyManager.isKeyDown("bookmark.chat_link") && NEIClientUtils.controlKey() && NEIClientUtils.shiftKey()) {
sendRecipeChatLink();
}
}
public void saveRecipeInBookmark() {
@@ -343,4 +352,7 @@ public class GuiOverlayButton extends GuiRecipeButton {
}
}
private void sendRecipeChatLink() {
NEIClientUtils.sendChatItemLink(BookmarkPayload.of(getRecipe()).toNBT());
}
}
@@ -54,6 +54,7 @@ import codechicken.nei.scroll.ScrollBar;
import codechicken.nei.scroll.ScrollBar.OverflowType;
import codechicken.nei.scroll.ScrollBar.ScrollPlace;
import codechicken.nei.scroll.ScrollContainer;
import codechicken.nei.util.EmptyContainer;
import codechicken.nei.util.SlotInaccessible;
public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer implements IGuiContainerOverlay,
@@ -181,7 +182,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private boolean isHeightHackApplied = false;
protected GuiRecipe(GuiScreen prevgui) {
super(new ContainerRecipe());
super(new EmptyContainer());
this.recipeTabs = new GuiRecipeTabs() {
@Override
@@ -191,7 +192,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
};
this.recipeCatalyst = new GuiRecipeCatalyst();
this.slotcontainer = (ContainerRecipe) this.inventorySlots;
this.slotcontainer = (EmptyContainer) this.inventorySlots;
this.prevGui = prevgui;
this.firstGuiGeneral = prevgui;
@@ -476,7 +477,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
final H localHandler = this.currenthandlers.get(j);
final HandlerInfo localHandlerInfo = GuiRecipeTab.getHandlerInfo(localHandler);
if (localHandlerInfo.getHandlerName().equals(this.recipeId.getHandleName())) {
if (localHandlerInfo.getHandlerName().equals(this.recipeId.getHandlerName())) {
recipetype = j;
if (!this.recipeId.getIngredients().isEmpty()) {
@@ -526,7 +527,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
@Override
public Slot getSlotAtPosition(int mousex, int mousey) {
final Widget activeWidget = this.container.getWidgetUnderMouse(mousex, mousey);
final ContainerRecipe slotcontainer = (ContainerRecipe) inventorySlots;
final EmptyContainer slotcontainer = (EmptyContainer) inventorySlots;
slotcontainer.setActiveStack(null);
if (activeWidget instanceof NEIRecipeWidget recipeWidget) {
@@ -1033,7 +1034,7 @@ public abstract class GuiRecipe<H extends IRecipeHandler> extends GuiContainer i
private GuiOverlayButton[] overlayButtons = new GuiOverlayButton[0];
@Deprecated
public ContainerRecipe slotcontainer;
public EmptyContainer slotcontainer;
@Deprecated
public List<GuiButton> getOverlayButtons() {
@@ -1,5 +1,6 @@
package codechicken.nei.recipe;
import java.util.Collections;
import java.util.List;
import net.minecraft.client.gui.inventory.GuiContainer;
@@ -82,15 +83,38 @@ public interface IRecipeHandler {
* @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your
* recipe drawing space. For example fuel in furnaces.
*/
default List<PositionedStack> getCatalystStacks(int recipe) {
return Collections.emptyList();
}
/**
* Legacy API, define either {@link #getCatalystStacks(int)} or {@link #getResultStacks(int)}
*
* @param recipe The recipe index to get items for.
* @return A list of the other {@link PositionedStack}s in this recipe relative to the top left corner of your
* recipe drawing space. For example fuel in furnaces.
*/
List<PositionedStack> getOtherStacks(int recipe);
/**
* Legacy API, use {@link #getResultStacks(int) getResultStacks.get(0)} if you want the primary result
*
* @param recipe The recipe index to get the result for.
* @return The recipe result {@link PositionedStack} relative to the top left corner of your recipe drawing space.
*/
PositionedStack getResultStack(int recipe);
/**
*
* @param recipe The recipe index to get the result for.
* @return A list of the result {@link PositionedStack}s relative to the top left corner of your recipe drawing
* space.
*/
default List<PositionedStack> getResultStacks(int recipe) {
final PositionedStack result = getResultStack(recipe);
return result != null ? Collections.singletonList(result) : Collections.emptyList();
}
/**
* A tick function called for updating progress bars and cycling damage items.
*/
@@ -6,6 +6,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -36,6 +37,7 @@ import codechicken.nei.recipe.GuiRecipeButton.UpdateRecipeButtonsEvent;
import codechicken.nei.recipe.Recipe.RecipeId;
import codechicken.nei.recipe.debug.DebugHandlerWidget;
import codechicken.nei.util.NEIMouseUtils;
import it.unimi.dsi.fastutil.longs.LongArraySet;
public class NEIRecipeWidget extends Widget {
@@ -54,6 +56,9 @@ public class NEIRecipeWidget extends Widget {
protected boolean showAsWidget = false;
protected List<GuiRecipeButton> recipeButtons = null;
// Handles re-draws for catalysts-outputs duplication to preserve full backward compatibility for otherStacks
private final Set<Long> drawnSlots = new LongArraySet();
public NEIRecipeWidget(RecipeHandlerRef handlerRef) {
this.handlerRef = handlerRef;
this.handlerInfo = GuiRecipeTab.getHandlerInfo(this.handlerRef.handler);
@@ -193,28 +198,40 @@ public class NEIRecipeWidget extends Widget {
GuiContainerManager.enableMatrixStackLogging();
for (PositionedStack pStack : getInputs()) {
final List<PositionedStack> inputs = getInputs();
final List<PositionedStack> catalysts = getCatalysts();
final List<PositionedStack> outputs = getOutputs();
if (!this.permutations.containsKey(pStack)) {
updatePermutationsFor(pStack);
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
for (PositionedStack pStack : getCatalysts()) {
if (!this.permutations.containsKey(pStack)) {
updatePermutationsFor(pStack);
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
for (PositionedStack pStack : getOutputs()) {
drawnSlots.clear();
for (PositionedStack pStack : inputs) {
drawnSlots.add(slotKey(pStack));
drawItem(pStack, mouseX, mouseY, yShift, false);
}
for (PositionedStack pStack : catalysts) {
if (!drawnSlots.add(slotKey(pStack))) {
continue;
}
if (!this.permutations.containsKey(pStack)) {
updatePermutationsFor(pStack);
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
for (PositionedStack pStack : outputs) {
if (!drawnSlots.add(slotKey(pStack))) {
continue;
}
if (!this.permutations.containsKey(pStack)) {
updatePermutationsFor(pStack);
}
drawItem(pStack, mouseX, mouseY, yShift, true);
}
GuiContainerManager.disableMatrixStackLogging();
this.handlerRef.handler.drawForeground(this.handlerRef.recipeIndex);
@@ -246,6 +263,10 @@ public class NEIRecipeWidget extends Widget {
DebugHandlerWidget.instance.drawGuiPlaceholder(this);
}
private static long slotKey(PositionedStack pStack) {
return ((long) pStack.relx << 32) ^ (pStack.rely & 0xFFFFFFFFL);
}
protected void drawItem(PositionedStack pStack, int mouseX, int mouseY, int yShift, boolean input) {
GuiContainerManager.drawItem(pStack.relx, pStack.rely, pStack.item);
@@ -656,13 +677,19 @@ public class NEIRecipeWidget extends Widget {
}
protected List<PositionedStack> getOutputs() {
final PositionedStack pStackResult = this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex);
return pStackResult != null ? Arrays.asList(pStackResult)
final List<PositionedStack> pStackResults = this.handlerRef.handler
.getResultStacks(this.handlerRef.recipeIndex);
return !pStackResults.isEmpty() ? pStackResults
: this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex);
}
protected List<PositionedStack> getCatalysts() {
if (this.handlerRef.handler.getResultStack(this.handlerRef.recipeIndex) == null) {
List<PositionedStack> catalysts = this.handlerRef.handler.getCatalystStacks(this.handlerRef.recipeIndex);
if (!catalysts.isEmpty()) {
return catalysts;
}
if (this.handlerRef.handler.getResultStacks(this.handlerRef.recipeIndex).isEmpty()) {
return Collections.emptyList();
}
return this.handlerRef.handler.getOtherStacks(this.handlerRef.recipeIndex);
@@ -54,19 +54,26 @@ public class Recipe {
public static RecipeId of(IRecipeHandler handler, int recipeIndex) {
final List<PositionedStack> ingredients = handler.getIngredientStacks(recipeIndex);
final String handlerName = GuiRecipeTab.getHandlerInfo(handler).getHandlerName();
PositionedStack pStackResult = handler.getResultStack(recipeIndex);
// Use getResultStacks even though getResult could work because if a handler, like GT for example, doesn't
// return anything from getResult, but will define all the outputs in getResultStacks we can still pick up
// the results here. If a handler still doesn't define getResultStacks, then it will naturally fall back to
// getResult anyway
List<PositionedStack> pStackResults = handler.getResultStacks(recipeIndex);
if (pStackResult == null) {
if (pStackResults.isEmpty()) {
for (PositionedStack otherStack : handler.getOtherStacks(recipeIndex)) {
if (!FluidContainerRegistry.isContainer(otherStack.items[0])
|| StackInfo.getFluid(otherStack.items[0]) != null) {
pStackResult = otherStack;
pStackResults.add(otherStack);
break;
}
}
}
return new RecipeId(extractItem(pStackResult), handlerName, extractIngredients(ingredients));
return new RecipeId(
extractItem(!pStackResults.isEmpty() ? pStackResults.get(0) : null),
handlerName,
extractIngredients(ingredients));
}
public static RecipeId of(JsonObject json) {
@@ -89,7 +96,7 @@ public class Recipe {
return new RecipeId(result, handlerName, ingredients);
}
public String getHandleName() {
public String getHandlerName() {
return this.handlerName;
}
@@ -389,8 +396,12 @@ public class Recipe {
ingredients.add(RecipeIngredient.of(positionedStack));
}
if (handler.getResultStack(recipeIndex) != null) {
results.add(RecipeIngredient.of(handler.getResultStack(recipeIndex)));
final List<PositionedStack> resultStacks = handler.getResultStacks(recipeIndex);
if (!resultStacks.isEmpty()) {
for (PositionedStack positionedStack : resultStacks) {
results.add(RecipeIngredient.of(positionedStack));
}
} else {
for (PositionedStack positionedStack : handler.getOtherStacks(recipeIndex)) {
results.add(RecipeIngredient.of(positionedStack));
@@ -4,6 +4,7 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -132,7 +133,48 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public abstract PositionedStack getResult();
/**
* The ingredients required to produce the result Use this if you have more than one ingredient
* The multiple items produced by this recipe, with position
*
* @return A list of positioned ingredient items.
*/
public List<PositionedStack> getResults() {
ArrayList<PositionedStack> stacks = new ArrayList<>();
try {
PositionedStack stack = getResult();
if (stack != null) stacks.add(stack);
} catch (ArithmeticException e) {
NEIClientConfig.logger.error("Error in getOtherStacks: " + e);
}
return stacks;
}
/**
* Return extra items that are not directly involved in the ingredient->result relationship. Eg fuels.
*
* @return A list of positioned ingredient items.
*/
public List<PositionedStack> getCatalysts() {
ArrayList<PositionedStack> stacks = new ArrayList<>();
try {
PositionedStack stack = getCatalyst();
if (stack != null) stacks.add(stack);
} catch (ArithmeticException e) {
NEIClientConfig.logger.error("Error in getCatalysts: " + e);
}
return stacks;
}
/**
* Simple utility
*
* @return The another positioned stack
*/
public PositionedStack getCatalyst() {
return null;
}
/**
* The ingredients required to produce the result. Use this if you have more than one ingredient
*
* @return A list of positioned ingredient items.
*/
@@ -151,6 +193,8 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
/**
* Legacy API
*
* Return extra items that are not directly involved in the ingredient->result relationship. Eg fuels. Use this
* if you have more than one other stack
*
@@ -289,9 +333,9 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
public boolean lastKeyTyped(GuiContainer gui, char keyChar, int keyCode) {
if (!canHandle(gui)) return false;
if (KeyManager.isHashDown("recipe.recipe")) {
if (KeyManager.isKeyDown("recipe.recipe")) {
return transferRect(gui, false);
} else if (KeyManager.isHashDown("recipe.usage")) {
} else if (KeyManager.isKeyDown("recipe.usage")) {
return transferRect(gui, true);
}
@@ -625,13 +669,23 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
}
public PositionedStack getResultStack(int recipe) {
List<PositionedStack> results = getResultStacks(recipe);
if (results == null || results.isEmpty()) return null;
return results.get(0);
}
public List<PositionedStack> getResultStacks(int recipe) {
try {
return arecipes.get(recipe).getResult();
return arecipes.get(recipe).getResults();
} catch (ArrayIndexOutOfBoundsException ignored) {
return null;
return Collections.emptyList();
}
}
public List<PositionedStack> getCatalystStacks(int recipe) {
return arecipes.get(recipe).getCatalysts();
}
public List<PositionedStack> getOtherStacks(int recipe) {
return arecipes.get(recipe).getOtherStacks();
}
@@ -673,9 +727,9 @@ public abstract class TemplateRecipeHandler implements ICraftingHandler, IUsageH
@Override
public boolean keyTyped(GuiRecipe<?> gui, char keyChar, int keyCode, int recipe) {
if (KeyManager.isHashDown("recipe.recipe")) {
if (KeyManager.isKeyDown("recipe.recipe")) {
return transferRect(gui, recipe, false);
} else if (KeyManager.isHashDown("recipe.usage")) {
} else if (KeyManager.isKeyDown("recipe.usage")) {
return transferRect(gui, recipe, true);
}
@@ -25,6 +25,34 @@ import codechicken.nei.recipe.StackInfo;
public class RecipeChainMath {
public interface ChainVisitor {
void enterRecipe(RecipeId recipeId, long multiplier);
void addIngredient(BookmarkItem ingrItem, long requestedAmount);
void addIngredientFresh(BookmarkItem ingrItem, long freshAmount);
void leaveRecipe();
}
private static class EmptyChainVisitor implements ChainVisitor {
@Override
public void enterRecipe(RecipeId recipeId, long multiplier) {}
@Override
public void addIngredient(BookmarkItem ingrItem, long requestedAmount) {}
@Override
public void addIngredientFresh(BookmarkItem ingrItem, long freshAmount) {}
@Override
public void leaveRecipe() {}
}
private static class ContainerItemResult {
public ItemStack stack;
@@ -38,6 +66,8 @@ public class RecipeChainMath {
}
}
private static final ChainVisitor EMPTY_CHAIN_VISITOR = new EmptyChainVisitor();
private static final ItemStack ROOT_ITEM = new ItemStack(Blocks.fire);
private static final RecipeId ROOT_RECIPE_ID = RecipeId
.of(ROOT_ITEM, "recipe-autocrafting", Collections.emptyList());
@@ -298,6 +328,10 @@ public class RecipeChainMath {
}
public RecipeChainMath refresh() {
return refresh(EMPTY_CHAIN_VISITOR);
}
public RecipeChainMath refresh(ChainVisitor visitor) {
final boolean isPausedItemDamageSound = StackInfo.isPausedItemDamageSound();
StackInfo.pauseItemDamageSound(true);
@@ -321,7 +355,7 @@ public class RecipeChainMath {
}
this.preferredItems.put(prefItem, prefItem);
calculateSuitableRecipe(prefItem, prefAmount, new ArrayList<>());
calculateSuitableRecipe(prefItem, prefAmount, new ArrayList<>(), visitor);
this.preferredItems.remove(prefItem);
}
}
@@ -338,9 +372,12 @@ public class RecipeChainMath {
return this;
}
private void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrAmount, List<RecipeId> visited) {
private void calculateSuitableRecipe(BookmarkItem ingrItem, long ingrAmount, List<RecipeId> visited,
ChainVisitor visitor) {
final BookmarkItem prefItem = this.preferredItems.get(ingrItem);
visitor.addIngredient(prefItem != null ? prefItem : ingrItem, ingrAmount);
// calculate existing containers
if (ingrAmount > 0) {
for (ItemStack stack : ingrItem.permutations.values()) {
@@ -358,7 +395,7 @@ public class RecipeChainMath {
if (ingrAmount > 0) {
for (BookmarkItem item : this.initialItems) {
if (item.containsItems(ingrItem)
&& (ingrAmount = addRequiredAmount(item, ingrAmount, item.getAmount())) == 0) {
&& (ingrAmount = addRequiredAmount(item, ingrAmount, item.getAmount(), visitor)) == 0) {
break;
}
}
@@ -366,33 +403,37 @@ public class RecipeChainMath {
// shift amount
if (prefItem == null) {
addRequiredAmount(ingrItem, ingrAmount, Long.MAX_VALUE);
addRequiredAmount(ingrItem, ingrAmount, Long.MAX_VALUE, visitor);
} else if (visited.contains(prefItem.recipeId)) {
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE);
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE, visitor);
} else {
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE);
addRequiredAmount(prefItem, ingrAmount, Long.MAX_VALUE, visitor);
final long multiplier = Math
.max(0, prefItem.getMultiplierFromAmount(this.requiredAmount.get(prefItem)) - prefItem.multiplier);
if (multiplier > 0) {
addShift(prefItem.recipeId, multiplier);
visited.add(prefItem.recipeId);
prepareIngredients(prefItem.recipeId, multiplier, visited);
prepareIngredients(prefItem.recipeId, multiplier, visited, visitor);
visited.remove(prefItem.recipeId);
}
}
}
private void prepareIngredients(RecipeId recipeId, long multiplier, List<RecipeId> visited) {
private void prepareIngredients(RecipeId recipeId, long multiplier, List<RecipeId> visited, ChainVisitor visitor) {
visitor.enterRecipe(recipeId, multiplier);
for (BookmarkItem item : this.recipeIngredients) {
if (!item.emptyFactor() && recipeId.equals(item.recipeId)) {
calculateSuitableRecipe(item, item.getAmount(multiplier), visited);
calculateSuitableRecipe(item, item.getAmount(multiplier), visited, visitor);
}
}
visitor.leaveRecipe();
}
private long addRequiredAmount(BookmarkItem prefItem, long ingrAmount, long maxAmount) {
private long addRequiredAmount(BookmarkItem prefItem, long ingrAmount, long maxAmount, ChainVisitor visitor) {
long shiftAmount = this.requiredAmount.getOrDefault(prefItem, 0L);
if (hasContainerItem(prefItem.itemStack)) {
@@ -411,8 +452,9 @@ public class RecipeChainMath {
final ContainerItemResult result = getToolsContainerItems(itemStack, steps);
if (result.stack == null) {
multiplier = steps / (steps - result.leftSteps);
steps = steps % (steps - result.leftSteps);
final long stepsPerReplacement = steps - result.leftSteps;
multiplier = Math.min(steps / stepsPerReplacement, maxAmount - shiftAmount);
steps -= multiplier * stepsPerReplacement;
} else {
steps = result.leftSteps;
}
@@ -448,6 +490,7 @@ public class RecipeChainMath {
ingrAmount -= initAmount;
}
visitor.addIngredientFresh(prefItem, shiftAmount - this.requiredAmount.getOrDefault(prefItem, 0L));
this.requiredAmount.put(prefItem, shiftAmount);
return ingrAmount;
@@ -1,4 +1,4 @@
package codechicken.nei.recipe;
package codechicken.nei.util;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
@@ -8,7 +8,7 @@ import net.minecraft.item.ItemStack;
import codechicken.nei.PositionedStack;
import codechicken.nei.api.ShortcutInputHandler;
public class ContainerRecipe extends Container {
public class EmptyContainer extends Container {
private ItemStack activeStack = null;
@@ -23,13 +23,13 @@ public class NEIKeyboardUtils {
keyHashMap.put(Keyboard.KEY_RSHIFT, SHIFT_HASH);
keyHashMap.put(Keyboard.KEY_LCONTROL, CTRL_HASH);
keyHashMap.put(Keyboard.KEY_LCONTROL, CTRL_HASH);
keyHashMap.put(Keyboard.KEY_RCONTROL, CTRL_HASH);
keyHashMap.put(Keyboard.KEY_LMETA, CTRL_HASH);
keyHashMap.put(Keyboard.KEY_RMETA, CTRL_HASH);
keyHashMap.put(Keyboard.KEY_LMENU, ALT_HASH);
keyHashMap.put(Keyboard.KEY_LMENU, ALT_HASH);
keyHashMap.put(Keyboard.KEY_RMENU, ALT_HASH);
}
private NEIKeyboardUtils() {}
@@ -67,7 +67,7 @@ public class NEIKeyboardUtils {
}
if (keyID != Keyboard.CHAR_NONE || hashText.isEmpty()) {
keyText.add(Keyboard.getKeyName(keyID));
keyText.add(keyID < 0 ? NEIMouseUtils.getKeyName(keyID + 100) : Keyboard.getKeyName(keyID));
}
return keyText.toString();
+21 -1
View File
@@ -62,6 +62,27 @@ ICONST_0
ICONST_1 #keep the destination alpha
INVOKESTATIC net/minecraft/client/renderer/OpenGlHelper.func_148821_a (IIII)V
list m_keyBindingOnTickAll
ILOAD 0
INVOKESTATIC codechicken/nei/asm/KeyBindingFixHooks.onTickAll (I)V
RETURN
list m_keyBindingSetStateAll
ILOAD 0
ILOAD 1
INVOKESTATIC codechicken/nei/asm/KeyBindingFixHooks.setKeyBindStateAll (IZ)V
RETURN
list saveOptionsHook
INVOKESTATIC codechicken/nei/asm/SaveOptionsFixHooks.onBeforeSaveOptions ()V
list n_saveOptionsSendSettings
ALOAD 0
INVOKEVIRTUAL net/minecraft/client/settings/GameSettings.func_82879_c ()V
list saveOptionsRestoreHook
INVOKESTATIC codechicken/nei/asm/SaveOptionsFixHooks.onAfterSaveOptions ()V
#begin GuiContainer patches
@@ -383,4 +404,3 @@ INVOKEVIRTUAL codechicken/nei/guihook/GuiContainerManager.objectUnderMouse (II)Z
IFEQ LCONT
RETURN
LCONT
@@ -32,7 +32,9 @@ nei.bookmark.group.pull_items=Pull all Items
nei.bookmark.group.pull_items_shift=Pull missing Items
nei.bookmark.group.craft_missing=Craft Missing Items
nei.bookmark.group.craft_all=Craft Items
nei.bookmark.group.chat_link=Send Bookmark Group to Chat
nei.bookmark.item.sorting=Move Position
nei.bookmark.item.chat_link=Send Item to Chat
nei.bookmark.remove_recipe=Remove Recipe
nei.bookmark.remove_item=Remove Item
nei.bookmark.add_item=Add Bookmark
@@ -42,6 +44,7 @@ nei.bookmark.add_item_with_count=Add Bookmark with Count
nei.bookmark.change_quantity=Change Quantity
nei.bookmark.change_quantity_step=Change Quantity with Multiplier
nei.bookmark.recipe.toggle_collapse=Toggle Collapse/Expand
nei.bookmark.recipe.chat_link=Send Recipe to Chat
nei.bookmark.crafting_chain=Crafting Chain
nei.bookmark.crafting_chain.input=Ingredients
nei.bookmark.crafting_chain.missing=Missing Items
@@ -68,6 +71,7 @@ nei.itempanel.infinite_item=Give Infinite Item
nei.itempanel.copy_name=Copy Item Name
nei.itempanel.copy_oredict=Copy Item OreDictionary
nei.itempanel.copy_id=Copy Item ID
nei.itempanel.chat_link_item=Send item link to chat
nei.enchant=Enchant
@@ -143,6 +147,9 @@ nei.chat.chunkoverlay.3=Chunk Overlay: GT Ore Veins
nei.chat.moboverlay.0=Mob Spawning Overlay: §cOff
nei.chat.moboverlay.1=Mob Spawning Overlay: §2On
nei.chat.item_link.text=<%s> §6Click to add bookmark: %s
nei.chat.item_link.item.text=Bookmark Item
nei.chat.item_link.recipe.text=Bookmark Recipe
nei.chat.item_link.group.text=Bookmark Group
nei.chat.bookmark_added.text=Bookmark added!
nei.chat.recipeid.many_params=Too many parameters! Usage: %s
@@ -194,6 +201,7 @@ nei.options.keys.bookmark.add=Add Bookmark
nei.options.keys.bookmark.remove_recipe=Remove Group from Bookmarks (Shift +)
nei.options.keys.bookmark.pull_items=Pull all Bookmarked Items
nei.options.keys.bookmark.hide=Toggle Bookmark Panel Visibility
nei.options.keys.bookmark.chat_link=Send link to chat (Ctrl +)
nei.options.keys.gui.overlay=Fill/Overlay Crafting Grid
nei.options.keys.gui.getprevioussearch=Previous Search
nei.options.keys.gui.getnextsearch=Next Search
@@ -202,7 +210,6 @@ nei.options.keys.copy.name=Copy Item Name (Ctrl +)
nei.options.keys.gui.craft_items=Craft Items (Shift +)
nei.options.keys.copy.oredict=Copy Item OreDictionary (Ctrl +)
nei.options.keys.copy.identifier=Copy Item ID (Ctrl +)
nei.options.keys.copy.chat_link_item=Send item link to chat (Ctrl +)
nei.options.keys.itemzoom.toggle=Toggle Item Zoom (Shift +)
nei.options.keys.itemzoom.hold=Hold to Display Item Zoom
nei.options.keys.itemzoom.zoom_in=Item Zoom In
@@ -605,6 +612,7 @@ nei.recipe.overlay.overlayRecipe=Overlay Recipe
nei.recipe.overlay.fillCraftingGrid=Fill Crafting Grid
nei.recipe.overlay.bookmarkRecipe=Save with Recipe
nei.recipe.overlay.bookmarkRecipeAndCount=Save with Recipe & Count
nei.recipe.overlay.sendRecipeToChat=Send Recipe to Chat
nei.recipe.favorite=Favorite
nei.recipe.favorite.bookmark_recipe=Save Recipe Tree
@@ -20,6 +20,7 @@ nei.bookmark.group.sorting=Сортировка
nei.bookmark.group.remove_recipe=Удалить группу
nei.bookmark.group.pull_items=Переместить все предметы
nei.bookmark.group.pull_items_shift=Переместить недостающие предметы
nei.bookmark.group.chat_link=Отправить группу в чат
nei.bookmark.crafting_chain=Цепочка крафта
nei.bookmark.crafting_chain.input=Ингредиенты
nei.bookmark.crafting_chain.output=Результаты
@@ -141,6 +142,7 @@ nei.options.keys.bookmark.add=Добавить закладку
nei.options.keys.bookmark.remove_recipe=Удалить группу из закладок (Shift +)
nei.options.keys.bookmark.pull_items=Переместить все отмеченные предметы
nei.options.keys.bookmark.hide=Переключить видимость панели закладок
nei.options.keys.bookmark.chat_link=Отправить ссылку в чат (Ctrl +)
nei.options.keys.gui.overlay=Заполнить сетку крафта
nei.options.keys.gui.getprevioussearch=Предыдущий поиск
nei.options.keys.gui.getnextsearch=Следующий поиск
@@ -149,7 +151,6 @@ nei.options.keys.copy.name=Скопировать название предме
nei.options.keys.gui.craft_items=Создать предметы (Shift +)
nei.options.keys.copy.oredict=Скопировать OreDictionary предмета (Ctrl +)
nei.options.keys.copy.identifier=Скопировать ID предмета (Ctrl +)
nei.options.keys.copy.chat_link_item=Отправить ссылку на предмет в чат (Ctrl +)
nei.options.keys.itemzoom.toggle=Переключить увеличение предмета (Shift +)
nei.options.keys.itemzoom.hold=Удерживать для отображения увеличения предмета
nei.options.keys.itemzoom.zoom_in=Увеличить предмет
@@ -422,6 +423,7 @@ nei.recipe.overlay.missing=Отсутствует следующее
nei.recipe.overlay.mismatch=Несоответствие сетки крафта
nei.recipe.overlay.fillCraftingGrid=Заполнить сетку крафта
nei.recipe.overlay.bookmarkRecipe=Сохранить с рецептом
nei.recipe.overlay.sendRecipeToChat=Отправить рецепт в чат
nei.itemsort.minecraft=Minecraft
nei.itemsort.minecraft.tip=Сортирует предметы из ванильного Minecraft первыми