2022-05-10 22:19:30 +03:00
/************************************************************************
2023-07-26 18:03:00 +01:00
* Auction House Pagination
*
* This allows players to list and view more than the client - restricted 7
* entries . This works by using multiple pages of 6 entries and pages
* through them every time the player opens their AH listing page .
2022-05-10 22:19:30 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "map/utils/moduleutils.h"
# include "common/timer.h"
2025-03-19 12:48:12 +00:00
2025-10-16 01:41:15 -06:00
# include "map/enums/chat_message_type.h"
2025-10-24 02:19:10 -06:00
# include "map/map_session.h"
2025-03-19 12:48:12 +00:00
# include "map/packet_system.h"
# include "map/packets/basic.h"
2025-10-16 01:05:54 -06:00
# include "map/packets/s2c/0x017_chat_std.h"
2022-05-10 22:19:30 +03:00
# include "map/zone.h"
2025-10-24 02:19:10 -06:00
# include "packets/s2c/0x04c_auc.h"
2022-05-10 22:19:30 +03:00
2025-03-19 12:48:12 +00:00
# include <functional>
# include <numeric>
2022-05-10 22:19:30 +03:00
class AHPaginationModule : public CPPModule
{
void OnInit ( ) override
{
TracyZoneScoped ;
2025-01-23 21:30:47 +00:00
const auto originalAHListLimit = settings : : get < uint32 > ( " map.AH_LIST_LIMIT " ) ;
if ( originalAHListLimit ! = 0 & & originalAHListLimit < = 7 )
{
ShowWarning ( " [AH PAGES] AH_LIST_LIMIT is already set to %i. AH_LIST_LIMIT <= 7 is handled by the client. This module isn't required. " , originalAHListLimit ) ;
return ;
}
2025-09-21 00:43:45 -06:00
totalPages_ = originalAHListLimit = = 0 ? 99 : ( originalAHListLimit / 6U ) + 1 ;
ShowInfo ( " [AH PAGES] AH_LIST_LIMIT is set to %i. Enabling pagination of %i pages with %i items per page. " , originalAHListLimit , totalPages_ , itemsPerPage_ ) ;
}
2022-05-10 22:19:30 +03:00
2025-09-21 00:43:45 -06:00
auto OnIncomingPacket ( MapSession * session , CCharEntity * PChar , CBasicPacket & packet ) - > bool override
{
if ( packet . getType ( ) ! = 0x04E )
{
return false ;
}
2022-05-10 22:19:30 +03:00
2025-09-21 00:43:45 -06:00
if ( PChar - > m_GMlevel = = 0 & & ! PChar - > loc . zone - > CanUseMisc ( MISC_AH ) )
{
ShowWarning ( " [AH PAGES] %s is trying to use the auction house in a disallowed zone [%s] " , PChar - > getName ( ) , PChar - > loc . zone - > getName ( ) ) ;
return true ;
}
2022-05-10 22:19:30 +03:00
2025-09-21 00:43:45 -06:00
const auto typedPacket = packet . as < GP_CLI_COMMAND_AUC > ( ) ;
// Only intercept for action 0x05: Open List Of Sales / Wait
if ( typedPacket - > Command ! = GP_CLI_COMMAND_AUC_COMMAND : : Info )
2022-05-10 22:19:30 +03:00
{
2025-09-21 00:43:45 -06:00
return false ;
}
2022-05-10 22:19:30 +03:00
2025-09-21 00:43:45 -06:00
// Rate limit opening the AH Sales Info to once every 1.5 seconds
const timer : : time_point curTick = timer : : now ( ) ;
if ( curTick < PChar - > m_AHHistoryTimestamp + 1500 ms )
{
2025-10-24 02:19:10 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_AUC > ( typedPacket - > Command , 246 , 0 , 0 , 0 , 0 ) ; // try again in a little while msg
2025-09-21 00:43:45 -06:00
return true ;
}
// Not const, because we're going to increment it below
// This will get wiped on zoning
auto currentAHPage = PChar - > GetLocalVar ( " AH_PAGE " ) ;
2023-03-31 20:41:32 -04:00
2025-09-21 00:43:45 -06:00
// Will only show the first time you access the AH until you zone again.
// Since we do rollover of pages inline below.
// This is also good for performance to not hammer the db completely.
if ( currentAHPage = = 0 ) // Page "1"
{
// Get the current number of items the player has for sale
const auto ahListings = [ & ] ( ) - > uint32
2022-05-10 22:19:30 +03:00
{
2025-09-21 00:43:45 -06:00
const auto rset = db : : preparedStmt ( " SELECT COUNT(*) "
" FROM auction_house "
" WHERE seller = ? AND sale = 0 " ,
PChar - > id ) ;
FOR_DB_SINGLE_RESULT ( rset )
2022-05-10 22:19:30 +03:00
{
2025-09-21 00:43:45 -06:00
return rset - > get < uint32 > ( 0 ) ;
2022-05-10 22:19:30 +03:00
}
2025-09-21 00:43:45 -06:00
return 0 ;
} ( ) ;
2025-10-16 01:05:54 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_CHAT_STD > ( PChar , MESSAGE_SYSTEM_3 , fmt : : format ( " You have {} items listed for sale. " , ahListings ) ) ;
2025-09-21 00:43:45 -06:00
}
PChar - > SetLocalVar ( " AH_PAGE " , ( currentAHPage + 1 ) % totalPages_ ) ;
PChar - > m_ah_history . clear ( ) ;
PChar - > m_AHHistoryTimestamp = curTick ;
2025-10-24 02:19:10 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_AUC > ( typedPacket - > Command ) ;
2025-09-21 00:43:45 -06:00
// Not const, because we're possibly going to overwrite it later
auto rset = db : : preparedStmt ( " SELECT itemid, price, stack "
" FROM auction_house "
" WHERE seller = ? and sale = 0 "
" ORDER BY id ASC "
" LIMIT ? OFFSET ? " ,
2025-11-06 11:30:52 +00:00
PChar - > id ,
static_cast < uint32 > ( itemsPerPage_ ) ,
static_cast < uint32 > ( currentAHPage * itemsPerPage_ ) ) ;
2025-09-21 00:43:45 -06:00
// If we get back 0 results, we're at the end of the list. We should redo the query and reset to page 1 (OFFSET 0)
if ( rset & & rset - > rowsCount ( ) = = 0 )
{
2025-10-16 01:05:54 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_CHAT_STD > ( PChar , MESSAGE_SYSTEM_3 , fmt : : format ( " No results for page: {} of {}. " , currentAHPage + 1 , totalPages_ ) ) ;
2025-09-21 00:43:45 -06:00
// Reset to Page 1
// Overwrite the original rset here
rset = db : : preparedStmt ( " SELECT itemid, price, stack "
" FROM auction_house "
" WHERE seller = ? and sale = 0 "
" ORDER BY id ASC "
" LIMIT ? OFFSET 0 " ,
2025-11-06 11:30:52 +00:00
PChar - > id ,
static_cast < uint32 > ( itemsPerPage_ ) ) ;
2025-09-21 00:43:45 -06:00
// Show Page 1 this time
currentAHPage = 0 ;
// Prepare Page 2 for next load
PChar - > SetLocalVar ( " AH_PAGE " , currentAHPage + 1 ) ;
}
// TODO: Don't use totalPages_ here, use the actual number of pages of results.
// Current (10 items): Current page: 2 of 99. Showing 4 items.
// Desired (10 items): Current page: 2 of 2. Showing 4 items.
2025-10-16 01:05:54 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_CHAT_STD > ( PChar , MESSAGE_SYSTEM_3 , fmt : : format ( " Current page: {} of {}. Showing {} items. " , currentAHPage + 1 , totalPages_ , rset - > rowsCount ( ) ) ) ;
2025-09-21 00:43:45 -06:00
FOR_DB_MULTIPLE_RESULTS ( rset )
{
PChar - > m_ah_history . emplace_back ( AuctionHistory_t {
. itemid = rset - > get < uint16 > ( " itemid " ) ,
. stack = rset - > get < uint8 > ( " stack " ) ,
. price = rset - > get < uint32 > ( " price " ) ,
. status = 0 ,
} ) ;
}
const auto totalItemsOnAh = PChar - > m_ah_history . size ( ) ;
for ( size_t slot = 0 ; slot < totalItemsOnAh ; slot + + )
{
2025-10-24 02:19:10 -06:00
PChar - > pushPacket < GP_SERV_COMMAND_AUC > ( GP_CLI_COMMAND_AUC_COMMAND : : LotCancel , static_cast < uint8 > ( slot ) , PChar ) ;
2025-09-21 00:43:45 -06:00
}
return true ;
2022-05-10 22:19:30 +03:00
}
2025-09-21 00:43:45 -06:00
// If this is set to 7, the client won't let you put up more than 7 items. So, 6.
uint8 itemsPerPage_ { 6u } ;
uint8 totalPages_ { 1 } ;
2022-05-10 22:19:30 +03:00
} ;
REGISTER_CPP_MODULE ( AHPaginationModule ) ;