diff --git a/CHANGELOG.md b/CHANGELOG.md index a477abb5c..eb88516ea 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Refined Storage Changelog +### 1.5.24 +- The Grid now displays last modified information (player name and date) and size on tooltips of stacks (raoulvdberge) + ### 1.5.23 - Fixed duplication bug with autocrafting (raoulvdberge) - Fixed Fluid Interface with Stack Upgrade not exporting fluids (raoulvdberge) diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java b/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java index 7ec8f27a5..7841bdb45 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/network/INetwork.java @@ -8,6 +8,7 @@ import com.raoulvdberge.refinedstorage.api.network.readerwriter.IReaderWriterCha import com.raoulvdberge.refinedstorage.api.network.security.ISecurityManager; import com.raoulvdberge.refinedstorage.api.storage.IStorage; import com.raoulvdberge.refinedstorage.api.storage.IStorageCache; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; import com.raoulvdberge.refinedstorage.api.util.IComparer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; @@ -274,6 +275,16 @@ public interface INetwork { return extractFluid(stack, size, IComparer.COMPARE_NBT, simulate); } + /** + * @return the storage tracker for items + */ + IStorageTracker getItemStorageTracker(); + + /** + * @return the storage tracker for fluids + */ + IStorageTracker getFluidStorageTracker(); + /** * @return the world where this network is in */ diff --git a/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageTracker.java b/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageTracker.java new file mode 100644 index 000000000..ce249041a --- /dev/null +++ b/src/main/java/com/raoulvdberge/refinedstorage/api/storage/IStorageTracker.java @@ -0,0 +1,37 @@ +package com.raoulvdberge.refinedstorage.api.storage; + +import net.minecraft.entity.player.EntityPlayer; + +import javax.annotation.Nullable; + +/** + * Keeps track of when a stack is changed in the system. + */ +public interface IStorageTracker { + interface IStorageTrackerEntry { + /** + * @return the modification time + */ + long getTime(); + + /** + * @return the name of the player who last modified the stack + */ + String getName(); + } + + /** + * Updates the storage tracker entry for a stack, changing it's player name and modification time. + * + * @param player player + * @param stack the stack + */ + void changed(EntityPlayer player, T stack); + + /** + * @param stack the stack + * @return the entry, or null if the stack hasn't been modified yet + */ + @Nullable + IStorageTrackerEntry get(T stack); +} diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java index 9aed443c2..28293ee06 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/FluidGridHandler.java @@ -55,6 +55,8 @@ public class FluidGridHandler implements IFluidGridHandler { if (bucket != null) { IFluidHandlerItem fluidHandler = bucket.getCapability(CapabilityFluidHandler.FLUID_HANDLER_ITEM_CAPABILITY, null); + network.getFluidStorageTracker().changed(player, stack.copy()); + fluidHandler.fill(network.extractFluid(stack, Fluid.BUCKET_VOLUME, false), true); if (shift) { @@ -85,6 +87,8 @@ public class FluidGridHandler implements IFluidGridHandler { Pair result = StackUtils.getFluid(container, true); if (result.getValue() != null && network.insertFluid(result.getValue(), result.getValue().amount, true) == null) { + network.getFluidStorageTracker().changed(player, result.getValue().copy()); + result = StackUtils.getFluid(container, false); network.insertFluid(result.getValue(), result.getValue().amount, false); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java index 8964549a4..797ce5f1c 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandler.java @@ -72,6 +72,8 @@ public class ItemGridHandler implements IItemGridHandler { ItemStack took = network.extractItem(item, size, true); if (took != null) { + network.getItemStorageTracker().changed(player, took.copy()); + if ((flags & EXTRACT_SHIFT) == EXTRACT_SHIFT) { IItemHandler playerInventory = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); @@ -106,6 +108,8 @@ public class ItemGridHandler implements IItemGridHandler { return stack; } + network.getItemStorageTracker().changed(player, stack.copy()); + ItemStack remainder = network.insertItem(stack, stack.getCount(), false); INetworkItem networkItem = network.getNetworkItemHandler().getItem(player); @@ -126,6 +130,8 @@ public class ItemGridHandler implements IItemGridHandler { ItemStack stack = player.inventory.getItemStack(); int size = single ? 1 : stack.getCount(); + network.getItemStorageTracker().changed(player, stack.copy()); + if (single) { if (network.insertItem(stack, size, true) == null) { network.insertItem(stack, size, false); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandlerPortable.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandlerPortable.java index 95635cf8d..422b18caf 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandlerPortable.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/grid/handler/ItemGridHandlerPortable.java @@ -72,6 +72,8 @@ public class ItemGridHandlerPortable implements IItemGridHandler { ItemStack took = portableGrid.getStorage().extract(item, size, IComparer.COMPARE_DAMAGE | IComparer.COMPARE_NBT, true); if (took != null) { + portableGrid.getStorageTracker().changed(player, took.copy()); + if ((flags & EXTRACT_SHIFT) == EXTRACT_SHIFT) { IItemHandler playerInventory = player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); @@ -103,6 +105,8 @@ public class ItemGridHandlerPortable implements IItemGridHandler { return stack; } + portableGrid.getStorageTracker().changed(player, stack.copy()); + ItemStack remainder = portableGrid.getStorage().insert(stack, stack.getCount(), false); portableGrid.drainEnergy(RS.INSTANCE.config.portableGridInsertUsage); @@ -119,6 +123,8 @@ public class ItemGridHandlerPortable implements IItemGridHandler { ItemStack stack = player.inventory.getItemStack(); int size = single ? 1 : stack.getCount(); + portableGrid.getStorageTracker().changed(player, stack.copy()); + if (single) { if (portableGrid.getStorage().insert(stack, size, true) == null) { portableGrid.getStorage().insert(stack, size, false); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeStorageMonitor.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeStorageMonitor.java index f90e61a1a..4e160a9d3 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeStorageMonitor.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/network/node/NetworkNodeStorageMonitor.java @@ -97,6 +97,7 @@ public class NetworkNodeStorageMonitor extends NetworkNode implements IComparabl ItemStack inserted = deposit.getKey(); long insertedAt = deposit.getValue(); + // @todo: why doesn't this break on servers??? if (Minecraft.getSystemTime() - insertedAt < DEPOSIT_ALL_MAX_DELAY) { for (int i = 0; i < player.inventory.getSizeInventory(); ++i) { ItemStack toInsert = player.inventory.getStackInSlot(i); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java index 062d4bfee..12f367dd2 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageCacheItemPortable.java @@ -3,6 +3,7 @@ package com.raoulvdberge.refinedstorage.apiimpl.storage; import com.raoulvdberge.refinedstorage.RS; import com.raoulvdberge.refinedstorage.api.storage.IStorage; import com.raoulvdberge.refinedstorage.api.storage.IStorageCache; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; import com.raoulvdberge.refinedstorage.api.util.IStackList; import com.raoulvdberge.refinedstorage.apiimpl.API; import com.raoulvdberge.refinedstorage.network.MessageGridItemDelta; @@ -12,6 +13,7 @@ import com.raoulvdberge.refinedstorage.util.StackUtils; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.item.ItemStack; +import net.minecraftforge.fml.common.network.ByteBufUtils; import javax.annotation.Nonnull; import java.util.Collections; @@ -45,7 +47,7 @@ public class StorageCacheItemPortable implements IStorageCache { list.add(stack, size); if (!rebuilding) { - portableGrid.getWatchers().forEach(w -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(null, stack, size), (EntityPlayerMP) w)); + portableGrid.getWatchers().forEach(w -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(null, portableGrid.getStorageTracker(), stack, size), (EntityPlayerMP) w)); listeners.forEach(Runnable::run); } @@ -54,7 +56,7 @@ public class StorageCacheItemPortable implements IStorageCache { @Override public void remove(@Nonnull ItemStack stack, int size, boolean batched) { if (list.remove(stack, size)) { - portableGrid.getWatchers().forEach(w -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(null, stack, -size), (EntityPlayerMP) w)); + portableGrid.getWatchers().forEach(w -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(null, portableGrid.getStorageTracker(), stack, -size), (EntityPlayerMP) w)); listeners.forEach(Runnable::run); } @@ -91,6 +93,13 @@ public class StorageCacheItemPortable implements IStorageCache { for (ItemStack stack : list.getStacks()) { StackUtils.writeItemStack(buf, stack, null, false); + + IStorageTracker.IStorageTrackerEntry entry = portableGrid.getStorageTracker().get(stack); + buf.writeBoolean(entry != null); + if (entry != null) { + buf.writeLong(entry.getTime()); + ByteBufUtils.writeUTF8String(buf, entry.getName()); + } } }, false), (EntityPlayerMP) player); } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerEntry.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerEntry.java new file mode 100644 index 000000000..0ddaa6474 --- /dev/null +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerEntry.java @@ -0,0 +1,30 @@ +package com.raoulvdberge.refinedstorage.apiimpl.storage; + +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; +import io.netty.buffer.ByteBuf; +import net.minecraftforge.fml.common.network.ByteBufUtils; + +public class StorageTrackerEntry implements IStorageTracker.IStorageTrackerEntry { + private long time; + private String name; + + public StorageTrackerEntry(long time, String name) { + this.time = time; + this.name = name; + } + + public StorageTrackerEntry(ByteBuf buf) { + this.time = buf.readLong(); + this.name = ByteBufUtils.readUTF8String(buf); + } + + @Override + public long getTime() { + return time; + } + + @Override + public String getName() { + return name; + } +} diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerFluid.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerFluid.java new file mode 100644 index 000000000..6a648edc4 --- /dev/null +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerFluid.java @@ -0,0 +1,81 @@ +package com.raoulvdberge.refinedstorage.apiimpl.storage; + +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; +import com.raoulvdberge.refinedstorage.api.util.IComparer; +import com.raoulvdberge.refinedstorage.apiimpl.API; +import gnu.trove.map.hash.TCustomHashMap; +import gnu.trove.strategy.HashingStrategy; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fluids.FluidStack; + +import java.util.Map; + +public class StorageTrackerFluid implements IStorageTracker { + private static final String NBT_STACK = "Stack"; + private static final String NBT_NAME = "Name"; + private static final String NBT_TIME = "Time"; + + private Map changes = new TCustomHashMap<>(new HashingStrategy() { + @Override + public int computeHashCode(FluidStack stack) { + return API.instance().getFluidStackHashCode(stack); + } + + @Override + public boolean equals(FluidStack left, FluidStack right) { + return API.instance().getComparer().isEqual(left, right, IComparer.COMPARE_NBT); + } + }); + + private Runnable listener; + + public StorageTrackerFluid(Runnable listener) { + this.listener = listener; + } + + @Override + public void changed(EntityPlayer player, FluidStack stack) { + changes.put(stack, new StorageTrackerEntry(MinecraftServer.getCurrentTimeMillis(), player.getName())); + + listener.run(); + } + + @Override + public IStorageTrackerEntry get(FluidStack stack) { + return changes.get(stack); + } + + public void readFromNBT(NBTTagList list) { + for (int i = 0; i < list.tagCount(); ++i) { + NBTTagCompound tag = list.getCompoundTagAt(i); + + FluidStack stack = FluidStack.loadFluidStackFromNBT(tag.getCompoundTag(NBT_STACK)); + + if (stack != null) { + changes.put( + stack, + new StorageTrackerEntry(tag.getLong(NBT_TIME), tag.getString(NBT_NAME)) + ); + } + } + } + + public NBTTagList serializeNBT() { + NBTTagList list = new NBTTagList(); + + for (Map.Entry entry : changes.entrySet()) { + NBTTagCompound tag = new NBTTagCompound(); + + tag.setLong(NBT_TIME, entry.getValue().getTime()); + tag.setString(NBT_NAME, entry.getValue().getName()); + tag.setTag(NBT_STACK, entry.getKey().writeToNBT(new NBTTagCompound())); + + list.appendTag(tag); + } + + return list; + } +} diff --git a/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerItem.java b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerItem.java new file mode 100644 index 000000000..7e514efae --- /dev/null +++ b/src/main/java/com/raoulvdberge/refinedstorage/apiimpl/storage/StorageTrackerItem.java @@ -0,0 +1,80 @@ +package com.raoulvdberge.refinedstorage.apiimpl.storage; + +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; +import com.raoulvdberge.refinedstorage.apiimpl.API; +import gnu.trove.map.hash.TCustomHashMap; +import gnu.trove.strategy.HashingStrategy; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.server.MinecraftServer; + +import java.util.Map; + +public class StorageTrackerItem implements IStorageTracker { + private static final String NBT_STACK = "Stack"; + private static final String NBT_NAME = "Name"; + private static final String NBT_TIME = "Time"; + + private Map changes = new TCustomHashMap<>(new HashingStrategy() { + @Override + public int computeHashCode(ItemStack stack) { + return API.instance().getItemStackHashCode(stack); + } + + @Override + public boolean equals(ItemStack left, ItemStack right) { + return API.instance().getComparer().isEqualNoQuantity(left, right); + } + }); + + private Runnable listener; + + public StorageTrackerItem(Runnable listener) { + this.listener = listener; + } + + @Override + public void changed(EntityPlayer player, ItemStack stack) { + changes.put(stack, new StorageTrackerEntry(MinecraftServer.getCurrentTimeMillis(), player.getName())); + + listener.run(); + } + + @Override + public IStorageTrackerEntry get(ItemStack stack) { + return changes.get(stack); + } + + public void readFromNBT(NBTTagList list) { + for (int i = 0; i < list.tagCount(); ++i) { + NBTTagCompound tag = list.getCompoundTagAt(i); + + ItemStack stack = new ItemStack(tag.getCompoundTag(NBT_STACK)); + + if (!stack.isEmpty()) { + changes.put( + stack, + new StorageTrackerEntry(tag.getLong(NBT_TIME), tag.getString(NBT_NAME)) + ); + } + } + } + + public NBTTagList serializeNBT() { + NBTTagList list = new NBTTagList(); + + for (Map.Entry entry : changes.entrySet()) { + NBTTagCompound tag = new NBTTagCompound(); + + tag.setLong(NBT_TIME, entry.getValue().getTime()); + tag.setString(NBT_NAME, entry.getValue().getName()); + tag.setTag(NBT_STACK, entry.getKey().serializeNBT()); + + list.appendTag(tag); + } + + return list; + } +} diff --git a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/GuiGrid.java b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/GuiGrid.java index 988875788..e7f480a63 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/GuiGrid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/GuiGrid.java @@ -2,6 +2,7 @@ package com.raoulvdberge.refinedstorage.gui.grid; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; import com.google.common.collect.Multimaps; import com.raoulvdberge.refinedstorage.RS; import com.raoulvdberge.refinedstorage.RSKeyBindings; @@ -25,18 +26,26 @@ import com.raoulvdberge.refinedstorage.tile.data.TileDataManager; import com.raoulvdberge.refinedstorage.tile.grid.TileGrid; import com.raoulvdberge.refinedstorage.tile.grid.portable.IPortableGrid; import com.raoulvdberge.refinedstorage.tile.grid.portable.TilePortableGrid; +import com.raoulvdberge.refinedstorage.util.RenderUtils; +import com.raoulvdberge.refinedstorage.util.TimeUtils; import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.FontRenderer; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.resources.I18n; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.client.event.RenderTooltipEvent; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fml.client.config.GuiCheckBox; +import net.minecraftforge.fml.client.config.GuiUtils; import net.minecraftforge.fml.common.FMLCommonHandler; import org.lwjgl.input.Keyboard; @@ -268,7 +277,7 @@ public class GuiGrid extends GuiBase implements IGridDisplay { } } - private boolean isOverSlotWithStack() { + public boolean isOverSlotWithStack() { return grid.isActive() && isOverSlot() && slotNumber < STACKS.size(); } @@ -478,9 +487,7 @@ public class GuiGrid extends GuiBase implements IGridDisplay { } if (isOverSlotWithStack()) { - IGridStack stack = STACKS.get(slotNumber); - - drawTooltip(stack instanceof GridStackItem ? ((GridStackItem) stack).getStack() : ItemStack.EMPTY, mouseX, mouseY, stack.getTooltip(GuiScreen.isShiftKeyDown() && GuiScreen.isCtrlKeyDown())); + drawGridTooltip(STACKS.get(slotNumber), mouseX, mouseY); } if (isOverClear(mouseX, mouseY)) { @@ -496,6 +503,142 @@ public class GuiGrid extends GuiBase implements IGridDisplay { } } + // Copied with some tweaks from GuiUtils#drawHoveringText(@Nonnull final ItemStack stack, List textLines, int mouseX, int mouseY, int screenWidth, int screenHeight, int maxTextWidth, FontRenderer font) + public void drawGridTooltip(IGridStack gridStack, int mouseX, int mouseY) { + // RS BEGIN + List textLines = Lists.newArrayList(gridStack.getTooltip().split("\n")); + + textLines.add(""); + + if (gridStack.getTrackerEntry() != null) { + textLines.add(""); + } + + ItemStack stack = gridStack instanceof GridStackItem ? ((GridStackItem) gridStack).getStack() : ItemStack.EMPTY; + // RS END + + if (!textLines.isEmpty()) { + RenderTooltipEvent.Pre event = new RenderTooltipEvent.Pre(stack, textLines, mouseX, mouseY, screenWidth, screenHeight, -1, fontRenderer); + if (MinecraftForge.EVENT_BUS.post(event)) { + return; + } + mouseX = event.getX(); + mouseY = event.getY(); + screenWidth = event.getScreenWidth(); + screenHeight = event.getScreenHeight(); + FontRenderer font = event.getFontRenderer(); + + // RS BEGIN + float textScale = font.getUnicodeFlag() ? 1F : 0.7F; + // RS END + + GlStateManager.disableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.disableDepth(); + int tooltipTextWidth = 0; + + for (String textLine : textLines) { + int textLineWidth = font.getStringWidth(textLine); + + if (textLineWidth > tooltipTextWidth) { + tooltipTextWidth = textLineWidth; + } + } + + // RS BEGIN + int size = (int) (font.getStringWidth(I18n.format("misc.refinedstorage:total", gridStack.getFormattedFullQuantity())) * textScale); + + if (size > tooltipTextWidth) { + tooltipTextWidth = size; + } + + if (gridStack.getTrackerEntry() != null) { + size = (int) (font.getStringWidth(TimeUtils.getAgo(gridStack.getTrackerEntry().getTime(), gridStack.getTrackerEntry().getName())) * textScale); + + if (size > tooltipTextWidth) { + tooltipTextWidth = size; + } + } + // RS END + + int titleLinesCount = 1; + int tooltipX = mouseX + 12; + + int tooltipY = mouseY - 12; + int tooltipHeight = 8; + + if (textLines.size() > 1) { + tooltipHeight += (textLines.size() - 1) * 10; + if (textLines.size() > titleLinesCount) { + tooltipHeight += 2; + } + } + + if (tooltipY + tooltipHeight + 6 > screenHeight) { + tooltipY = screenHeight - tooltipHeight - 6; + } + + final int zLevel = 300; + final int backgroundColor = 0xF0100010; + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 4, tooltipX + tooltipTextWidth + 3, tooltipY - 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 4, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(zLevel, tooltipX - 4, tooltipY - 3, tooltipX - 3, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + GuiUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 3, tooltipY - 3, tooltipX + tooltipTextWidth + 4, tooltipY + tooltipHeight + 3, backgroundColor, backgroundColor); + final int borderColorStart = 0x505000FF; + final int borderColorEnd = (borderColorStart & 0xFEFEFE) >> 1 | borderColorStart & 0xFF000000; + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3 + 1, tooltipX - 3 + 1, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + GuiUtils.drawGradientRect(zLevel, tooltipX + tooltipTextWidth + 2, tooltipY - 3 + 1, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3 - 1, borderColorStart, borderColorEnd); + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY - 3, tooltipX + tooltipTextWidth + 3, tooltipY - 3 + 1, borderColorStart, borderColorStart); + GuiUtils.drawGradientRect(zLevel, tooltipX - 3, tooltipY + tooltipHeight + 2, tooltipX + tooltipTextWidth + 3, tooltipY + tooltipHeight + 3, borderColorEnd, borderColorEnd); + + MinecraftForge.EVENT_BUS.post(new RenderTooltipEvent.PostBackground(stack, textLines, tooltipX, tooltipY, font, tooltipTextWidth, tooltipHeight)); + int tooltipTop = tooltipY; + + for (int lineNumber = 0; lineNumber < textLines.size(); ++lineNumber) { + String line = textLines.get(lineNumber); + font.drawStringWithShadow(line, (float) tooltipX, (float) tooltipY, -1); + + if (lineNumber + 1 == titleLinesCount) { + tooltipY += 2; + } + + tooltipY += 10; + } + + MinecraftForge.EVENT_BUS.post(new RenderTooltipEvent.PostText(stack, textLines, tooltipX, tooltipTop, font, tooltipTextWidth, tooltipHeight)); + + // RS BEGIN + GlStateManager.pushMatrix(); + GlStateManager.scale(textScale, textScale, 1); + + font.drawStringWithShadow( + TextFormatting.GRAY + I18n.format("misc.refinedstorage:total", gridStack.getFormattedFullQuantity()), + RenderUtils.getOffsetOnScale(tooltipX, textScale), + RenderUtils.getOffsetOnScale(tooltipTop + tooltipHeight - (gridStack.getTrackerEntry() != null ? 15 : 6) - (font.getUnicodeFlag() ? 2 : 0), textScale), + -1 + ); + + if (gridStack.getTrackerEntry() != null) { + font.drawStringWithShadow( + TextFormatting.GRAY + TimeUtils.getAgo(gridStack.getTrackerEntry().getTime(), gridStack.getTrackerEntry().getName()), + RenderUtils.getOffsetOnScale(tooltipX, textScale), + RenderUtils.getOffsetOnScale(tooltipTop + tooltipHeight - 6 - (font.getUnicodeFlag() ? 2 : 0), textScale), + -1 + ); + } + + GlStateManager.popMatrix(); + // RS END + + GlStateManager.enableLighting(); + GlStateManager.enableDepth(); + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableRescaleNormal(); + } + } + @Override protected void actionPerformed(GuiButton button) throws IOException { super.actionPerformed(button); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/filtering/GridFilterTooltip.java b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/filtering/GridFilterTooltip.java index 6666787dc..2f88ef1dc 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/filtering/GridFilterTooltip.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/filtering/GridFilterTooltip.java @@ -13,7 +13,7 @@ public class GridFilterTooltip implements Predicate { @Override public boolean test(IGridStack stack) { - String otherTooltip = stack.getTooltip(false).trim().toLowerCase(); + String otherTooltip = stack.getTooltip().trim().toLowerCase(); if (!otherTooltip.contains("\n")) { return false; diff --git a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackFluid.java b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackFluid.java index e9a765a67..4187629b7 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackFluid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackFluid.java @@ -1,18 +1,23 @@ package com.raoulvdberge.refinedstorage.gui.grid.stack; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; import com.raoulvdberge.refinedstorage.gui.GuiBase; import com.raoulvdberge.refinedstorage.util.RenderUtils; -import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fluids.FluidStack; import org.apache.commons.lang3.tuple.Pair; +import javax.annotation.Nullable; + public class GridStackFluid implements IGridStack { private int hash; private FluidStack stack; + @Nullable + private IStorageTracker.IStorageTrackerEntry entry; - public GridStackFluid(Pair data) { + public GridStackFluid(Pair data, @Nullable IStorageTracker.IStorageTrackerEntry entry) { this.hash = data.getLeft(); this.stack = data.getRight(); + this.entry = entry; } public FluidStack getStack() { @@ -40,8 +45,8 @@ public class GridStackFluid implements IGridStack { } @Override - public String getTooltip(boolean quantity) { - return stack.getFluid().getLocalizedName(stack) + (quantity ? (" " + TextFormatting.GRAY + "(" + RenderUtils.QUANTITY_FORMATTER_UNFORMATTED.format(stack.amount) + " mB)" + TextFormatting.RESET) : ""); + public String getTooltip() { + return stack.getFluid().getLocalizedName(stack); } @Override @@ -49,6 +54,11 @@ public class GridStackFluid implements IGridStack { return stack.amount; } + @Override + public String getFormattedFullQuantity() { + return RenderUtils.QUANTITY_FORMATTER_UNFORMATTED.format(getQuantity()) + " mB"; + } + @Override public void draw(GuiBase gui, int x, int y) { GuiBase.FLUID_RENDERER.draw(gui.mc, x, y, stack); @@ -61,6 +71,17 @@ public class GridStackFluid implements IGridStack { return stack; } + @Nullable + @Override + public IStorageTracker.IStorageTrackerEntry getTrackerEntry() { + return entry; + } + + @Override + public void setTrackerEntry(@Nullable IStorageTracker.IStorageTrackerEntry entry) { + this.entry = entry; + } + @Override public boolean equals(Object obj) { return obj instanceof GridStackFluid && ((GridStackFluid) obj).getStack().isFluidEqual(stack); diff --git a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackItem.java b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackItem.java index 0a99c5bc7..952854314 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackItem.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/GridStackItem.java @@ -1,5 +1,7 @@ package com.raoulvdberge.refinedstorage.gui.grid.stack; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; +import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageTrackerEntry; import com.raoulvdberge.refinedstorage.gui.GuiBase; import com.raoulvdberge.refinedstorage.util.RenderUtils; import com.raoulvdberge.refinedstorage.util.StackUtils; @@ -12,6 +14,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.text.TextFormatting; import net.minecraftforge.oredict.OreDictionary; +import javax.annotation.Nullable; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @@ -22,6 +25,8 @@ public class GridStackItem implements IGridStack { private boolean craftable; private boolean displayCraftText; private String[] oreIds = null; + @Nullable + private IStorageTracker.IStorageTrackerEntry entry; public GridStackItem(ByteBuf buf) { this.stack = StackUtils.readItemStack(buf); @@ -29,6 +34,10 @@ public class GridStackItem implements IGridStack { this.craftable = buf.readBoolean(); setDisplayCraftText(buf.readBoolean()); + + if (buf.readBoolean()) { + this.entry = new StorageTrackerEntry(buf); + } } public ItemStack getStack() { @@ -84,7 +93,7 @@ public class GridStackItem implements IGridStack { } @Override - public String getTooltip(boolean quantity) { + public String getTooltip() { try { List lines = stack.getTooltip(Minecraft.getMinecraft().player, Minecraft.getMinecraft().gameSettings.advancedItemTooltips ? ITooltipFlag.TooltipFlags.ADVANCED : ITooltipFlag.TooltipFlags.NORMAL); @@ -97,10 +106,6 @@ public class GridStackItem implements IGridStack { } } - if (quantity && !lines.isEmpty()) { - lines.set(0, lines.get(0) + " " + TextFormatting.GRAY + "(" + RenderUtils.QUANTITY_FORMATTER_UNFORMATTED.format(stack.getCount()) + "x)" + TextFormatting.RESET); - } - return lines.stream().collect(Collectors.joining("\n")); } catch (Throwable t) { return ""; @@ -112,6 +117,11 @@ public class GridStackItem implements IGridStack { return stack.getCount(); } + @Override + public String getFormattedFullQuantity() { + return RenderUtils.QUANTITY_FORMATTER_UNFORMATTED.format(getQuantity()); + } + @Override public void draw(GuiBase gui, int x, int y) { String text = null; @@ -130,6 +140,17 @@ public class GridStackItem implements IGridStack { return stack; } + @Nullable + @Override + public IStorageTracker.IStorageTrackerEntry getTrackerEntry() { + return entry; + } + + @Override + public void setTrackerEntry(@Nullable IStorageTracker.IStorageTrackerEntry entry) { + this.entry = entry; + } + @Override public boolean equals(Object obj) { return obj instanceof IGridStack && ((GridStackItem) obj).getHash() == hash; diff --git a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/IGridStack.java b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/IGridStack.java index 886b17689..978a7ba67 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/IGridStack.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/gui/grid/stack/IGridStack.java @@ -1,7 +1,10 @@ package com.raoulvdberge.refinedstorage.gui.grid.stack; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; import com.raoulvdberge.refinedstorage.gui.GuiBase; +import javax.annotation.Nullable; + public interface IGridStack { int getHash(); @@ -11,11 +14,18 @@ public interface IGridStack { String[] getOreIds(); - String getTooltip(boolean quantity); + String getTooltip(); int getQuantity(); + String getFormattedFullQuantity(); + void draw(GuiBase gui, int x, int y); Object getIngredient(); + + @Nullable + IStorageTracker.IStorageTrackerEntry getTrackerEntry(); + + void setTrackerEntry(@Nullable IStorageTracker.IStorageTrackerEntry entry); } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridFluidDelta.java b/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridFluidDelta.java index 96402735d..247b01dce 100755 --- a/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridFluidDelta.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/network/MessageGridFluidDelta.java @@ -1,16 +1,21 @@ package com.raoulvdberge.refinedstorage.network; +import com.raoulvdberge.refinedstorage.api.network.INetwork; +import com.raoulvdberge.refinedstorage.api.storage.IStorageTracker; +import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageTrackerEntry; import com.raoulvdberge.refinedstorage.gui.grid.GuiGrid; import com.raoulvdberge.refinedstorage.gui.grid.stack.GridStackFluid; import com.raoulvdberge.refinedstorage.util.StackUtils; import io.netty.buffer.ByteBuf; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.common.network.ByteBufUtils; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler; import net.minecraftforge.fml.common.network.simpleimpl.MessageContext; public class MessageGridFluidDelta implements IMessage, IMessageHandler { + private INetwork network; private FluidStack stack; private int delta; @@ -19,20 +24,29 @@ public class MessageGridFluidDelta implements IMessage, IMessageHandler { @Nullable private INetwork network; + private IStorageTracker storageTracker; private List> deltas; @Nullable @@ -32,14 +35,16 @@ public class MessageGridItemDelta implements IMessage, IMessageHandler storageTracker, ItemStack stack, int delta) { this.network = network; + this.storageTracker = storageTracker; this.stack = stack; this.delta = delta; } - public MessageGridItemDelta(@Nullable INetwork network, List> deltas) { + public MessageGridItemDelta(@Nullable INetwork network, IStorageTracker storageTracker, List> deltas) { this.network = network; + this.storageTracker = storageTracker; this.deltas = deltas; } @@ -65,12 +70,28 @@ public class MessageGridItemDelta implements IMessage, IMessageHandler delta : deltas) { StackUtils.writeItemStack(buf, delta.getLeft(), network, false); + + IStorageTracker.IStorageTrackerEntry entry = storageTracker.get(stack); + buf.writeBoolean(entry != null); + if (entry != null) { + buf.writeLong(entry.getTime()); + ByteBufUtils.writeUTF8String(buf, entry.getName()); + } + buf.writeInt(delta.getRight()); } } @@ -110,6 +131,8 @@ public class MessageGridItemDelta implements IMessage, IMessageHandler itemStorage = new StorageCacheItem(this); + private StorageTrackerItem itemStorageTracker = new StorageTrackerItem(this::markDirty); private List> batchedItemStorageDeltas = new LinkedList<>(); private IStorageCache fluidStorage = new StorageCacheFluid(this); + private StorageTrackerFluid fluidStorageTracker = new StorageTrackerFluid(this::markDirty); private Map readerWriterChannels = new HashMap<>(); @@ -305,7 +313,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe if (!batchedItemStorageDeltas.isEmpty()) { world.getMinecraftServer().getPlayerList().getPlayers().stream() .filter(player -> isWatchingGrid(player, GridType.NORMAL, GridType.CRAFTING, GridType.PATTERN)) - .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, batchedItemStorageDeltas), player)); + .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, itemStorageTracker, batchedItemStorageDeltas), player)); batchedItemStorageDeltas.clear(); } @@ -316,7 +324,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe if (!batched) { world.getMinecraftServer().getPlayerList().getPlayers().stream() .filter(player -> isWatchingGrid(player, GridType.NORMAL, GridType.CRAFTING, GridType.PATTERN)) - .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, stack, delta), player)); + .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridItemDelta(this, itemStorageTracker, stack, delta), player)); } else { batchedItemStorageDeltas.add(Pair.of(stack, delta)); } @@ -338,7 +346,7 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe public void sendFluidStorageDeltaToClient(FluidStack stack, int delta) { world.getMinecraftServer().getPlayerList().getPlayers().stream() .filter(player -> isWatchingGrid(player, GridType.FLUID)) - .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridFluidDelta(stack, delta), player)); + .forEach(player -> RS.INSTANCE.network.sendTo(new MessageGridFluidDelta(this, stack, delta), player)); } private boolean isWatchingGrid(EntityPlayer player, GridType... types) { @@ -591,6 +599,16 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe return newStack; } + @Override + public IStorageTracker getItemStorageTracker() { + return itemStorageTracker; + } + + @Override + public IStorageTracker getFluidStorageTracker() { + return fluidStorageTracker; + } + @Override public World world() { return world; @@ -623,6 +641,14 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe readerWriterChannels.put(name, channel); } } + + if (tag.hasKey(NBT_ITEM_STORAGE_TRACKER)) { + itemStorageTracker.readFromNBT(tag.getTagList(NBT_ITEM_STORAGE_TRACKER, Constants.NBT.TAG_COMPOUND)); + } + + if (tag.hasKey(NBT_FLUID_STORAGE_TRACKER)) { + fluidStorageTracker.readFromNBT(tag.getTagList(NBT_FLUID_STORAGE_TRACKER, Constants.NBT.TAG_COMPOUND)); + } } @Override @@ -647,6 +673,9 @@ public class TileController extends TileBase implements ITickable, INetwork, IRe tag.setTag(NBT_READER_WRITER_CHANNELS, readerWriterChannelsList); + tag.setTag(NBT_ITEM_STORAGE_TRACKER, itemStorageTracker.serializeNBT()); + tag.setTag(NBT_FLUID_STORAGE_TRACKER, fluidStorageTracker.serializeNBT()); + return tag; } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/IPortableGrid.java b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/IPortableGrid.java index 1ee2925c1..5dab33985 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/IPortableGrid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/IPortableGrid.java @@ -2,6 +2,7 @@ package com.raoulvdberge.refinedstorage.tile.grid.portable; import com.raoulvdberge.refinedstorage.api.storage.IStorageCache; import com.raoulvdberge.refinedstorage.api.storage.IStorageDisk; +import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageTrackerItem; import com.raoulvdberge.refinedstorage.inventory.ItemHandlerBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; @@ -25,4 +26,6 @@ public interface IPortableGrid { ItemHandlerBase getDisk(); IItemHandlerModifiable getFilter(); + + StorageTrackerItem getStorageTracker(); } diff --git a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/PortableGrid.java b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/PortableGrid.java index 8581a7d42..6ded2b518 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/PortableGrid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/PortableGrid.java @@ -15,6 +15,7 @@ import com.raoulvdberge.refinedstorage.apiimpl.network.grid.handler.ItemGridHand import com.raoulvdberge.refinedstorage.apiimpl.network.node.diskdrive.NetworkNodeDiskDrive; import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageCacheItemPortable; import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageDiskItemPortable; +import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageTrackerItem; import com.raoulvdberge.refinedstorage.gui.grid.GuiGrid; import com.raoulvdberge.refinedstorage.inventory.ItemHandlerBase; import com.raoulvdberge.refinedstorage.inventory.ItemHandlerFilter; @@ -29,6 +30,7 @@ import net.minecraft.inventory.InventoryCraftResult; import net.minecraft.inventory.InventoryCrafting; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; @@ -43,6 +45,8 @@ import java.util.List; public class PortableGrid implements IGrid, IPortableGrid { public static int ID; + public static final String NBT_STORAGE_TRACKER = "StorageTracker"; + @Nullable private IStorageDisk storage; private StorageCacheItemPortable cache = new StorageCacheItemPortable(this); @@ -58,6 +62,10 @@ public class PortableGrid implements IGrid, IPortableGrid { private int tabPage; private int size; + private StorageTrackerItem storageTracker = new StorageTrackerItem(() -> { + stack.getTagCompound().setTag(NBT_STORAGE_TRACKER, getStorageTracker().serializeNBT()); + }); + private List filters = new ArrayList<>(); private List tabs = new ArrayList<>(); private ItemHandlerFilter filter = new ItemHandlerFilter(filters, tabs, null) { @@ -128,6 +136,10 @@ public class PortableGrid implements IGrid, IPortableGrid { stack.setTagCompound(new NBTTagCompound()); } + if (stack.getTagCompound().hasKey(NBT_STORAGE_TRACKER)) { + storageTracker.readFromNBT(stack.getTagCompound().getTagList(NBT_STORAGE_TRACKER, Constants.NBT.TAG_COMPOUND)); + } + if (player != null) { StackUtils.readItems(filter, 0, stack.getTagCompound()); } @@ -325,6 +337,11 @@ public class PortableGrid implements IGrid, IPortableGrid { return filter; } + @Override + public StorageTrackerItem getStorageTracker() { + return storageTracker; + } + @Override public InventoryCrafting getCraftingMatrix() { return null; diff --git a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/TilePortableGrid.java b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/TilePortableGrid.java index eb16b3375..dcbf78abe 100644 --- a/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/TilePortableGrid.java +++ b/src/main/java/com/raoulvdberge/refinedstorage/tile/grid/portable/TilePortableGrid.java @@ -14,6 +14,7 @@ import com.raoulvdberge.refinedstorage.apiimpl.network.node.NetworkNodeGrid; import com.raoulvdberge.refinedstorage.apiimpl.network.node.diskdrive.NetworkNodeDiskDrive; import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageCacheItemPortable; import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageDiskItemPortable; +import com.raoulvdberge.refinedstorage.apiimpl.storage.StorageTrackerItem; import com.raoulvdberge.refinedstorage.block.BlockPortableGrid; import com.raoulvdberge.refinedstorage.block.PortableGridDiskState; import com.raoulvdberge.refinedstorage.block.PortableGridType; @@ -43,6 +44,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.util.EnumFacing; import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.energy.CapabilityEnergy; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.fml.relauncher.Side; @@ -108,6 +110,8 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, private static final String NBT_DISK_STATE = "DiskState"; private static final String NBT_CONNECTED = "Connected"; + private static final String NBT_STORAGE_TRACKER = "StorageTracker"; + private EnergyForge energyStorage = new EnergyForge(ItemEnergyItem.CAPACITY); private PortableGridType type; @@ -181,6 +185,8 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, private PortableGridDiskState diskState = PortableGridDiskState.NONE; private boolean connected; + private StorageTrackerItem storageTracker = new StorageTrackerItem(this::markDirty); + public TilePortableGrid() { dataManager.addWatchedParameter(REDSTONE_MODE); dataManager.addWatchedParameter(ENERGY_STORED); @@ -226,6 +232,10 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, StackUtils.readItems(disk, 4, stack.getTagCompound()); this.redstoneMode = RedstoneMode.read(stack.getTagCompound()); + + if (stack.getTagCompound().hasKey(PortableGrid.NBT_STORAGE_TRACKER)) { + storageTracker.readFromNBT(stack.getTagCompound().getTagList(PortableGrid.NBT_STORAGE_TRACKER, Constants.NBT.TAG_COMPOUND)); + } } this.diskState = getDiskState(this); @@ -249,6 +259,8 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, stack.getTagCompound().setInteger(NetworkNodeGrid.NBT_TAB_SELECTED, tabSelected); stack.getTagCompound().setInteger(NetworkNodeGrid.NBT_TAB_PAGE, tabPage); + stack.getTagCompound().setTag(PortableGrid.NBT_STORAGE_TRACKER, storageTracker.serializeNBT()); + stack.getCapability(CapabilityEnergy.ENERGY, null).receiveEnergy(energyStorage.getEnergyStored(), false); for (int i = 0; i < 4; ++i) { @@ -400,6 +412,11 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, return filter; } + @Override + public StorageTrackerItem getStorageTracker() { + return storageTracker; + } + @Override public InventoryCrafting getCraftingMatrix() { return null; @@ -531,6 +548,8 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, redstoneMode.write(tag); + tag.setTag(NBT_STORAGE_TRACKER, storageTracker.serializeNBT()); + return tag; } @@ -570,6 +589,10 @@ public class TilePortableGrid extends TileBase implements IGrid, IPortableGrid, } redstoneMode = RedstoneMode.read(tag); + + if (tag.hasKey(NBT_STORAGE_TRACKER)) { + storageTracker.readFromNBT(tag.getTagList(NBT_STORAGE_TRACKER, Constants.NBT.TAG_COMPOUND)); + } } @Override diff --git a/src/main/java/com/raoulvdberge/refinedstorage/util/TimeUtils.java b/src/main/java/com/raoulvdberge/refinedstorage/util/TimeUtils.java new file mode 100644 index 000000000..2e54bfa2d --- /dev/null +++ b/src/main/java/com/raoulvdberge/refinedstorage/util/TimeUtils.java @@ -0,0 +1,32 @@ +package com.raoulvdberge.refinedstorage.util; + +import net.minecraft.client.resources.I18n; + +public final class TimeUtils { + private static final long SECOND = 1000; + private static final long MINUTE = SECOND * 60; + private static final long HOUR = MINUTE * 60; + private static final long DAY = HOUR * 24; + private static final long WEEK = DAY * 7; + private static final long YEAR = DAY * 365; + + public static String getAgo(long ago, String by) { + long diff = System.currentTimeMillis() - ago; + + if (diff < SECOND * 5) { + return I18n.format("misc.refinedstorage:last_modified.just_now", by); + } else if (diff < MINUTE) { + return I18n.format("misc.refinedstorage:last_modified.second" + ((diff / SECOND) > 1 ? "s" : ""), diff / SECOND, by); + } else if (diff < HOUR) { + return I18n.format("misc.refinedstorage:last_modified.minute" + ((diff / MINUTE) > 1 ? "s" : ""), diff / MINUTE, by); + } else if (diff < DAY) { + return I18n.format("misc.refinedstorage:last_modified.hour" + ((diff / HOUR) > 1 ? "s" : ""), diff / HOUR, by); + } else if (diff < WEEK) { + return I18n.format("misc.refinedstorage:last_modified.day" + ((diff / DAY) > 1 ? "s" : ""), diff / DAY, by); + } else if (diff < YEAR) { + return I18n.format("misc.refinedstorage:last_modified.week" + ((diff / WEEK) > 1 ? "s" : ""), diff / WEEK, by); + } + + return I18n.format("misc.refinedstorage:last_modified.year" + ((diff / YEAR) > 1 ? "s" : ""), diff / YEAR, by); + } +} diff --git a/src/main/resources/assets/refinedstorage/lang/en_us.lang b/src/main/resources/assets/refinedstorage/lang/en_us.lang index 9155eefec..2f732f7ac 100644 --- a/src/main/resources/assets/refinedstorage/lang/en_us.lang +++ b/src/main/resources/assets/refinedstorage/lang/en_us.lang @@ -110,6 +110,22 @@ misc.refinedstorage:processing=Processing misc.refinedstorage:reader_writer.redstone=Redstone strength: %d +misc.refinedstorage:total=%s total + +misc.refinedstorage:last_modified.just_now=Last modified just now by %s +misc.refinedstorage:last_modified.second=Last modified %d second ago by %s +misc.refinedstorage:last_modified.seconds=Last modified %d seconds ago by %s +misc.refinedstorage:last_modified.minute=Last modified %d minute ago by %s +misc.refinedstorage:last_modified.minutes=Last modified %d minutes ago by %s +misc.refinedstorage:last_modified.hour=Last modified %d hour ago by %s +misc.refinedstorage:last_modified.hours=Last modified %d hours ago by %s +misc.refinedstorage:last_modified.day=Last modified %d day ago by %s +misc.refinedstorage:last_modified.days=Last modified %d days ago by %s +misc.refinedstorage:last_modified.week=Last modified %d week ago by %s +misc.refinedstorage:last_modified.weeks=Last modified %d weeks ago by %s +misc.refinedstorage:last_modified.year=Last modified %d year ago by %s +misc.refinedstorage:last_modified.years=Last modified %d years ago by %s + sidebutton.refinedstorage:compare.1=Compare damage sidebutton.refinedstorage:compare.2=Compare NBT sidebutton.refinedstorage:compare.8=Compare ore dictionary