diff --git a/src/main/java/kamkeel/npcs/network/PacketHandler.java b/src/main/java/kamkeel/npcs/network/PacketHandler.java index b22537070..fa8f99de4 100644 --- a/src/main/java/kamkeel/npcs/network/PacketHandler.java +++ b/src/main/java/kamkeel/npcs/network/PacketHandler.java @@ -36,6 +36,7 @@ import kamkeel.npcs.network.packets.data.gui.GuiWaypointPacket; import kamkeel.npcs.network.packets.data.gui.IsGuiOpenPacket; import kamkeel.npcs.network.packets.data.large.GuiDataPacket; import kamkeel.npcs.network.packets.data.large.PartyDataPacket; +import kamkeel.npcs.network.packets.data.large.AuctionDataPacket; import kamkeel.npcs.network.packets.data.large.ScrollDataPacket; import kamkeel.npcs.network.packets.data.large.ScrollListPacket; import kamkeel.npcs.network.packets.data.large.SyncEffectPacket; @@ -551,6 +552,7 @@ public class PacketHandler { DATA_PACKET.registerPacket(new SyncEffectPacket()); DATA_PACKET.registerPacket(new GuiDataPacket()); DATA_PACKET.registerPacket(new PartyDataPacket()); + DATA_PACKET.registerPacket(new AuctionDataPacket()); } public void registerPlayerPackets() { diff --git a/src/main/java/kamkeel/npcs/network/enums/EnumDataPacket.java b/src/main/java/kamkeel/npcs/network/enums/EnumDataPacket.java index 874677af9..2f8cf71bc 100644 --- a/src/main/java/kamkeel/npcs/network/enums/EnumDataPacket.java +++ b/src/main/java/kamkeel/npcs/network/enums/EnumDataPacket.java @@ -58,4 +58,7 @@ public enum EnumDataPacket { SWING_PLAYER_ARM, DISABLE_MOUSE_INPUT, LOGIN, + + // Auction + AUCTION_DATA, } diff --git a/src/main/java/kamkeel/npcs/network/packets/data/large/AuctionDataPacket.java b/src/main/java/kamkeel/npcs/network/packets/data/large/AuctionDataPacket.java new file mode 100644 index 000000000..f7ca16dd7 --- /dev/null +++ b/src/main/java/kamkeel/npcs/network/packets/data/large/AuctionDataPacket.java @@ -0,0 +1,218 @@ +package kamkeel.npcs.network.packets.data.large; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import kamkeel.npcs.network.LargeAbstractPacket; +import kamkeel.npcs.network.PacketChannel; +import kamkeel.npcs.network.PacketHandler; +import kamkeel.npcs.network.enums.EnumDataPacket; +import kamkeel.npcs.util.ByteBufUtils; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import noppes.npcs.controllers.AuctionController; +import noppes.npcs.controllers.data.AuctionFilter; +import noppes.npcs.controllers.data.AuctionListing; + +import java.io.IOException; +import java.util.List; + +/** + * Large packet for sending auction listing data from server to client. + * Used for initial load and search/filter queries. + */ +public final class AuctionDataPacket extends LargeAbstractPacket { + public static final String packetName = "Large|AuctionData"; + + private DataType dataType; + private List listings; + private int totalResults; + private int currentPage; + private int totalPages; + private long playerBalance; + + public AuctionDataPacket() { + } + + private AuctionDataPacket(DataType type, List listings, + int totalResults, int currentPage, int totalPages, + long playerBalance) { + this.dataType = type; + this.listings = listings; + this.totalResults = totalResults; + this.currentPage = currentPage; + this.totalPages = totalPages; + this.playerBalance = playerBalance; + } + + /** + * Send filtered listing results to client + */ + public static void sendFilteredListings(EntityPlayerMP player, AuctionFilter filter) { + if (AuctionController.Instance == null) { + return; + } + + AuctionController.FilterResult result = AuctionController.Instance.getFilteredListings(filter); + long balance = 0; + if (noppes.npcs.controllers.CurrencyController.Instance != null) { + balance = noppes.npcs.controllers.CurrencyController.Instance.getBalance(player); + } + + AuctionDataPacket packet = new AuctionDataPacket( + DataType.FILTERED_LISTINGS, + result.listings, + result.totalResults, + result.currentPage, + result.totalPages, + balance + ); + PacketHandler.Instance.sendToPlayer(packet, player); + } + + /** + * Send player's own listings + */ + public static void sendMyListings(EntityPlayerMP player) { + if (AuctionController.Instance == null) { + return; + } + + List myListings = AuctionController.Instance + .getListingsBySeller(player.getUniqueID()); + + long balance = 0; + if (noppes.npcs.controllers.CurrencyController.Instance != null) { + balance = noppes.npcs.controllers.CurrencyController.Instance.getBalance(player); + } + + AuctionDataPacket packet = new AuctionDataPacket( + DataType.MY_LISTINGS, + myListings, + myListings.size(), + 0, + 1, + balance + ); + PacketHandler.Instance.sendToPlayer(packet, player); + } + + /** + * Send player's active bids + */ + public static void sendMyBids(EntityPlayerMP player) { + if (AuctionController.Instance == null) { + return; + } + + List myBids = AuctionController.Instance + .getListingsAsBidder(player.getUniqueID()); + + long balance = 0; + if (noppes.npcs.controllers.CurrencyController.Instance != null) { + balance = noppes.npcs.controllers.CurrencyController.Instance.getBalance(player); + } + + AuctionDataPacket packet = new AuctionDataPacket( + DataType.MY_BIDS, + myBids, + myBids.size(), + 0, + 1, + balance + ); + PacketHandler.Instance.sendToPlayer(packet, player); + } + + /** + * Send player's claimable items + */ + public static void sendClaimable(EntityPlayerMP player) { + if (AuctionController.Instance == null) { + return; + } + + List claimable = AuctionController.Instance + .getClaimableListings(player.getUniqueID()); + + long balance = 0; + if (noppes.npcs.controllers.CurrencyController.Instance != null) { + balance = noppes.npcs.controllers.CurrencyController.Instance.getBalance(player); + } + + AuctionDataPacket packet = new AuctionDataPacket( + DataType.CLAIMABLE, + claimable, + claimable.size(), + 0, + 1, + balance + ); + PacketHandler.Instance.sendToPlayer(packet, player); + } + + @Override + public Enum getType() { + return EnumDataPacket.AUCTION_DATA; + } + + @Override + public PacketChannel getChannel() { + return PacketHandler.DATA_PACKET; + } + + @Override + protected byte[] getData() throws IOException { + ByteBuf buffer = Unpooled.buffer(); + + buffer.writeInt(dataType.ordinal()); + buffer.writeInt(totalResults); + buffer.writeInt(currentPage); + buffer.writeInt(totalPages); + buffer.writeLong(playerBalance); + + // Write listings as NBT + NBTTagList listingList = new NBTTagList(); + if (listings != null) { + for (AuctionListing listing : listings) { + listingList.appendTag(listing.writeToNBT(new NBTTagCompound())); + } + } + + NBTTagCompound compound = new NBTTagCompound(); + compound.setTag("Listings", listingList); + ByteBufUtils.writeNBT(buffer, compound); + + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + return bytes; + } + + @Override + protected void handleCompleteData(ByteBuf data, EntityPlayer player) throws IOException { + // Client-side handling will be done in a client util class + // Similar to NoppesUtil.setScrollData() + int typeOrdinal = data.readInt(); + int totalResults = data.readInt(); + int currentPage = data.readInt(); + int totalPages = data.readInt(); + long playerBalance = data.readLong(); + + NBTTagCompound compound = ByteBufUtils.readNBT(data); + + DataType type = DataType.values()[typeOrdinal]; + + // Store data for client GUI to access + // This will be implemented in the client-side GUI handler + noppes.npcs.client.NoppesUtil.setAuctionData(type.ordinal(), compound, + totalResults, currentPage, totalPages, playerBalance); + } + + public enum DataType { + FILTERED_LISTINGS, + MY_LISTINGS, + MY_BIDS, + CLAIMABLE + } +} diff --git a/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java b/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java index 4aa56ab57..54bfaef50 100644 --- a/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java +++ b/src/main/java/kamkeel/npcs/network/packets/player/AuctionActionPacket.java @@ -19,8 +19,10 @@ import noppes.npcs.constants.EnumAuctionDuration; import noppes.npcs.constants.EnumRoleType; import noppes.npcs.controllers.AuctionController; import noppes.npcs.controllers.CurrencyController; +import noppes.npcs.controllers.data.AuctionFilter; import noppes.npcs.controllers.data.AuctionListing; import noppes.npcs.util.NBTJsonUtil; +import kamkeel.npcs.network.packets.data.large.AuctionDataPacket; import java.io.IOException; @@ -37,6 +39,7 @@ public class AuctionActionPacket extends AbstractPacket { private long buyoutPrice; private int durationOrdinal; private NBTTagCompound itemData; + private NBTTagCompound filterData; // For query operations private String responseMessage; private boolean success; @@ -118,6 +121,43 @@ public class AuctionActionPacket extends AbstractPacket { PacketClient.sendClient(packet); } + /** + * Query filtered listings + */ + @SideOnly(Side.CLIENT) + public static void QueryListings(AuctionFilter filter) { + AuctionActionPacket packet = new AuctionActionPacket(Action.QueryListings); + packet.filterData = filter.writeToNBT(new NBTTagCompound()); + PacketClient.sendClient(packet); + } + + /** + * Request player's own listings + */ + @SideOnly(Side.CLIENT) + public static void QueryMyListings() { + AuctionActionPacket packet = new AuctionActionPacket(Action.QueryMyListings); + PacketClient.sendClient(packet); + } + + /** + * Request player's active bids + */ + @SideOnly(Side.CLIENT) + public static void QueryMyBids() { + AuctionActionPacket packet = new AuctionActionPacket(Action.QueryMyBids); + PacketClient.sendClient(packet); + } + + /** + * Request player's claimable items + */ + @SideOnly(Side.CLIENT) + public static void QueryClaimable() { + AuctionActionPacket packet = new AuctionActionPacket(Action.QueryClaimable); + PacketClient.sendClient(packet); + } + @Override public Enum getType() { return EnumPlayerPacket.AuctionAction; @@ -155,6 +195,19 @@ public class AuctionActionPacket extends AbstractPacket { case ClaimProceeds: out.writeInt(listingId); break; + + case QueryListings: + String filterJson = NBTJsonUtil.Convert(filterData); + byte[] filterBytes = filterJson.getBytes("UTF-8"); + out.writeInt(filterBytes.length); + out.writeBytes(filterBytes); + break; + + case QueryMyListings: + case QueryMyBids: + case QueryClaimable: + // No additional data needed + break; } } @@ -204,6 +257,18 @@ public class AuctionActionPacket extends AbstractPacket { case ClaimProceeds: handleClaimProceeds(in, playerMP); break; + case QueryListings: + handleQueryListings(in, playerMP); + break; + case QueryMyListings: + handleQueryMyListings(playerMP); + break; + case QueryMyBids: + handleQueryMyBids(playerMP); + break; + case QueryClaimable: + handleQueryClaimable(playerMP); + break; } } @@ -420,6 +485,62 @@ public class AuctionActionPacket extends AbstractPacket { } } + // ==================== Query Handlers ==================== + + private void handleQueryListings(ByteBuf in, EntityPlayerMP player) throws IOException { + // Check permission + if (!CustomNpcsPermissions.hasPermission(player, CustomNpcsPermissions.AUCTION_USE)) { + sendResponse(player, false, "You don't have permission to view auctions"); + return; + } + + int filterBytesLength = in.readInt(); + byte[] filterBytes = new byte[filterBytesLength]; + in.readBytes(filterBytes); + String filterJson = new String(filterBytes, "UTF-8"); + + try { + NBTTagCompound filterNBT = NBTJsonUtil.Convert(filterJson); + AuctionFilter filter = new AuctionFilter(); + filter.readFromNBT(filterNBT); + + // Send filtered listings to client + AuctionDataPacket.sendFilteredListings(player, filter); + } catch (Exception e) { + sendResponse(player, false, "Error parsing filter"); + } + } + + private void handleQueryMyListings(EntityPlayerMP player) { + // Check permission + if (!CustomNpcsPermissions.hasPermission(player, CustomNpcsPermissions.AUCTION_USE)) { + sendResponse(player, false, "You don't have permission to view auctions"); + return; + } + + AuctionDataPacket.sendMyListings(player); + } + + private void handleQueryMyBids(EntityPlayerMP player) { + // Check permission + if (!CustomNpcsPermissions.hasPermission(player, CustomNpcsPermissions.AUCTION_USE)) { + sendResponse(player, false, "You don't have permission to view auctions"); + return; + } + + AuctionDataPacket.sendMyBids(player); + } + + private void handleQueryClaimable(EntityPlayerMP player) { + // Check permission + if (!CustomNpcsPermissions.hasPermission(player, CustomNpcsPermissions.AUCTION_USE)) { + sendResponse(player, false, "You don't have permission to view auctions"); + return; + } + + AuctionDataPacket.sendClaimable(player); + } + /** * Send a response message to the player */ @@ -436,6 +557,10 @@ public class AuctionActionPacket extends AbstractPacket { Buyout, CancelListing, ClaimItem, - ClaimProceeds + ClaimProceeds, + QueryListings, + QueryMyListings, + QueryMyBids, + QueryClaimable } } diff --git a/src/main/java/noppes/npcs/client/NoppesUtil.java b/src/main/java/noppes/npcs/client/NoppesUtil.java index 620142564..64b26fa5d 100644 --- a/src/main/java/noppes/npcs/client/NoppesUtil.java +++ b/src/main/java/noppes/npcs/client/NoppesUtil.java @@ -182,6 +182,67 @@ public class NoppesUtil { data = new HashMap(); } + // ==================== Auction Data ==================== + + // Static storage for auction data that the GUI can access + private static NBTTagCompound auctionListingData; + private static int auctionDataType; + private static int auctionTotalResults; + private static int auctionCurrentPage; + private static int auctionTotalPages; + private static long auctionPlayerBalance; + + public static void setAuctionData(int dataType, NBTTagCompound compound, + int totalResults, int currentPage, int totalPages, + long playerBalance) { + auctionDataType = dataType; + auctionListingData = compound; + auctionTotalResults = totalResults; + auctionCurrentPage = currentPage; + auctionTotalPages = totalPages; + auctionPlayerBalance = playerBalance; + + // Notify the current GUI if it's an auction GUI + GuiScreen gui = Minecraft.getMinecraft().currentScreen; + if (gui instanceof IAuctionData) { + ((IAuctionData) gui).onAuctionDataReceived(dataType, compound, + totalResults, currentPage, totalPages, playerBalance); + } + } + + public static NBTTagCompound getAuctionListingData() { + return auctionListingData; + } + + public static int getAuctionDataType() { + return auctionDataType; + } + + public static int getAuctionTotalResults() { + return auctionTotalResults; + } + + public static int getAuctionCurrentPage() { + return auctionCurrentPage; + } + + public static int getAuctionTotalPages() { + return auctionTotalPages; + } + + public static long getAuctionPlayerBalance() { + return auctionPlayerBalance; + } + + /** + * Interface for GUIs that receive auction data + */ + public interface IAuctionData { + void onAuctionDataReceived(int dataType, NBTTagCompound data, + int totalResults, int currentPage, int totalPages, + long playerBalance); + } + public static void guiQuestCompletion(EntityPlayer player, NBTTagCompound read) { Quest quest = new Quest(); quest.readNBT(read); diff --git a/src/main/java/noppes/npcs/constants/EnumAuctionSort.java b/src/main/java/noppes/npcs/constants/EnumAuctionSort.java new file mode 100644 index 000000000..a94d1f1bf --- /dev/null +++ b/src/main/java/noppes/npcs/constants/EnumAuctionSort.java @@ -0,0 +1,68 @@ +package noppes.npcs.constants; + +/** + * Sorting options for auction listings. + */ +public enum EnumAuctionSort { + /** Default - newest listings first */ + NEWEST_FIRST("Newest First"), + + /** Oldest listings first */ + OLDEST_FIRST("Oldest First"), + + /** Highest current price first */ + PRICE_HIGH_TO_LOW("Most Expensive"), + + /** Lowest current price first */ + PRICE_LOW_TO_HIGH("Least Expensive"), + + /** Buyout price high to low (listings without buyout at end) */ + BUYOUT_HIGH_TO_LOW("Buyout: High to Low"), + + /** Buyout price low to high (listings without buyout at end) */ + BUYOUT_LOW_TO_HIGH("Buyout: Low to High"), + + /** Ending soonest first */ + ENDING_SOON("Ending Soon"), + + /** Most time remaining first */ + MOST_TIME_LEFT("Most Time Left"), + + /** Alphabetical by item name A-Z */ + NAME_A_TO_Z("Name: A to Z"), + + /** Alphabetical by item name Z-A */ + NAME_Z_TO_A("Name: Z to A"), + + /** Most bids first */ + MOST_BIDS("Most Bids"), + + /** Least bids first */ + LEAST_BIDS("Least Bids"); + + private final String displayName; + + EnumAuctionSort(String displayName) { + this.displayName = displayName; + } + + public String getDisplayName() { + return displayName; + } + + /** + * Get next sort option (for cycling through in GUI) + */ + public EnumAuctionSort next() { + EnumAuctionSort[] values = values(); + return values[(this.ordinal() + 1) % values.length]; + } + + /** + * Get previous sort option + */ + public EnumAuctionSort previous() { + EnumAuctionSort[] values = values(); + return values[(this.ordinal() - 1 + values.length) % values.length]; + } +} diff --git a/src/main/java/noppes/npcs/controllers/AuctionController.java b/src/main/java/noppes/npcs/controllers/AuctionController.java index ec3b6de19..3e0f44a21 100644 --- a/src/main/java/noppes/npcs/controllers/AuctionController.java +++ b/src/main/java/noppes/npcs/controllers/AuctionController.java @@ -9,7 +9,9 @@ import noppes.npcs.CustomNpcs; import noppes.npcs.config.ConfigMarket; import noppes.npcs.constants.EnumAuctionCategory; import noppes.npcs.constants.EnumAuctionDuration; +import noppes.npcs.constants.EnumAuctionSort; import noppes.npcs.constants.EnumAuctionStatus; +import noppes.npcs.controllers.data.AuctionFilter; import noppes.npcs.controllers.data.AuctionListing; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -144,6 +146,171 @@ public class AuctionController { .collect(Collectors.toList()); } + // ==================== Filtering & Sorting ==================== + + /** + * Get filtered and sorted auction listings. + * This is the main method for GUI queries. + * @param filter The filter criteria + * @return FilterResult containing matching listings and metadata + */ + public FilterResult getFilteredListings(AuctionFilter filter) { + processExpiredAuctions(); + + // Get all active listings + List results = listings.values().stream() + .filter(AuctionListing::isActive) + .filter(filter::matches) + .collect(Collectors.toList()); + + // Sort the results + sortListings(results, filter.sortOrder); + + // Calculate pagination + int totalResults = results.size(); + int totalPages = (int) Math.ceil((double) totalResults / filter.pageSize); + int startIndex = filter.page * filter.pageSize; + int endIndex = Math.min(startIndex + filter.pageSize, totalResults); + + // Get the page slice + List pageResults; + if (startIndex >= totalResults) { + pageResults = new ArrayList<>(); + } else { + pageResults = results.subList(startIndex, endIndex); + } + + return new FilterResult(pageResults, totalResults, filter.page, totalPages); + } + + /** + * Sort listings according to the specified sort order + */ + public void sortListings(List listings, EnumAuctionSort sortOrder) { + if (listings == null || listings.isEmpty()) { + return; + } + + Comparator comparator; + + switch (sortOrder) { + case NEWEST_FIRST: + comparator = Comparator.comparingLong(l -> -l.listingTime); + break; + + case OLDEST_FIRST: + comparator = Comparator.comparingLong(l -> l.listingTime); + break; + + case PRICE_HIGH_TO_LOW: + comparator = Comparator.comparingLong((AuctionListing l) -> + -(l.currentBid > 0 ? l.currentBid : l.startingPrice)); + break; + + case PRICE_LOW_TO_HIGH: + comparator = Comparator.comparingLong(l -> + l.currentBid > 0 ? l.currentBid : l.startingPrice); + break; + + case BUYOUT_HIGH_TO_LOW: + // Listings with buyout first, sorted high to low + comparator = Comparator + .comparingInt((AuctionListing l) -> l.buyoutPrice > 0 ? 0 : 1) + .thenComparingLong(l -> -l.buyoutPrice); + break; + + case BUYOUT_LOW_TO_HIGH: + // Listings with buyout first, sorted low to high + comparator = Comparator + .comparingInt((AuctionListing l) -> l.buyoutPrice > 0 ? 0 : 1) + .thenComparingLong(l -> l.buyoutPrice); + break; + + case ENDING_SOON: + comparator = Comparator.comparingLong(l -> l.expirationTime); + break; + + case MOST_TIME_LEFT: + comparator = Comparator.comparingLong(l -> -l.expirationTime); + break; + + case NAME_A_TO_Z: + comparator = Comparator.comparing(l -> + l.item.getDisplayName().toLowerCase()); + break; + + case NAME_Z_TO_A: + comparator = Comparator.comparing((AuctionListing l) -> + l.item.getDisplayName().toLowerCase()).reversed(); + break; + + case MOST_BIDS: + comparator = Comparator.comparingInt(l -> -l.bidCount); + break; + + case LEAST_BIDS: + comparator = Comparator.comparingInt(l -> l.bidCount); + break; + + default: + comparator = Comparator.comparingLong(l -> l.expirationTime); + } + + listings.sort(comparator); + } + + /** + * Quick search with default sorting (ending soon) + */ + public List quickSearch(String searchText, EnumAuctionCategory category, int limit) { + AuctionFilter filter = new AuctionFilter() + .withSearch(searchText) + .withCategory(category) + .withSort(EnumAuctionSort.ENDING_SOON) + .withPage(0, limit); + + return getFilteredListings(filter).listings; + } + + /** + * Get listings sorted by a specific order + */ + public List getListingsSorted(EnumAuctionSort sortOrder) { + List results = new ArrayList<>(getActiveListings()); + sortListings(results, sortOrder); + return results; + } + + /** + * Result container for filtered queries + */ + public static class FilterResult { + public final List listings; + public final int totalResults; + public final int currentPage; + public final int totalPages; + + public FilterResult(List listings, int totalResults, + int currentPage, int totalPages) { + this.listings = listings; + this.totalResults = totalResults; + this.currentPage = currentPage; + this.totalPages = totalPages; + } + + public boolean hasNextPage() { + return currentPage < totalPages - 1; + } + + public boolean hasPreviousPage() { + return currentPage > 0; + } + + public boolean isEmpty() { + return listings == null || listings.isEmpty(); + } + } + /** * Get all listings by a seller (all statuses) */ diff --git a/src/main/java/noppes/npcs/controllers/data/AuctionFilter.java b/src/main/java/noppes/npcs/controllers/data/AuctionFilter.java new file mode 100644 index 000000000..a4a3d1d5a --- /dev/null +++ b/src/main/java/noppes/npcs/controllers/data/AuctionFilter.java @@ -0,0 +1,250 @@ +package noppes.npcs.controllers.data; + +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.constants.EnumAuctionCategory; +import noppes.npcs.constants.EnumAuctionSort; + +/** + * Filter/search criteria for auction listings. + * Used to query the auction controller with specific criteria. + */ +public class AuctionFilter { + + /** Search text to match against item names */ + public String searchText = ""; + + /** Category to filter by (ALL for no category filter) */ + public EnumAuctionCategory category = EnumAuctionCategory.ALL; + + /** How to sort results */ + public EnumAuctionSort sortOrder = EnumAuctionSort.ENDING_SOON; + + /** Minimum current bid/price filter */ + public long minPrice = 0; + + /** Maximum current bid/price filter (0 = no max) */ + public long maxPrice = 0; + + /** Only show listings with buyout option */ + public boolean buyoutOnly = false; + + /** Only show listings without any bids yet */ + public boolean noBidsOnly = false; + + /** Page number for pagination (0-indexed) */ + public int page = 0; + + /** Items per page */ + public int pageSize = 20; + + /** Filter by specific seller UUID string (empty = all sellers) */ + public String sellerUUID = ""; + + /** Filter by specific seller name (partial match, empty = all) */ + public String sellerName = ""; + + public AuctionFilter() { + } + + /** + * Create a filter with search text + */ + public static AuctionFilter search(String text) { + AuctionFilter filter = new AuctionFilter(); + filter.searchText = text != null ? text : ""; + return filter; + } + + /** + * Create a filter with category + */ + public static AuctionFilter category(EnumAuctionCategory category) { + AuctionFilter filter = new AuctionFilter(); + filter.category = category; + return filter; + } + + /** + * Create a filter for a specific seller + */ + public static AuctionFilter seller(String sellerUUID) { + AuctionFilter filter = new AuctionFilter(); + filter.sellerUUID = sellerUUID != null ? sellerUUID : ""; + return filter; + } + + // Builder-style methods for chaining + + public AuctionFilter withSearch(String text) { + this.searchText = text != null ? text : ""; + return this; + } + + public AuctionFilter withCategory(EnumAuctionCategory category) { + this.category = category; + return this; + } + + public AuctionFilter withSort(EnumAuctionSort sort) { + this.sortOrder = sort; + return this; + } + + public AuctionFilter withPriceRange(long min, long max) { + this.minPrice = min; + this.maxPrice = max; + return this; + } + + public AuctionFilter withBuyoutOnly(boolean buyoutOnly) { + this.buyoutOnly = buyoutOnly; + return this; + } + + public AuctionFilter withNoBidsOnly(boolean noBidsOnly) { + this.noBidsOnly = noBidsOnly; + return this; + } + + public AuctionFilter withPage(int page, int pageSize) { + this.page = Math.max(0, page); + this.pageSize = Math.max(1, Math.min(100, pageSize)); + return this; + } + + public AuctionFilter withSeller(String sellerUUID, String sellerName) { + this.sellerUUID = sellerUUID != null ? sellerUUID : ""; + this.sellerName = sellerName != null ? sellerName : ""; + return this; + } + + /** + * Check if a listing matches this filter's criteria + */ + public boolean matches(AuctionListing listing) { + if (listing == null) { + return false; + } + + // Search text filter (case-insensitive) + if (!searchText.isEmpty()) { + String lowerSearch = searchText.toLowerCase(); + String itemName = listing.item.getDisplayName().toLowerCase(); + if (!itemName.contains(lowerSearch)) { + return false; + } + } + + // Category filter + if (category != EnumAuctionCategory.ALL && listing.category != category) { + return false; + } + + // Price range filter (uses current bid or starting price) + long price = listing.currentBid > 0 ? listing.currentBid : listing.startingPrice; + if (minPrice > 0 && price < minPrice) { + return false; + } + if (maxPrice > 0 && price > maxPrice) { + return false; + } + + // Buyout only filter + if (buyoutOnly && listing.buyoutPrice <= 0) { + return false; + } + + // No bids only filter + if (noBidsOnly && listing.hasBids()) { + return false; + } + + // Seller UUID filter + if (!sellerUUID.isEmpty() && !listing.sellerUUID.toString().equals(sellerUUID)) { + return false; + } + + // Seller name filter (partial match) + if (!sellerName.isEmpty()) { + String lowerName = sellerName.toLowerCase(); + if (!listing.sellerName.toLowerCase().contains(lowerName)) { + return false; + } + } + + return true; + } + + /** + * Reset all filters to defaults + */ + public void reset() { + searchText = ""; + category = EnumAuctionCategory.ALL; + sortOrder = EnumAuctionSort.ENDING_SOON; + minPrice = 0; + maxPrice = 0; + buyoutOnly = false; + noBidsOnly = false; + page = 0; + sellerUUID = ""; + sellerName = ""; + } + + /** + * Write filter to NBT for network transmission + */ + public NBTTagCompound writeToNBT(NBTTagCompound compound) { + compound.setString("SearchText", searchText); + compound.setInteger("Category", category.ordinal()); + compound.setInteger("SortOrder", sortOrder.ordinal()); + compound.setLong("MinPrice", minPrice); + compound.setLong("MaxPrice", maxPrice); + compound.setBoolean("BuyoutOnly", buyoutOnly); + compound.setBoolean("NoBidsOnly", noBidsOnly); + compound.setInteger("Page", page); + compound.setInteger("PageSize", pageSize); + compound.setString("SellerUUID", sellerUUID); + compound.setString("SellerName", sellerName); + return compound; + } + + /** + * Read filter from NBT + */ + public void readFromNBT(NBTTagCompound compound) { + searchText = compound.getString("SearchText"); + + int catOrdinal = compound.getInteger("Category"); + if (catOrdinal >= 0 && catOrdinal < EnumAuctionCategory.values().length) { + category = EnumAuctionCategory.values()[catOrdinal]; + } + + int sortOrdinal = compound.getInteger("SortOrder"); + if (sortOrdinal >= 0 && sortOrdinal < EnumAuctionSort.values().length) { + sortOrder = EnumAuctionSort.values()[sortOrdinal]; + } + + minPrice = compound.getLong("MinPrice"); + maxPrice = compound.getLong("MaxPrice"); + buyoutOnly = compound.getBoolean("BuyoutOnly"); + noBidsOnly = compound.getBoolean("NoBidsOnly"); + page = compound.getInteger("Page"); + pageSize = compound.getInteger("PageSize"); + if (pageSize <= 0) pageSize = 20; + sellerUUID = compound.getString("SellerUUID"); + sellerName = compound.getString("SellerName"); + } + + @Override + public String toString() { + return "AuctionFilter{" + + "search='" + searchText + '\'' + + ", category=" + category + + ", sort=" + sortOrder + + ", price=" + minPrice + "-" + maxPrice + + ", buyoutOnly=" + buyoutOnly + + ", page=" + page + + '}'; + } +} diff --git a/src/main/java/noppes/npcs/controllers/data/AuctionListing.java b/src/main/java/noppes/npcs/controllers/data/AuctionListing.java index e17a08112..24c778439 100644 --- a/src/main/java/noppes/npcs/controllers/data/AuctionListing.java +++ b/src/main/java/noppes/npcs/controllers/data/AuctionListing.java @@ -55,9 +55,24 @@ public class AuctionListing { public boolean sellerClaimed = false; // Has seller claimed their proceeds? public boolean buyerClaimed = false; // Has buyer claimed their item? + // Bid tracking + public int bidCount = 0; // Number of bids placed + public AuctionListing() { } + /** + * Alias for listingTime (when the auction was created) + */ + public long getCreationTime() { + return listingTime; + } + + // For sorting compatibility + public long creationTime() { + return listingTime; + } + /** * Create a new auction listing */ @@ -174,6 +189,7 @@ public class AuctionListing { highBidderUUID = bidder; highBidderName = bidderName; currentBid = amount; + bidCount++; // Snipe protection - extend if bid placed in final minutes if (ConfigMarket.SnipeProtectionMinutes > 0) { @@ -338,6 +354,7 @@ public class AuctionListing { compound.setInteger("Category", category.ordinal()); compound.setBoolean("SellerClaimed", sellerClaimed); compound.setBoolean("BuyerClaimed", buyerClaimed); + compound.setInteger("BidCount", bidCount); return compound; } @@ -388,5 +405,6 @@ public class AuctionListing { sellerClaimed = compound.getBoolean("SellerClaimed"); buyerClaimed = compound.getBoolean("BuyerClaimed"); + bidCount = compound.getInteger("BidCount"); } }