mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-07-31 01:15:48 -04:00
1458 lines
54 KiB
C++
1458 lines
54 KiB
C++
// ======================================================================
|
|
//
|
|
// Auction.cpp
|
|
// copyright (c) 2004 Sony Online Entertainment
|
|
//
|
|
// (refactor of original code only to the degree to make it work with new)
|
|
// (server & database framework ... i.e. none of the logic has changed)
|
|
//
|
|
// Original Author: Matt Severenson
|
|
// Refactored by : Doug Mellencamp
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "FirstCommodityServer.h"
|
|
#include "Auction.h"
|
|
#include "AuctionMarket.h"
|
|
#include "CommodityServer.h"
|
|
#include "ConfigCommodityServer.h"
|
|
#include "StringId.h"
|
|
#include "UnicodeUtils.h"
|
|
#include "sharedFoundation/Crc.h"
|
|
#include "sharedFoundation/CrcLowerString.h"
|
|
#include "sharedGame/CommoditiesAdvancedSearchAttribute.h"
|
|
#include "sharedGame/GameObjectTypes.h"
|
|
#include "sharedGame/SharedObjectTemplate.h"
|
|
#include "sharedLog/Log.h"
|
|
#include "sharedUtility/DataTable.h"
|
|
#include "sharedUtility/DataTableManager.h"
|
|
|
|
#include <algorithm>
|
|
|
|
// src/engine/client/library/clientGame/src/shared/core/AuctionManagerClient.cpp (s_maxBid)
|
|
// src/engine/server/application/CommoditiesServer/src/shared/Auction.cpp (MAX_BID)
|
|
// src/engine/server/library/serverGame/src/shared/commoditiesMarket/CommoditiesMarket.cpp (MAX_BID)
|
|
#define MAX_BID 10000000
|
|
#define MAX_VENDOR_PRICE 833333333
|
|
|
|
namespace AuctionNamespace
|
|
{
|
|
typedef bool (*SearchConditionComparisonFn) (AuctionQueryHeadersMessage::SearchCondition const & /*searchCondition*/,
|
|
std::map<uint32, int> const * /*searchableAttributeInt*/,
|
|
std::map<uint32, double> const * /*searchableAttributeFloat*/,
|
|
std::map<uint32, std::string> const * /*searchableAttributeString*/,
|
|
bool & /*applicableSearchCondition*/);
|
|
|
|
SearchConditionComparisonFn s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_LAST)];
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnInt(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_int), ("searchConditionComparisonFnInt() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_int", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnInt\n"));
|
|
#endif
|
|
UNREF(searchableAttributeFloat);
|
|
UNREF(searchableAttributeString);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeInt)
|
|
{
|
|
std::map<uint32, int>::const_iterator iterAttr = searchableAttributeInt->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeInt->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return ((iterAttr->second >= searchCondition.intMin) && (iterAttr->second <= searchCondition.intMax));
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnFloat(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_float), ("searchConditionComparisonFnFloat() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_float", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnFloat\n"));
|
|
#endif
|
|
UNREF(searchableAttributeInt);
|
|
UNREF(searchableAttributeString);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeFloat)
|
|
{
|
|
std::map<uint32, double>::const_iterator iterAttr = searchableAttributeFloat->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeFloat->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return ((iterAttr->second >= searchCondition.floatMin) && (iterAttr->second <= searchCondition.floatMax));
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnStringEqual(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_string_equal), ("searchConditionComparisonFnStringEqual() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_string_equal", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnStringEqual\n"));
|
|
#endif
|
|
UNREF(searchableAttributeInt);
|
|
UNREF(searchableAttributeFloat);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeString)
|
|
{
|
|
std::map<uint32, std::string>::const_iterator iterAttr = searchableAttributeString->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeString->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return (0 == strcmp(iterAttr->second.c_str(), searchCondition.stringValue.c_str())); // lowercase is assumed (and required)
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnStringNotEqual(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_string_not_equal), ("searchConditionComparisonFnStringNotEqual() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_string_not_equal", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnStringNotEqual\n"));
|
|
#endif
|
|
UNREF(searchableAttributeInt);
|
|
UNREF(searchableAttributeFloat);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeString)
|
|
{
|
|
std::map<uint32, std::string>::const_iterator iterAttr = searchableAttributeString->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeString->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return (0 != strcmp(iterAttr->second.c_str(), searchCondition.stringValue.c_str())); // lowercase is assumed (and required)
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnStringContain(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_string_contain), ("searchConditionComparisonFnStringContain() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_string_contain", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnStringContain\n"));
|
|
#endif
|
|
UNREF(searchableAttributeInt);
|
|
UNREF(searchableAttributeFloat);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeString)
|
|
{
|
|
std::map<uint32, std::string>::const_iterator iterAttr = searchableAttributeString->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeString->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return (NULL != strstr(iterAttr->second.c_str(), searchCondition.stringValue.c_str())); // lowercase is assumed (and required)
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool searchConditionComparisonFnStringNotContain(AuctionQueryHeadersMessage::SearchCondition const & searchCondition,
|
|
std::map<uint32, int> const * searchableAttributeInt,
|
|
std::map<uint32, double> const * searchableAttributeFloat,
|
|
std::map<uint32, std::string> const * searchableAttributeString,
|
|
bool & applicableSearchCondition)
|
|
{
|
|
#ifdef _DEBUG
|
|
DEBUG_FATAL((searchCondition.comparison != AuctionQueryHeadersMessage::SCC_string_not_contain), ("searchConditionComparisonFnStringNotContain() called for attributeNameCrc=(%lu), comparison=(%d) that is not AuctionQueryHeadersMessage::SCC_string_not_contain", searchCondition.attributeNameCrc, static_cast<int>(searchCondition.comparison)));
|
|
DEBUG_REPORT_LOG(ConfigCommodityServer::getShowAllDebugInfo(), ("[Commodities Server QueryAuctionHeadersMessage] searchConditionComparisonFnStringNotContain\n"));
|
|
#endif
|
|
UNREF(searchableAttributeInt);
|
|
UNREF(searchableAttributeFloat);
|
|
|
|
// if the filter condition requires the attribute,
|
|
// it will always be an applicable filter condition;
|
|
// otherwise, it will only be applicable if the item
|
|
// has the attribute
|
|
applicableSearchCondition = searchCondition.requiredAttribute;
|
|
|
|
if (searchableAttributeString)
|
|
{
|
|
std::map<uint32, std::string>::const_iterator iterAttr = searchableAttributeString->find(searchCondition.attributeNameCrc);
|
|
if (iterAttr != searchableAttributeString->end())
|
|
{
|
|
applicableSearchCondition = true;
|
|
return (NULL == strstr(iterAttr->second.c_str(), searchCondition.stringValue.c_str())); // lowercase is assumed (and required)
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
// <stop?, search return value>
|
|
std::pair<bool, bool> s_searchConditionMatchAllAnyActionAnyMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_LAST)];
|
|
std::pair<bool, bool> s_searchConditionMatchAllAnyActionAnyNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_LAST)];
|
|
|
|
// search return value
|
|
bool s_searchConditionMatchAllAnyActionAllMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_LAST)];
|
|
bool s_searchConditionMatchAllAnyActionAllNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_LAST)];
|
|
};
|
|
|
|
using namespace AuctionNamespace;
|
|
|
|
// ======================================================================
|
|
|
|
Auction::Auction(
|
|
const NetworkId & creatorId,
|
|
int minBid,
|
|
int auctionTimer,
|
|
const NetworkId & itemId,
|
|
int itemNameLength,
|
|
const Unicode::String & itemName,
|
|
int itemType,
|
|
int itemTemplateId,
|
|
int itemTimer,
|
|
int itemSize,
|
|
const AuctionLocation & location,
|
|
int flags,
|
|
int buyNowPrice,
|
|
int userDescriptionLength,
|
|
const Unicode::String & userDescription,
|
|
int oobLength,
|
|
const Unicode::String & oobData,
|
|
std::vector<std::pair<std::string, Unicode::String> > const & attributes
|
|
) :
|
|
m_creatorId(creatorId),
|
|
m_minBid(minBid),
|
|
m_auctionTimer(auctionTimer),
|
|
m_buyNowPrice(buyNowPrice),
|
|
m_userDescriptionLength(userDescriptionLength),
|
|
m_userDescription(userDescription),
|
|
m_oobLength(oobLength),
|
|
m_oobData(oobData),
|
|
m_attributes(),
|
|
m_searchableAttributeInt(NULL),
|
|
m_searchableAttributeFloat(NULL),
|
|
m_searchableAttributeString(NULL),
|
|
m_highBid(NULL),
|
|
m_item(new AuctionItem(itemId, itemType, itemTemplateId, itemTimer, itemNameLength, itemName, creatorId, itemSize)),
|
|
m_sold(false),
|
|
m_active(true),
|
|
m_bids(),
|
|
m_location(const_cast<AuctionLocation &>(location)),
|
|
m_flags(flags),
|
|
m_trackId(-1)
|
|
{
|
|
assert(m_auctionTimer != 0);
|
|
|
|
// it it's a instant sale resource container, add "credits per unit" attribute
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()) && (m_buyNowPrice > 0))
|
|
{
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
|
|
{
|
|
if (iter->first == "credits_per_unit")
|
|
{
|
|
// ignore any existing credits per unit attribute; we'll recalculate it and add the updated attribute
|
|
continue;
|
|
}
|
|
|
|
m_attributes.push_back(*iter);
|
|
|
|
if (iter->first == "resource_contents")
|
|
{
|
|
int count = ::atoi(Unicode::wideToNarrow(iter->second).c_str());
|
|
if (count > 0)
|
|
{
|
|
char buffer[128];
|
|
int const len = snprintf(buffer, sizeof(buffer)-1, "%.6f", (static_cast<double>(m_buyNowPrice) / static_cast<double>(count)));
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
// trim trailing 0s after the decimal point
|
|
char * pChar = buffer + len - 1;
|
|
while (pChar >= buffer)
|
|
{
|
|
if (*pChar == '.')
|
|
{
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
else if (*pChar != '0')
|
|
{
|
|
++pChar;
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
|
|
--pChar;
|
|
}
|
|
|
|
m_attributes.push_back(std::make_pair("credits_per_unit", Unicode::narrowToWide(buffer)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_attributes = attributes;
|
|
}
|
|
|
|
SetItemResourceContainerClassCrc();
|
|
BuildSearchableAttributeList();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Auction::Auction(
|
|
const NetworkId & creatorId,
|
|
int minBid,
|
|
int auctionTimer,
|
|
const NetworkId & itemId,
|
|
int itemNameLength,
|
|
const Unicode::String & itemName,
|
|
int itemType,
|
|
int itemTemplateId,
|
|
int itemTimer,
|
|
int itemSize,
|
|
const AuctionLocation & location,
|
|
int flags,
|
|
int buyNowPrice,
|
|
int userDescriptionLength,
|
|
const Unicode::String & userDescription,
|
|
int oobLength,
|
|
const Unicode::String & oobData,
|
|
std::vector<std::pair<std::string, Unicode::String> > const & attributes,
|
|
bool isActive,
|
|
bool isSold
|
|
) :
|
|
m_creatorId(creatorId),
|
|
m_minBid(minBid),
|
|
m_auctionTimer(auctionTimer),
|
|
m_buyNowPrice(buyNowPrice),
|
|
m_userDescriptionLength(userDescriptionLength),
|
|
m_userDescription(userDescription),
|
|
m_oobLength(oobLength),
|
|
m_oobData(oobData),
|
|
m_attributes(),
|
|
m_searchableAttributeInt(NULL),
|
|
m_searchableAttributeFloat(NULL),
|
|
m_searchableAttributeString(NULL),
|
|
m_highBid(NULL),
|
|
m_item(new AuctionItem(itemId, itemType, itemTemplateId, itemTimer, itemNameLength, itemName, creatorId, itemSize)),
|
|
m_sold(isSold),
|
|
m_active(isActive),
|
|
m_bids(),
|
|
m_location(const_cast<AuctionLocation &>(location)),
|
|
m_flags(flags),
|
|
m_trackId(-1)
|
|
{
|
|
assert(m_auctionTimer != 0);
|
|
|
|
// it it's a instant sale resource container, add "credits per unit" attribute
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()) && (m_buyNowPrice > 0))
|
|
{
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
|
|
{
|
|
if (iter->first == "credits_per_unit")
|
|
{
|
|
// ignore any existing credits per unit attribute; we'll recalculate it and add the updated attribute
|
|
continue;
|
|
}
|
|
|
|
m_attributes.push_back(*iter);
|
|
|
|
if (iter->first == "resource_contents")
|
|
{
|
|
int count = ::atoi(Unicode::wideToNarrow(iter->second).c_str());
|
|
if (count > 0)
|
|
{
|
|
char buffer[128];
|
|
int const len = snprintf(buffer, sizeof(buffer)-1, "%.6f", (static_cast<double>(m_buyNowPrice) / static_cast<double>(count)));
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
// trim trailing 0s after the decimal point
|
|
char * pChar = buffer + len - 1;
|
|
while (pChar >= buffer)
|
|
{
|
|
if (*pChar == '.')
|
|
{
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
else if (*pChar != '0')
|
|
{
|
|
++pChar;
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
|
|
--pChar;
|
|
}
|
|
|
|
m_attributes.push_back(std::make_pair("credits_per_unit", Unicode::narrowToWide(buffer)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_attributes = attributes;
|
|
}
|
|
|
|
SetItemResourceContainerClassCrc();
|
|
BuildSearchableAttributeList();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Auction::~Auction()
|
|
{
|
|
std::vector<AuctionBid *>::iterator i = m_bids.begin();
|
|
for (; i != m_bids.end(); ++i)
|
|
{
|
|
AuctionBid *bid = (*i);
|
|
delete bid;
|
|
}
|
|
delete m_item;
|
|
|
|
delete m_searchableAttributeInt;
|
|
delete m_searchableAttributeFloat;
|
|
delete m_searchableAttributeString;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::Initialization()
|
|
{
|
|
#ifdef _DEBUG
|
|
for (int i = 0; i < static_cast<int>(AuctionQueryHeadersMessage::SCC_LAST); ++i)
|
|
{
|
|
s_searchConditionComparisonFn[i] = NULL;
|
|
}
|
|
#endif
|
|
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_int)] = searchConditionComparisonFnInt;
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_float)] = searchConditionComparisonFnFloat;
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_string_equal)] = searchConditionComparisonFnStringEqual;
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_string_not_equal)] = searchConditionComparisonFnStringNotEqual;
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_string_contain)] = searchConditionComparisonFnStringContain;
|
|
s_searchConditionComparisonFn[static_cast<int>(AuctionQueryHeadersMessage::SCC_string_not_contain)] = searchConditionComparisonFnStringNotContain;
|
|
|
|
#ifdef _DEBUG
|
|
for (int j = 0; j < static_cast<int>(AuctionQueryHeadersMessage::SCC_LAST); ++j)
|
|
{
|
|
DEBUG_FATAL((s_searchConditionComparisonFn[j] == NULL), ("s_searchConditionComparisonFn array is NULL at index (%d)", j));
|
|
}
|
|
#endif
|
|
|
|
// <stop?, search return value>
|
|
static std::pair<bool, bool> const continueToCheckNextMatch(std::make_pair(false, false));
|
|
static std::pair<bool, bool> const stopAndReturnTrue(std::make_pair(true, true));
|
|
static std::pair<bool, bool> const stopAndReturnFalse(std::make_pair(true, false));
|
|
|
|
// "will only match if all applicable attribute filter conditions match"
|
|
// ASMAA_match_all -> true after all match
|
|
// -> false after first mismatch
|
|
s_searchConditionMatchAllAnyActionAnyMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_all)] = continueToCheckNextMatch; // after any match, continue to check next match
|
|
s_searchConditionMatchAllAnyActionAnyNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_all)] = stopAndReturnFalse; // after any not match, stop and return false
|
|
s_searchConditionMatchAllAnyActionAllMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_all)] = true; // after all match, return true
|
|
s_searchConditionMatchAllAnyActionAllNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_all)] = false; // after all not match, return false
|
|
|
|
// "will only match if any applicable attribute filter condition matches"
|
|
// ASMAA_match_any -> false after all mismatch
|
|
// -> true after first match
|
|
s_searchConditionMatchAllAnyActionAnyMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_any)] = stopAndReturnTrue; // after any match, stop and return true
|
|
s_searchConditionMatchAllAnyActionAnyNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_any)] = continueToCheckNextMatch; // after any not match, continue to check next match
|
|
s_searchConditionMatchAllAnyActionAllMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_any)] = true; // after all match, return true
|
|
s_searchConditionMatchAllAnyActionAllNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_match_any)] = false; // after all not match, return false
|
|
|
|
// "will only match if no applicable attribute filter condition matches"
|
|
// (i.e. match none)
|
|
// ASMAA_not_match_all is basically "not ASMAA_match_any"
|
|
// ASMAA_not_match_all -> true after all mismatch
|
|
// -> false after first match
|
|
s_searchConditionMatchAllAnyActionAnyMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_all)] = stopAndReturnFalse; // after any match, stop and return false
|
|
s_searchConditionMatchAllAnyActionAnyNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_all)] = continueToCheckNextMatch; // after any not match, continue to check next match
|
|
s_searchConditionMatchAllAnyActionAllMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_all)] = false; // after all match, return false
|
|
s_searchConditionMatchAllAnyActionAllNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_all)] = true; // after all not match, return true
|
|
|
|
// "will only match if any applicable attribute filter condition does not match"
|
|
// ASMAA_not_match_any is basically "not ASMAA_match_all"
|
|
// ASMAA_not_match_any -> false after all match
|
|
// -> true after first mismatch
|
|
s_searchConditionMatchAllAnyActionAnyMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_any)] = continueToCheckNextMatch; // after any match, continue to check next match
|
|
s_searchConditionMatchAllAnyActionAnyNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_any)] = stopAndReturnTrue; // after any not match, stop and return true
|
|
s_searchConditionMatchAllAnyActionAllMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_any)] = false; // after all match, return false
|
|
s_searchConditionMatchAllAnyActionAllNotMatch[static_cast<int>(AuctionQueryHeadersMessage::ASMAA_not_match_any)] = true; // after all not match, return true
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::GetAttributes(std::string & output) const
|
|
{
|
|
output.clear();
|
|
|
|
output = Unicode::wideToNarrow(m_item->GetName());
|
|
output += " (";
|
|
output += m_item->GetItemId().getValueString();
|
|
output += ") (GOT_";
|
|
output += GameObjectTypes::getCanonicalName(m_item->GetCategory());
|
|
output += ")\r\n\r\n";
|
|
|
|
char buffer[2048];
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = m_attributes.begin(); iter != m_attributes.end(); ++iter)
|
|
{
|
|
snprintf(buffer, sizeof(buffer)-1, "(%s) (%s)", iter->first.c_str(), Unicode::wideToNarrow(iter->second).c_str());
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
output += buffer;
|
|
output += "\r\n";
|
|
}
|
|
|
|
if (m_searchableAttributeInt)
|
|
{
|
|
output += "\r\nInteger indexed search attribute:\r\n";
|
|
|
|
for (std::map<uint32, int>::const_iterator iter = m_searchableAttributeInt->begin(); iter != m_searchableAttributeInt->end(); ++iter)
|
|
{
|
|
std::string attributeName;
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iterAttribute = m_attributes.begin(); iterAttribute != m_attributes.end(); ++iterAttribute)
|
|
{
|
|
if (Crc::calculate(iterAttribute->first.c_str()) == iter->first)
|
|
{
|
|
attributeName = iterAttribute->first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer)-1, "(%s, %lu) (%d)", attributeName.c_str(), iter->first, iter->second);
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
output += buffer;
|
|
output += "\r\n";
|
|
}
|
|
}
|
|
|
|
if (m_searchableAttributeFloat)
|
|
{
|
|
output += "\r\nFloat indexed search attribute:\r\n";
|
|
|
|
for (std::map<uint32, double>::const_iterator iter = m_searchableAttributeFloat->begin(); iter != m_searchableAttributeFloat->end(); ++iter)
|
|
{
|
|
std::string attributeName;
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iterAttribute = m_attributes.begin(); iterAttribute != m_attributes.end(); ++iterAttribute)
|
|
{
|
|
if (Crc::calculate(iterAttribute->first.c_str()) == iter->first)
|
|
{
|
|
attributeName = iterAttribute->first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer)-1, "(%s, %lu) (%.10g)", attributeName.c_str(), iter->first, iter->second);
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
output += buffer;
|
|
output += "\r\n";
|
|
}
|
|
}
|
|
|
|
if (m_searchableAttributeString)
|
|
{
|
|
output += "\r\nString indexed search attribute:\r\n";
|
|
|
|
for (std::map<uint32, std::string>::const_iterator iter = m_searchableAttributeString->begin(); iter != m_searchableAttributeString->end(); ++iter)
|
|
{
|
|
std::string attributeName;
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iterAttribute = m_attributes.begin(); iterAttribute != m_attributes.end(); ++iterAttribute)
|
|
{
|
|
if (Crc::calculate(iterAttribute->first.c_str()) == iter->first)
|
|
{
|
|
attributeName = iterAttribute->first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
snprintf(buffer, sizeof(buffer)-1, "(%s, %lu) (%s)", attributeName.c_str(), iter->first, iter->second.c_str());
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
output += buffer;
|
|
output += "\r\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int Auction::GetHighBidAmount() const
|
|
{
|
|
if (m_highBid)
|
|
return m_highBid->GetBid();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
const AuctionBid *Auction::GetPreviousBid() const
|
|
{
|
|
if (m_bids.size() <= 1)
|
|
{
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
return m_bids[m_bids.size() - 2];
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
/**
|
|
* This isn't at all obvious, so here is a description. This function returns
|
|
* bid that will actually take place when a given bid is attempted. This bid
|
|
* can be less than the existing bid, so the bid will fail, but we still need
|
|
* to adjust the existing high bid to outbid the current one.
|
|
*/
|
|
int Auction::GetActualBid(AuctionBid *bid)
|
|
{
|
|
assert(bid != NULL);
|
|
int bidNeeded = std::max(m_minBid, bid->GetBid());
|
|
if (m_highBid != NULL)
|
|
{
|
|
bidNeeded = m_highBid->GetBid();
|
|
if (*bid > *m_highBid)
|
|
{
|
|
bidNeeded = std::max(bid->GetBid(), m_highBid->GetMaxProxyBid() + 1);
|
|
}
|
|
else if (*bid == *m_highBid)
|
|
{
|
|
bidNeeded = m_highBid->GetMaxProxyBid();
|
|
}
|
|
else if (*bid < *m_highBid)
|
|
{
|
|
bidNeeded = std::max(m_highBid->GetBid(), bid->GetMaxProxyBid() + 1);
|
|
}
|
|
}
|
|
return bidNeeded;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::AddLoadedBid(
|
|
const NetworkId & bidderId,
|
|
int bid,
|
|
int maxProxyBid
|
|
)
|
|
{
|
|
AuctionBid *auctionBid = new AuctionBid(bidderId, bid, maxProxyBid);
|
|
std::vector<AuctionBid *>::iterator i = m_bids.begin();
|
|
bool inserted = false;
|
|
for (; i != m_bids.end(); ++i)
|
|
{
|
|
if ((*i)->GetMaxProxyBid() > auctionBid->GetMaxProxyBid())
|
|
{
|
|
m_bids.insert(i, auctionBid);
|
|
inserted = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!inserted)
|
|
{
|
|
m_bids.push_back(auctionBid);
|
|
}
|
|
m_highBid = m_bids.back();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
AuctionResultCode Auction::AddBid(
|
|
const NetworkId & bidderId,
|
|
int bid,
|
|
int maxProxyBid
|
|
)
|
|
{
|
|
AuctionResultCode success = ARC_Success;
|
|
int maxBid = std::max(bid, maxProxyBid);
|
|
|
|
if (!m_active || m_sold)
|
|
{
|
|
return ARC_AuctionExpired;
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server AddBid] : Auction Expired - Auction is sold and inactive.\n"));
|
|
}
|
|
|
|
if (maxBid > MAX_BID && !m_location.IsVendorMarket())
|
|
{
|
|
return ARC_BidTooHigh;
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server AddBid] : Bid too high.\n"));
|
|
}
|
|
|
|
/*!!!TODO, put me back in after testingif (bidderId == m_creatorId)
|
|
{
|
|
return ARC_OwnerBidOnOwnItem;
|
|
}*/
|
|
if (maxProxyBid < m_minBid)
|
|
{
|
|
return ARC_BidTooLow;
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server AddBid] : Bid too low.\n"));
|
|
}
|
|
if (IsImmediate() && (GetFlags() & AUCTION_ALWAYS_PRESENT))
|
|
{
|
|
return ARC_SuccessPermanentAuction;
|
|
}
|
|
|
|
AuctionBid *auctionBid = new AuctionBid(bidderId, bid, maxProxyBid);
|
|
int newBidForHighBidder = GetActualBid(auctionBid);
|
|
if (m_highBid != NULL)
|
|
{
|
|
if (*auctionBid <= *m_highBid)
|
|
{
|
|
delete auctionBid;
|
|
success = ARC_BidOutbid;
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server AddBid] : Bid outbid.\n"));
|
|
}
|
|
}
|
|
|
|
if (success == ARC_Success)
|
|
{
|
|
m_highBid = auctionBid;
|
|
m_bids.push_back(auctionBid);
|
|
}
|
|
m_highBid->SetBid(newBidForHighBidder);
|
|
|
|
if (IsImmediate())
|
|
{
|
|
m_sold = true;
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server AddBid] : Immediate Auction - marked as sold.\n"));
|
|
|
|
// put auction into the completed auctions list so
|
|
// that the auction can be processed to completion
|
|
// during the next frame
|
|
AuctionMarket::getInstance().AddAuctionToCompletedAuctionsList(*this);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
const AuctionBid *Auction::GetPlayerBid(const NetworkId & playerId) const
|
|
{
|
|
// new bids are pushed on the back of the vector
|
|
// so, we search using a reverse iterator
|
|
std::vector<AuctionBid *>::const_reverse_iterator iter = m_bids.rbegin();
|
|
while (iter != m_bids.rend())
|
|
{
|
|
if ((*iter) && ((*iter)->GetBidderId() == playerId))
|
|
{
|
|
return (*iter);
|
|
}
|
|
++iter;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Auction::Update(int gameTime)
|
|
{
|
|
if (GetFlags() & AUCTION_ALWAYS_PRESENT)
|
|
{
|
|
return true;
|
|
}
|
|
if (m_active && m_sold)
|
|
{
|
|
//immediate sale
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Update] : Expiring immediate sale - Auction is active and sold.\n"));
|
|
|
|
if ((m_trackId != -1) && (CommodityServer::getInstance().getGameServer(m_trackId) != NULL))
|
|
{
|
|
Expire(true, false, m_trackId);
|
|
m_trackId = -1;
|
|
}
|
|
else
|
|
{
|
|
Expire(true, false);
|
|
}
|
|
}
|
|
else if (m_active && (gameTime >= m_auctionTimer))
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Update] : Auction expiring based on timer.\n"));
|
|
|
|
if ((m_trackId != -1) && (CommodityServer::getInstance().getGameServer(m_trackId) != NULL))
|
|
{
|
|
Expire(m_highBid != NULL, true, m_trackId);
|
|
m_trackId = -1;
|
|
}
|
|
else
|
|
{
|
|
Expire(m_highBid != NULL, true);
|
|
}
|
|
}
|
|
|
|
return !m_item->IsExpired(gameTime);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::Expire(bool sold, bool expiredBasedOnTimer, int track_id)
|
|
{
|
|
AuctionMarket::getInstance().RemoveFromAuctionTimerPriorityQueue(m_auctionTimer, m_item->GetItemId());
|
|
|
|
m_auctionTimer = 0;
|
|
m_active = false;
|
|
if (sold && m_highBid != NULL)
|
|
{
|
|
m_sold = true;
|
|
}
|
|
else
|
|
{
|
|
m_sold = false;
|
|
}
|
|
|
|
bool immediate = IsImmediate();
|
|
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Sold flag is %d.\n", m_sold));
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Active flag is %d.\n", m_active));
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Immediate flag is %d.\n", immediate));
|
|
|
|
if (m_sold)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Old Owner is %s.\n", m_item->GetOwnerId().getValueString().c_str()));
|
|
m_item->SetOwnerId(m_highBid->GetBidderId());
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : New Owner is %s.\n", m_item->GetOwnerId().getValueString().c_str()));
|
|
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Sending OnAuctionExpired for sold auction .\n"));
|
|
|
|
AuctionMarket::getInstance().OnAuctionExpired(
|
|
GetCreatorId(), m_sold, m_flags,
|
|
m_highBid->GetBidderId(), m_highBid->GetBid(), m_item->GetItemId(),
|
|
m_highBid->GetMaxProxyBid(),
|
|
m_location.GetLocationString(), immediate,
|
|
m_item->GetNameLength(), m_item->GetName(),
|
|
m_item->GetOwnerId(), track_id, true);
|
|
}
|
|
else
|
|
{
|
|
if (m_highBid)
|
|
{
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Sending OnAuctionExpired for non-sold auction and high bid obj .\n"));
|
|
AuctionMarket::getInstance().OnAuctionExpired(
|
|
GetCreatorId(), m_sold, m_flags,
|
|
m_highBid->GetBidderId(), m_highBid->GetBid(), m_item->GetItemId(),
|
|
m_highBid->GetMaxProxyBid(),
|
|
m_location.GetLocationString(), immediate,
|
|
m_item->GetNameLength(), m_item->GetName(),
|
|
m_item->GetOwnerId(), track_id, true);
|
|
}
|
|
else
|
|
{
|
|
// for items on a vendor placed by the vendor owner, we
|
|
// use the following rule to determine if a mail message
|
|
// should be sent to the vendor owner when the item
|
|
// auction expire and goes back into the vendor stockroom;
|
|
// the goal here is to avoid sending too many mail message
|
|
// to a vendor owner when a bunch of auctions expire around
|
|
// the same time
|
|
//
|
|
// 1) if the auction did not expire because of the auction
|
|
// timer, don't send mail. This means if the vendor owner
|
|
// withdraws the sale of an item, no mail is sent.
|
|
// 2) if the auction expired because of the auction timer,
|
|
// send mail only for the first item, and don't send
|
|
// another mail until the next auction expires after
|
|
// the vendor is next accessed or the cluster is restarted.
|
|
bool sendSellerMail = true;
|
|
if (m_location.IsVendorMarket() && m_location.IsOwner(GetCreatorId()))
|
|
{
|
|
if (!expiredBasedOnTimer)
|
|
sendSellerMail = false;
|
|
else if (m_location.GetVendorFirstTimerExpiredAuctionDate() > 0)
|
|
sendSellerMail = false;
|
|
else
|
|
m_location.SetVendorFirstTimerExpiredAuctionDate(time(0));
|
|
}
|
|
|
|
DEBUG_REPORT_LOG(true, ("[Commodities Server Auction::Expire] : Sending OnAuctionExpired for non-sold auction and null high bid obj .\n"));
|
|
AuctionMarket::getInstance().OnAuctionExpired(
|
|
GetCreatorId(), m_sold, m_flags,
|
|
NetworkId::cms_invalid, 0, m_item->GetItemId(), 0,
|
|
m_location.GetLocationString(), immediate,
|
|
m_item->GetNameLength(), m_item->GetName(),
|
|
m_item->GetOwnerId(), track_id, sendSellerMail);
|
|
}
|
|
if (m_location.IsVendorMarket() &&
|
|
m_location.IsOwner(GetCreatorId()))
|
|
{
|
|
//vendor owner canceled his own auction. Sorry this is such a hack
|
|
m_location.CancelVendorSale(this);
|
|
}
|
|
}
|
|
|
|
if (m_location.IsVendorMarket())
|
|
{
|
|
//If its a vendor, remove and then re-add the item to ensure it is in the right list
|
|
m_location.RemoveAuction(this);
|
|
m_location.AddAuction(this);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int Auction::GetBuyNowPriceWithSalesTax() const
|
|
{
|
|
if( GetBuyNowPrice() > 0 )
|
|
{
|
|
int salesTax = GetLocation().GetSalesTax();
|
|
return (int)(GetBuyNowPrice() + (GetBuyNowPrice() * (salesTax/10000.0f)));
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::AddAttributes(const std::string & attributeName, const Unicode::String & attributeValue)
|
|
{
|
|
// it it's a instant sale resource container, add "credits per unit" attribute
|
|
// once we receive the item count attribute for the resource container, and also
|
|
// ignore any existing "credits per unit" attribute to avoid duplicate attribute
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()))
|
|
{
|
|
if (m_buyNowPrice > 0)
|
|
{
|
|
if (attributeName == "credits_per_unit")
|
|
{
|
|
// ignore any existing credits per unit attribute; we'll recalculate it and add the updated attribute
|
|
return;
|
|
}
|
|
|
|
m_attributes.push_back(std::make_pair(attributeName, attributeValue));
|
|
|
|
if (attributeName == "resource_contents")
|
|
{
|
|
int count = ::atoi(Unicode::wideToNarrow(attributeValue).c_str());
|
|
if (count > 0)
|
|
{
|
|
char buffer[128];
|
|
int const len = snprintf(buffer, sizeof(buffer)-1, "%.6f", (static_cast<double>(m_buyNowPrice) / static_cast<double>(count)));
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
// trim trailing 0s after the decimal point
|
|
char * pChar = buffer + len - 1;
|
|
while (pChar >= buffer)
|
|
{
|
|
if (*pChar == '.')
|
|
{
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
else if (*pChar != '0')
|
|
{
|
|
++pChar;
|
|
*pChar = '\0';
|
|
break;
|
|
}
|
|
|
|
--pChar;
|
|
}
|
|
|
|
m_attributes.push_back(std::make_pair("credits_per_unit", Unicode::narrowToWide(buffer)));
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_attributes.push_back(std::make_pair(attributeName, attributeValue));
|
|
}
|
|
|
|
SetItemResourceContainerClassCrc(attributeName, attributeValue);
|
|
}
|
|
else
|
|
{
|
|
m_attributes.push_back(std::make_pair(attributeName, attributeValue));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::SetItemResourceContainerClassCrc()
|
|
{
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()) && ((m_item->GetResourceContainerClassCrc() == 0) || (m_item->GetResourceNameCrc() == 0)))
|
|
{
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = m_attributes.begin(); iter != m_attributes.end(); ++iter)
|
|
{
|
|
if ((iter->first == "resource_class") || (iter->first == "resource_name"))
|
|
{
|
|
SetItemResourceContainerClassCrc(iter->first, iter->second);
|
|
|
|
if ((m_item->GetResourceContainerClassCrc() != 0) && (m_item->GetResourceNameCrc() != 0))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::SetItemResourceContainerClassCrc(const std::string & attributeName, const Unicode::String & attributeValue)
|
|
{
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()) && ((m_item->GetResourceContainerClassCrc() == 0) || (m_item->GetResourceNameCrc() == 0)))
|
|
{
|
|
if ((attributeName == "resource_class") || (attributeName == "resource_name"))
|
|
{
|
|
if ((attributeName == "resource_class") && (m_item->GetResourceContainerClassCrc() == 0))
|
|
{
|
|
std::string className = Unicode::wideToNarrow(attributeValue);
|
|
if (0 == className.find("@resource/resource_names:"))
|
|
{
|
|
className = className.substr(sizeof("@resource/resource_names:") - 1);
|
|
if (!className.empty())
|
|
{
|
|
m_item->SetResourceContainerClassCrc(static_cast<int>(Crc::calculate(className.c_str())));
|
|
}
|
|
}
|
|
}
|
|
else if ((attributeName == "resource_name") && (m_item->GetResourceNameCrc() == 0))
|
|
{
|
|
std::string name = Unicode::wideToNarrow(attributeValue);
|
|
if (!name.empty())
|
|
{
|
|
m_item->SetResourceName(name);
|
|
m_item->SetResourceNameCrc(static_cast<int>(Crc::calculate(name.c_str())));
|
|
}
|
|
}
|
|
|
|
if ((m_item->GetResourceContainerClassCrc() != 0) && (m_item->GetResourceNameCrc() != 0))
|
|
{
|
|
AuctionMarket::getInstance().AddResourceType(m_item->GetResourceContainerClassCrc(), m_item->GetResourceName());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void Auction::BuildSearchableAttributeList()
|
|
{
|
|
std::map<std::string, CommoditiesAdvancedSearchAttribute::SearchAttribute const *> const & sa = CommoditiesAdvancedSearchAttribute::getSearchAttributeForGameObjectType(m_item->GetCategory());
|
|
if (sa.empty())
|
|
return;
|
|
|
|
std::map<std::string, std::string> const & saAlias = CommoditiesAdvancedSearchAttribute::getSearchAttributeNameAliasesForGameObjectType(m_item->GetCategory());
|
|
|
|
// for factory crates, also include the attributes of the item inside the crate
|
|
std::map<std::string, CommoditiesAdvancedSearchAttribute::SearchAttribute const *> const * saItemInsideFactoryCrate = NULL;
|
|
std::map<std::string, std::string> const * saAliasItemInsideFactoryCrate = NULL;
|
|
int gotItemInsideFactoryCrate = 0;
|
|
|
|
if (m_item->GetCategory() == SharedObjectTemplate::GOT_misc_factory_crate)
|
|
{
|
|
// determine the type of item inside the crate by looking at the value of the object_type attribute
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = m_attributes.begin(); iter != m_attributes.end(); ++iter)
|
|
{
|
|
if (iter->first == "object_type")
|
|
{
|
|
std::string const objectType(Unicode::wideToNarrow(iter->second));
|
|
if (0 == objectType.find("@got_n:"))
|
|
{
|
|
gotItemInsideFactoryCrate = GameObjectTypes::getGameObjectType(objectType.substr(7));
|
|
if (gotItemInsideFactoryCrate == SharedObjectTemplate::GOT_misc_factory_crate)
|
|
{
|
|
// factory crate inside factory crate???
|
|
gotItemInsideFactoryCrate = 0;
|
|
}
|
|
|
|
if (gotItemInsideFactoryCrate > 0)
|
|
{
|
|
saItemInsideFactoryCrate = &(CommoditiesAdvancedSearchAttribute::getSearchAttributeForGameObjectType(gotItemInsideFactoryCrate));
|
|
saAliasItemInsideFactoryCrate = &(CommoditiesAdvancedSearchAttribute::getSearchAttributeNameAliasesForGameObjectType(gotItemInsideFactoryCrate));
|
|
|
|
if (saItemInsideFactoryCrate->empty())
|
|
saItemInsideFactoryCrate = NULL;
|
|
|
|
if (saAliasItemInsideFactoryCrate->empty())
|
|
saAliasItemInsideFactoryCrate = NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string attributeName;
|
|
for (std::vector<std::pair<std::string, Unicode::String> >::const_iterator iter = m_attributes.begin(); iter != m_attributes.end(); ++iter)
|
|
{
|
|
// strip out the embedded trailing '\0' on those funky attribute
|
|
// name that have the trailing '\0' embedded in the string
|
|
//
|
|
// also, remove any leading/trailing white space
|
|
attributeName = Unicode::getTrim(iter->first.c_str());
|
|
|
|
// handle attribute name alias
|
|
if (!saAlias.empty())
|
|
{
|
|
std::map<std::string, std::string>::const_iterator const iterFindAlias = saAlias.find(attributeName);
|
|
if (iterFindAlias != saAlias.end())
|
|
{
|
|
attributeName = iterFindAlias->second;
|
|
}
|
|
}
|
|
|
|
std::map<std::string, CommoditiesAdvancedSearchAttribute::SearchAttribute const *>::const_iterator iterFind = sa.find(attributeName);
|
|
if ((iterFind == sa.end()) && (m_item->GetCategory() == SharedObjectTemplate::GOT_misc_factory_crate) && saItemInsideFactoryCrate)
|
|
{
|
|
// strip out the embedded trailing '\0' on those funky attribute
|
|
// name that have the trailing '\0' embedded in the string
|
|
//
|
|
// also, remove any leading/trailing white space
|
|
attributeName = Unicode::getTrim(iter->first.c_str());
|
|
|
|
// handle attribute name alias
|
|
if (saAliasItemInsideFactoryCrate && !saAliasItemInsideFactoryCrate->empty())
|
|
{
|
|
std::map<std::string, std::string>::const_iterator const iterFindAlias = saAliasItemInsideFactoryCrate->find(attributeName);
|
|
if (iterFindAlias != saAliasItemInsideFactoryCrate->end())
|
|
{
|
|
attributeName = iterFindAlias->second;
|
|
}
|
|
}
|
|
|
|
iterFind = saItemInsideFactoryCrate->find(attributeName);
|
|
if (iterFind == saItemInsideFactoryCrate->end())
|
|
{
|
|
iterFind = sa.end();
|
|
}
|
|
}
|
|
|
|
if (iterFind != sa.end())
|
|
{
|
|
if (iterFind->second->attributeDataType == CommoditiesAdvancedSearchAttribute::SADT_int)
|
|
{
|
|
if (!m_searchableAttributeInt)
|
|
m_searchableAttributeInt = new std::map<uint32, int>;
|
|
|
|
if (attributeName == "cat_wpn_other.wpn_range")
|
|
{
|
|
// special parsing for cat_wpn_other.wpn_range attribute which is
|
|
// of the "range" form 22-49m and we want to use the second value
|
|
std::string const narrowStr(Unicode::wideToNarrow(iter->second));
|
|
|
|
char const * const pChar = strchr(narrowStr.c_str(), '-');
|
|
if (pChar)
|
|
{
|
|
(*m_searchableAttributeInt)[iterFind->second->attributeNameCrc] = ::atoi(pChar + 1);
|
|
}
|
|
else
|
|
{
|
|
(*m_searchableAttributeInt)[iterFind->second->attributeNameCrc] = ::atoi(narrowStr.c_str());
|
|
}
|
|
}
|
|
else if (attributeName == "storage_module_rating")
|
|
{
|
|
// Storage Module Rating - 10
|
|
std::string const narrowStr(Unicode::wideToNarrow(iter->second));
|
|
|
|
char const * const pChar = strchr(narrowStr.c_str(), '-');
|
|
if (pChar)
|
|
{
|
|
(*m_searchableAttributeInt)[iterFind->second->attributeNameCrc] = ::atoi(pChar + 1);
|
|
}
|
|
else
|
|
{
|
|
(*m_searchableAttributeInt)[iterFind->second->attributeNameCrc] = ::atoi(narrowStr.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
(*m_searchableAttributeInt)[iterFind->second->attributeNameCrc] = ::atoi(Unicode::wideToNarrow(iter->second).c_str());
|
|
}
|
|
}
|
|
else if (iterFind->second->attributeDataType == CommoditiesAdvancedSearchAttribute::SADT_float)
|
|
{
|
|
if (!m_searchableAttributeFloat)
|
|
m_searchableAttributeFloat = new std::map<uint32, double>;
|
|
|
|
(*m_searchableAttributeFloat)[iterFind->second->attributeNameCrc] = ::atof(Unicode::wideToNarrow(iter->second).c_str());
|
|
}
|
|
else if (iterFind->second->attributeDataType == CommoditiesAdvancedSearchAttribute::SADT_string)
|
|
{
|
|
if (!m_searchableAttributeString)
|
|
m_searchableAttributeString = new std::map<uint32, std::string>;
|
|
|
|
// strip out the embedded trailing '\0' on those funky attribute
|
|
// values that have the trailing '\0' embedded in the string
|
|
//
|
|
// also, remove any leading/trailing white space
|
|
std::string const attributeValue(Unicode::getTrim(Unicode::wideToNarrow(iter->second).c_str()));
|
|
|
|
// for resource container's "Resource Class" attribute, convert
|
|
// the string id to a display string so it can be searched on
|
|
if (AuctionItem::IsCategoryResourceContainer(m_item->GetCategory()) && (attributeName == "resource_class"))
|
|
{
|
|
(*m_searchableAttributeString)[iterFind->second->attributeNameCrc] = Unicode::getTrim(Unicode::toLower(Unicode::wideToNarrow(StringId::decodeString(Unicode::narrowToWide(attributeValue)))));
|
|
}
|
|
// for fish type, convert the string id to a display string so it can be searched on
|
|
else if ((m_item->GetCategory() == SharedObjectTemplate::GOT_misc) && (attributeName == "type"))
|
|
{
|
|
(*m_searchableAttributeString)[iterFind->second->attributeNameCrc] = Unicode::getTrim(Unicode::toLower(Unicode::wideToNarrow(StringId::decodeString(Unicode::narrowToWide(attributeValue)))));
|
|
}
|
|
else
|
|
{
|
|
(*m_searchableAttributeString)[iterFind->second->attributeNameCrc] = Unicode::toLower(attributeValue);
|
|
}
|
|
}
|
|
else if (iterFind->second->attributeDataType == CommoditiesAdvancedSearchAttribute::SADT_enum)
|
|
{
|
|
if (!m_searchableAttributeString)
|
|
m_searchableAttributeString = new std::map<uint32, std::string>;
|
|
|
|
// strip out the embedded trailing '\0' on those funky attribute
|
|
// values that have the trailing '\0' embedded in the string
|
|
std::string attributeValue(Unicode::wideToNarrow(iter->second).c_str());
|
|
|
|
// handle enum value alias
|
|
std::map<std::string, std::string>::const_iterator const iterFindAlias = iterFind->second->enumValueAliasList.find(attributeValue);
|
|
if (iterFindAlias != iterFind->second->enumValueAliasList.end())
|
|
{
|
|
attributeValue = iterFindAlias->second;
|
|
}
|
|
|
|
// remove any leading/trailing white space
|
|
attributeValue = Unicode::getTrim(attributeValue);
|
|
|
|
(*m_searchableAttributeString)[iterFind->second->attributeNameCrc] = Unicode::toLower(attributeValue);
|
|
|
|
if (iterFind->second->defaultSearchValueList.count(attributeValue) <= 0)
|
|
{
|
|
std::string bufferStr;
|
|
char buffer[16];
|
|
for (size_t i = 0, size = attributeValue.size(); i < size; ++i)
|
|
{
|
|
snprintf(buffer, sizeof(buffer)-1, "%d ", attributeValue[i]);
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
bufferStr += buffer;
|
|
}
|
|
|
|
LOG("CustomerService", ("CommoditiesAttributeSearch:Item %s (%s) in category %d (%s) has value (%s) (%s) (%d) for attribute (%s) that is not in the attribute enum list",
|
|
m_item->GetItemId().getValueString().c_str(),
|
|
Unicode::wideToNarrow(m_item->GetName()).c_str(),
|
|
m_item->GetCategory(),
|
|
GameObjectTypes::getCanonicalName(m_item->GetCategory()).c_str(),
|
|
attributeValue.c_str(),
|
|
bufferStr.c_str(),
|
|
attributeValue.size(),
|
|
iterFind->second->attributeName.c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// for ship component and ship flight computer (or factory crate containing them), need to add "certifications required" search attribute
|
|
bool const itemIsShipComponentOrShipFlightComputer = ((GameObjectTypes::getMaskedType(m_item->GetCategory()) == SharedObjectTemplate::GOT_ship_component) || (m_item->GetCategory() == SharedObjectTemplate::GOT_deed_droid));
|
|
bool const factoryCrateItemIsShipComponentOrShipFlightComputer = ((m_item->GetCategory() == SharedObjectTemplate::GOT_misc_factory_crate) && (gotItemInsideFactoryCrate > 0) && ((GameObjectTypes::getMaskedType(gotItemInsideFactoryCrate) == SharedObjectTemplate::GOT_ship_component) || (gotItemInsideFactoryCrate == SharedObjectTemplate::GOT_deed_droid)));
|
|
if (itemIsShipComponentOrShipFlightComputer || factoryCrateItemIsShipComponentOrShipFlightComputer)
|
|
{
|
|
static std::map<int, std::string> requiredCerts;
|
|
|
|
// load the required certs table
|
|
static bool requiredCertsLoaded = false;
|
|
if (!requiredCertsLoaded)
|
|
{
|
|
requiredCertsLoaded = true;
|
|
|
|
DataTable * table = DataTableManager::getTable("datatables/commodity/ship_certification_attribute.iff", true);
|
|
if (table)
|
|
{
|
|
int const columnItem = table->findColumnNumber("Item");
|
|
int const columnCert = table->findColumnNumber("Certifications Required");
|
|
|
|
if ((columnItem >= 0) && (columnCert >= 0) && (columnItem != columnCert))
|
|
{
|
|
std::string item, cert;
|
|
int const numRows = table->getNumRows();
|
|
for (int i = 0; i < numRows; ++i)
|
|
{
|
|
item = Unicode::getTrim(table->getStringValue(columnItem, i));
|
|
cert = Unicode::getTrim(table->getStringValue(columnCert, i));
|
|
|
|
if (!item.empty() && !cert.empty())
|
|
{
|
|
requiredCerts[static_cast<int>(CrcLowerString::calculateCrc(item.c_str()))] = cert;
|
|
}
|
|
}
|
|
}
|
|
|
|
DataTableManager::close("datatables/commodity/ship_certification_attribute.iff");
|
|
}
|
|
}
|
|
|
|
std::map<int, std::string>::const_iterator const iterFind = requiredCerts.find(m_item->GetItemTemplateId());
|
|
if (iterFind != requiredCerts.end())
|
|
{
|
|
if (!m_searchableAttributeString)
|
|
m_searchableAttributeString = new std::map<uint32, std::string>;
|
|
|
|
static uint32 const shipCertAttributeCrc = Crc::calculate("ship_equipment_certification_search_attribute");
|
|
static uint32 const astromechCertAttributeCrc = Crc::calculate("astromech_certification_search_attribute");
|
|
|
|
if (itemIsShipComponentOrShipFlightComputer)
|
|
{
|
|
(*m_searchableAttributeString)[((m_item->GetCategory() == SharedObjectTemplate::GOT_deed_droid) ? astromechCertAttributeCrc : shipCertAttributeCrc)] = iterFind->second;
|
|
}
|
|
else
|
|
{
|
|
(*m_searchableAttributeString)[((gotItemInsideFactoryCrate == SharedObjectTemplate::GOT_deed_droid) ? astromechCertAttributeCrc : shipCertAttributeCrc)] = iterFind->second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool Auction::IsAdvancedFilterMatch(std::list<AuctionQueryHeadersMessage::SearchCondition> const & advancedSearch, int matchAllAny) const
|
|
{
|
|
if (advancedSearch.empty())
|
|
return true;
|
|
|
|
// the following is the logic what will be used to determine match/no match
|
|
|
|
// "will only match if all applicable attribute filter conditions match"
|
|
// ASMAA_match_all -> true after all match
|
|
// -> false after first mismatch
|
|
|
|
// "will only match if any applicable attribute filter condition does not match"
|
|
// ASMAA_not_match_any is basically "not ASMAA_match_all"
|
|
// ASMAA_not_match_any -> false after all match
|
|
// -> true after first mismatch
|
|
|
|
// "will only match if any applicable attribute filter condition matches"
|
|
// ASMAA_match_any -> false after all mismatch
|
|
// -> true after first match
|
|
|
|
// "will only match if no applicable attribute filter condition matches"
|
|
// (i.e. match none)
|
|
// ASMAA_not_match_all is basically "not ASMAA_match_any"
|
|
// ASMAA_not_match_all -> true after all mismatch
|
|
// -> false after first match
|
|
|
|
int filterCountMatch = 0;
|
|
int filterCountNotMatch = 0;
|
|
bool applicableFilter;
|
|
bool matchFilter;
|
|
for (std::list<AuctionQueryHeadersMessage::SearchCondition>::const_iterator iter = advancedSearch.begin(); iter != advancedSearch.end(); ++iter)
|
|
{
|
|
matchFilter = (*(s_searchConditionComparisonFn[static_cast<int>(iter->comparison)]))(*iter, m_searchableAttributeInt, m_searchableAttributeFloat, m_searchableAttributeString, applicableFilter);
|
|
|
|
if (!applicableFilter)
|
|
{
|
|
continue;
|
|
}
|
|
else if (matchFilter) // match filter
|
|
{
|
|
if (s_searchConditionMatchAllAnyActionAnyMatch[matchAllAny].first)
|
|
return s_searchConditionMatchAllAnyActionAnyMatch[matchAllAny].second;
|
|
|
|
++filterCountMatch;
|
|
}
|
|
else // does not match filter
|
|
{
|
|
if (s_searchConditionMatchAllAnyActionAnyNotMatch[matchAllAny].first)
|
|
return s_searchConditionMatchAllAnyActionAnyNotMatch[matchAllAny].second;
|
|
|
|
++filterCountNotMatch;
|
|
}
|
|
}
|
|
|
|
// if we get this far, then either all applicable filters
|
|
// match or all applicable filters do not match, or there
|
|
// were no applicable filters
|
|
//
|
|
// it's impossible to have gotten this far and have some
|
|
// applicable filters match and some not match, because
|
|
// that situation should have caused an immediate return
|
|
// as soon as it was encountered
|
|
if (filterCountMatch > 0)
|
|
{
|
|
// all applicable filters match
|
|
return s_searchConditionMatchAllAnyActionAllMatch[matchAllAny];
|
|
}
|
|
else if (filterCountNotMatch > 0)
|
|
{
|
|
// all applicable filters do not match
|
|
return s_searchConditionMatchAllAnyActionAllNotMatch[matchAllAny];
|
|
}
|
|
|
|
// no filter was applicable, so treat as if no filter was specified
|
|
return true;
|
|
}
|
|
|
|
// ======================================================================
|