hxp
2019-03-04 3d658259b25c4914c766c43aeea883bdd0847c5d
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
@@ -6,91 +6,1334 @@
#
# @todo:拍卖行
# @author hxp
# @date 2019-02-19
# @date 2019-03-04
# @version 1.0
#
# 详细描述: 拍卖行
#
#-------------------------------------------------------------------------------
#"""Version = 2019-02-19 12:00"""
#"""Version = 2019-03-04 17:00"""
#-------------------------------------------------------------------------------
import CommFunc
import GameWorld
import PyDataManager
import PlayerControl
import DataRecordPack
import PyGameDataStruct
import PlayerCompensation
import ChPyNetSendPack
import IpyGameDataPY
import NetPackCommon
import PlayerBourse
import PlayerFamily
import ChConfig
import operator
import time
import math
import json
#拍卖关注管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
class AuctionAttentionManager(object):
    def __init__(self):
# 拍卖记录类型定义
AuctionRecordTypeList = (
AuctionRecordType_MyAuction, # 我的拍品
AuctionRecordType_FamilyAuction, # 仙盟拍品
AuctionRecordType_MyBid, # 我的竞拍
) = range(3)
# 拍卖记录结果定义
AuctionRecordResultList = (
AuctionRecordResult_SellFail, # 流拍
AuctionRecordResult_SellOK, # 拍卖成交
AuctionRecordResult_Recycle, # 系统回收
AuctionRecordResult_BidOK, # 竞价成功
AuctionRecordResult_BidFail, # 竞价失败
AuctionRecordResult_MoveToWorld, # 仙盟拍品转移到全服拍品
) = range(6)
# 当前拍品归类 0-全服拍品 1-仙盟私有拍品
AuctionType_World = 0
AuctionType_Family = 1
#拍卖行状态开关
def GetAuctionHouseState(): return PlayerBourse.GetOpenState()
def OnPlayerLogin(curPlayer):
    Sync_PlayerAuctionItemInfo(curPlayer)
    Sync_FamilyAuctionItemInfo(curPlayer, curPlayer.GetFamilyID())
    Sync_PlayerBiddingItemInfo(curPlayer)
    Sync_PlayerAttentionAuctionItemID(curPlayer, False)
    return
def OnPlayerLeaveServer(curPlayer):
    playerID = curPlayer.GetPlayerID()
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    auctionItemMgr.myAttentionItemDict.pop(playerID, None)
    return
##--------------------------------------- 拍卖记录 -------------------------------------------------
def GetMyAuctionItemRecord(playerID):
    ## 获取我的拍品记录
    return PyDataManager.GetAuctionRecordManager().myAuctionItemRecordDict.get(playerID, [])
def GetMyBiddingItemRecord(playerID):
    ## 获取我的竞拍记录
    return PyDataManager.GetAuctionRecordManager().myBidItemRecordDict.get(playerID, [])
def GetFamilyAuctionItemRecord(familyID):
    ## 获取仙盟拍品记录
    return PyDataManager.GetAuctionRecordManager().familyAuctionItemRecordDict.get(familyID, [])
def AddAuctionRecord(auctionItem, recordResult):
    ''' 添加拍卖行记录
    @param auctionItem: 相关的拍品实例
    @param recordResult: 记录结果 0-流拍 1-拍卖成交 2-回收 3-竞价成功 4-竞价失败
    '''
    if not auctionItem:
        return
    
    ## ===========================================================================================
    playerID, familyID, bidderID = auctionItem.PlayerID, auctionItem.FamilyID, auctionItem.BidderID
    recordPlayerID, recordFamilyID = 0, 0
    
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        GameWorld.Log("Save AuctionAttention count :%s" % cnt)
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load AuctionAttention count :%s" % cnt)
        return pos
#拍卖记录管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
class AuctionRecordManager(object):
    def __init__(self):
    if recordResult in [AuctionRecordResult_BidOK, AuctionRecordResult_BidFail]:
        recordType = AuctionRecordType_MyBid
        recordPlayerID = bidderID
    else:
        if familyID:
            recordType = AuctionRecordType_FamilyAuction
            recordFamilyID = familyID
        else:
            recordType = AuctionRecordType_MyAuction
            recordPlayerID = playerID
    if not recordPlayerID and not recordFamilyID:
        return
    
    ## ===========================================================================================
    newRecordData = PyGameDataStruct.tagDBAuctionRecord()
    newRecordData.ItemGUID = auctionItem.ItemGUID
    newRecordData.PlayerID = recordPlayerID
    newRecordData.FamilyID = recordFamilyID
    newRecordData.RecordType = recordType
    newRecordData.RecordResult = recordResult
    newRecordData.RecordTime = GameWorld.GetCurrentDataTimeStr()
    newRecordData.BidderPrice = auctionItem.BidderPrice
    newRecordData.BidderName = auctionItem.BidderName
    newRecordData.ItemID = auctionItem.ItemID
    newRecordData.Count = auctionItem.Count
    newRecordData.UserData = auctionItem.UserData
    newRecordData.UserDataLen = auctionItem.UserDataLen
    AddNewAuctionRecord(newRecordData)
    
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        GameWorld.Log("Save AuctionRecord count :%s" % cnt)
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load AuctionRecord count :%s" % cnt)
        return pos
    if recordFamilyID:
        Sync_PlayerAuctionRecordInfo(None, recordType, newRecordData, recordFamilyID)
    elif recordPlayerID:
        recordPlayer = GameWorld.GetPlayerManager().FindPlayerByID(recordPlayerID)
        if recordPlayer and not PlayerControl.GetIsTJG(recordPlayer):
            Sync_PlayerAuctionRecordInfo(recordPlayer, recordType, newRecordData)
    return
def AddNewAuctionRecord(newRecordData):
    playerID = newRecordData.PlayerID
    familyID = newRecordData.FamilyID
    recordType = newRecordData.RecordType
    if recordType == AuctionRecordType_MyAuction:
        keyID = playerID
        recordDict = PyDataManager.GetAuctionRecordManager().myAuctionItemRecordDict
    elif recordType == AuctionRecordType_FamilyAuction:
        keyID = familyID
        recordDict = PyDataManager.GetAuctionRecordManager().familyAuctionItemRecordDict
    elif recordType == AuctionRecordType_MyBid:
        keyID = playerID
        recordDict = PyDataManager.GetAuctionRecordManager().myBidItemRecordDict
    else:
        return
    recordList = recordDict.get(keyID, [])
    maxCount = min(50, IpyGameDataPY.GetFuncCfg("AuctionHouse", 3))
    if len(recordList) >= maxCount:
        del recordList[0]
    recordList.append(newRecordData)
    recordDict[keyID] = recordList
    return
##-------------------------------------- 拍卖关注 ------------------------------------------------
#拍卖物品管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
class AuctionItemManager(object):
def GetPlayerAuctionAttention(attentionMgr, playerID):
    ## 获取玩家关注的物品ID列表
    if playerID not in attentionMgr.playerAttentionDict:
        return []
    attentionObj = attentionMgr.playerAttentionDict[playerID]
    attentionItemIDList = attentionObj.AttentionItemIDList
    return attentionItemIDList
def AddPlayerAuctionAttention(attentionMgr, playerID, itemID):
    ## 添加玩家关注的物品ID
    if playerID not in attentionMgr.playerAttentionDict:
        dbData = PyGameDataStruct.tagDBAuctionAttention()
        dbData.PlayerID = playerID
        __InitAuctionAttentionAttrEx(dbData)
        attentionMgr.playerAttentionDict[playerID] = dbData
    attentionObj = attentionMgr.playerAttentionDict[playerID]
    attentionItemIDList = attentionObj.AttentionItemIDList
    if len(attentionItemIDList) >= IpyGameDataPY.GetFuncCfg("AuctionHouse", 5):
        GameWorld.DebugLog("关注拍品数达到上限!", playerID)
        return
    if itemID in attentionItemIDList:
        return True
    attentionItemIDList.append(itemID)
    attentionObj.AttentionInfo = str(attentionItemIDList)
    attentionObj.AttentionLen = len(attentionObj.AttentionInfo)
    return True
def DelPlayerAuctionAttention(attentionMgr, playerID, itemID):
    ## 删除玩家关注的物品ID
    if playerID not in attentionMgr.playerAttentionDict:
        return
    attentionObj = attentionMgr.playerAttentionDict[playerID]
    attentionItemIDList = attentionObj.AttentionItemIDList
    if itemID not in attentionItemIDList:
        return
    attentionItemIDList.remove(itemID)
    attentionObj.AttentionInfo = str(attentionItemIDList)
    attentionObj.AttentionLen = len(attentionObj.AttentionInfo)
    return True
def OnLoadAuctionAttentionDataEx(attentionData):
    ## 加载拍卖关注表时附加处理
    __InitAuctionAttentionAttrEx(attentionData)
    return
def __InitAuctionAttentionAttrEx(attentionData):
    ## 初始化拍卖关注实例附加属性
    setattr(attentionData, "AttentionItemIDList", [] if not attentionData.AttentionInfo else json.loads(attentionData.AttentionInfo))
    return
##-------------------------------------------------------------------------------------------------
def OnLoadAuctionItemDataEx(dbData):
    ## 加载拍卖物品表时附加处理
    __InitAuctionItemAttrEx(dbData)
    playerID = dbData.PlayerID
    familyID = dbData.FamilyID
    bidderID = dbData.BidderID
    
    def __init__(self):
    pyAuctionItemMgr = PyDataManager.GetAuctionItemManager()
    pyAuctionItemMgr.allAuctionItemByEndTimeList.append(dbData)
    if dbData.AuctionType == AuctionType_World:
        pyAuctionItemMgr.worldAuctionItemList.append(dbData)
    if familyID:
        familyItemList = pyAuctionItemMgr.familyAuctionItemDict.get(familyID, [])
        familyItemList.append(dbData)
        pyAuctionItemMgr.familyAuctionItemDict[familyID] = familyItemList
    if playerID:
        myItemList = pyAuctionItemMgr.myAuctionItemDict.get(playerID, [])
        myItemList.append(dbData)
        pyAuctionItemMgr.myAuctionItemDict[playerID] = myItemList
        if str(playerID) in dbData.BidderIDInfo:
            if playerID == bidderID:
                nowBidItemList = pyAuctionItemMgr.nowBiddingAuctionItemDict.get(playerID, [])
                nowBidItemList.append(dbData)
                pyAuctionItemMgr.nowBiddingAuctionItemDict[playerID] = nowBidItemList
            else:
                hisBidItemList = pyAuctionItemMgr.hisBiddingAuctionItemDict.get(playerID, [])
                hisBidItemList.append(dbData)
                pyAuctionItemMgr.hisBiddingAuctionItemDict[playerID] = hisBidItemList
    return
def OnLoadAuctionItemDataOK():
    ## 加载拍卖物品表完成后的处理
    __SortAuctionitem()
    return
def __InitAuctionItemAttrEx(auctionItem, ipyData=None):
    ## 初始化拍品实例附加属性
    if not ipyData:
        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", auctionItem.ItemID)
    setattr(auctionItem, "Sortpriority", 99999 - (0 if not ipyData else ipyData.GetSortpriority())) # 排序优先级归组
    setattr(auctionItem, "BiddingQueryID", 0) # 当前正在竞价的玩家ID
    setattr(auctionItem, "BiddingQueryTick", 0) # 当前正在竞价的tick
    setattr(auctionItem, "EndTime", 0) # 结束竞价time值
    __SetAuctionItemEndTime(auctionItem, ipyData)
    return True
def __SetAuctionItemEndTime(auctionItem, ipyData):
    ## 更新拍品结束竞价time值
    if not ipyData:
        return
    addTimeStr = auctionItem.AddTime
    auctionType = auctionItem.AuctionType
    addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
    noticeMinutes = ipyData.GetNoticeSaleMinutes()
    saleMinutes = ipyData.GetFamilySaleMinutes() if auctionType == AuctionType_Family else ipyData.GetWorldSaleMinutes()
    auctionItem.EndTime = addTime + (noticeMinutes + saleMinutes) * 60
    __AddAuctionItemEndTimeByBid(auctionItem)
    return
def __AddAuctionItemEndTimeByBid(auctionItem):
    # 根据最后一次竞价时间进行加时
    if not auctionItem.BidderPrice:
        return
    bidTime = GameWorld.ChangeTimeStrToNum(auctionItem.BiddingTime)
    endTime = bidTime + IpyGameDataPY.GetFuncCfg("AuctionHouse", 4)
    if endTime < auctionItem.EndTime:
        return
    auctionItem.EndTime = endTime
    return True
def __GetAuctionItemDRDict(auctionItem):
    drDict = {"GUID":auctionItem.ItemGUID, "PlayerID":auctionItem.PlayerID, "FamilyID":auctionItem.FamilyID,
              "ItemID":auctionItem.ItemID, "Count":auctionItem.Count, "AuctionType":auctionItem.AuctionType,
              "AddTime":auctionItem.AddTime, "BiddingTime":auctionItem.BiddingTime, "BidderID":auctionItem.BidderID,
              "BidderPrice":auctionItem.BidderPrice, "UserData":auctionItem.UserData,
              "EndTime":"" if not auctionItem.EndTime else GameWorld.ChangeTimeNumToStr(auctionItem.EndTime),
              "FamilyPlayerIDInfo":auctionItem.FamilyPlayerIDInfo, "BidderIDInfo":auctionItem.BidderIDInfo}
    return drDict
def GetAuctionItem(itemGUID):
    ## 获取拍品实例
    return PyDataManager.GetAuctionItemManager().allAuctionItemDict.get(itemGUID)
def GetPlayerAuctionItemList(playerID):
    ## 玩家拍卖中的拍品列表
    return PyDataManager.GetAuctionItemManager().myAuctionItemDict.get(playerID, [])
def GetFamilyAuctionItemList(familyID):
    ## 仙盟拍卖中的拍品列表
    return PyDataManager.GetAuctionItemManager().familyAuctionItemDict.get(familyID, [])
def OnAuctionItemTimeProcess(curTime):
    ## 拍卖行拍品定时处理,每秒触发一次
    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
    if not allAuctionItemByEndTimeList:
        return
    
    ## ===========================================================================================
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
    endItemList = [] # 结束竞价的拍品列表
    moveToWorldItemList = [] # 转移到全服拍卖的仙盟拍品列表
    doCount = len(allAuctionItemByEndTimeList)
    while doCount > 0 and allAuctionItemByEndTimeList:
        doCount -= 1
        auctionItem = allAuctionItemByEndTimeList[0]
        if curTime < auctionItem.EndTime:
            break
        allAuctionItemByEndTimeList.pop(0)
        
        GameWorld.Log("Save AuctionItem count :%s" % cnt)
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load AuctionItem count :%s" % cnt)
        return pos
        # 没有人竞价的仙盟拍品
        if not auctionItem.BidderPrice and auctionItem.FamilyID and auctionItem.AuctionType == AuctionType_Family:
            moveToWorldItemList.append(auctionItem)
        else:
            endItemList.append(auctionItem)
    __EndAuctionItem(endItemList, "ByTime")
    __MoveFamilyAuctionItemToWorld(moveToWorldItemList)
    return
def __MoveFamilyAuctionItemToWorld(auctionItemList):
    ## 仙盟拍品转移到全服
    if not auctionItemList:
        return
    curTimeStr = GameWorld.GetCurrentDataTimeStr()
    attentionMgr = PyDataManager.GetAuctionAttentionManager()
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    notifyWorldAddItemList = [] # 新增全服拍品通知 [[itemGUID, itemID, playerID], ...]
    for auctionItem in auctionItemList:
        itemGUID = auctionItem.ItemGUID
        itemID = auctionItem.ItemID
        itemCount = auctionItem.Count
        playerID = auctionItem.PlayerID
        familyID = auctionItem.FamilyID
        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
        auctionItem.AuctionType = AuctionType_World
        auctionItem.AddTime = curTimeStr
        __SetAuctionItemEndTime(auctionItem, ipyData)
        auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
        auctionItemMgr.worldAuctionItemList.append(auctionItem)
        notifyWorldAddItemList.append([itemGUID, itemID, playerID])
        # 添加进我的关注
        for attentionPlayerID, attentionList in auctionItemMgr.myAttentionItemDict.items():
            if itemID not in GetPlayerAuctionAttention(attentionMgr, attentionPlayerID):
                continue
            if auctionItem not in attentionList:
                attentionList.append(auctionItem)
        if ipyData.GetNeedWorldNotify():
            PlayerControl.WorldNotify(0, "Paimai4", [itemID, ipyData.GetNoticeSaleMinutes()])
        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
        DR_AuctionHouse(None, "FamilyToWorld", drDict)
        GameWorld.DebugLog("仙盟拍品转移到全服: itemID=%s,itemCount=%s,familyID=%s,itemGUID=%s"
                           % (itemID, itemCount, familyID, itemGUID))
    __SortAuctionitem()
    __SyncRefreshAuctionItem(auctionItemList)
    __NotifyAuctionPlayerAddItem(notifyWorldAddItemList)
    return
def __SortAuctionitem(isSortWorldItem=True):
    ''' 刷新全部拍品排序
        后端排序
        1.全服拍品: 因为全服拍品数量比较大,采用只查询指定个数拍品,所以只能由后端排序后同步
        2.全部拍品竞价结束时间:后端需要处理竞价结束
        前端排序:
        1.个人拍品:个数较少,全部同步,由前端自己排序
        2.仙盟拍品:个数较少,全部同步,由前端自己排序
        3.竞价拍品:个数较少,全部同步,由前端自己整合排序,区分最高竞价、历史竞价
        4.关注拍品:个数较少,全部同步,由前端自己排序
        触发排序时机:
        1-上架新全服拍品
        2-仙盟拍品转移全服拍品,相当于上架新全服拍品
        3-竞价拍品触发加时时,需要对结束时间重排,竞价加时的只触发加时,对全服拍品的排序没有影响,故可不重拍全服拍品
    '''
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    auctionItemMgr.allAuctionItemByEndTimeList.sort(key=operator.attrgetter("EndTime"))
    if isSortWorldItem:
        auctionItemMgr.worldAuctionItemList.sort(key=operator.attrgetter("Sortpriority", "AddTime"))
        auctionItemMgr.worldAuctionItemQueryDict = {} # 重置全服拍品条件查询,下次有玩家查询时再重新刷新
    return
def __EndAuctionItem(endItemList, endEvent):
    ''' 结束拍品竞拍
    @param delItemStateDict: 删除的拍品竞拍状态
    '''
    if not endItemList:
        return 0
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    clearAuctionItemList = []
    for auctionItem in endItemList:
        if not auctionItem:
            continue
        itemGUID = auctionItem.ItemGUID
        itemID = auctionItem.ItemID
        itemCount = auctionItem.Count
        playerID = auctionItem.PlayerID
        familyID = auctionItem.FamilyID
        bidderID = auctionItem.BidderID # 当前竞价玩家ID
        bidderPrice = auctionItem.BidderPrice # 当前竞价,有人竞价的话代表竞拍成功
        endType = ""
        # 有人竞价,成交
        if bidderID and bidderPrice:
            endType = "OK"
            # 竞拍成功邮件,发放物品
            paramList = [bidderPrice]
            detail = {"ItemGUID":itemGUID}
            addItemList = [{"ItemID":itemID, "Count":itemCount, "IsBind":True, "UserData":auctionItem.UserData}]
            PlayerCompensation.SendMailByKey("PaimaiMail3", [bidderID], addItemList, paramList, detail=detail)
            AddAuctionRecord(auctionItem, AuctionRecordResult_BidOK)
            # 拍卖成功收益,都以玩家收益向上取整
            if familyID and auctionItem.FamilyPlayerIDInfo:
                familyPlayerIDList = json.loads(auctionItem.FamilyPlayerIDInfo)
                taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 2) # 仙盟拍品税率百分比
                personMaxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 3) # 仙盟拍品个人最大收益百分比
                giveTotalGold = int(math.ceil(bidderPrice * (100 - taxRate) / 100.0))
                giveMaxGold = int(math.ceil(giveTotalGold * personMaxRate / 100.0))
                memCount = len(familyPlayerIDList)
                giveGoldAverage = min(giveMaxGold, int(math.ceil(giveTotalGold * 1.0 / memCount))) # 有收益的人平分
                # 仙盟拍品收益邮件
                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice, "FamilyPlayerIDList":familyPlayerIDList,
                          ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionGain}
                paramList = [itemID, auctionItem.BidderName, bidderPrice, taxRate, giveGoldAverage, personMaxRate]
                PlayerCompensation.SendMailByKey("PaimaiMail6", familyPlayerIDList, [], paramList, gold=giveGoldAverage, detail=detail)
            elif playerID:
                taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 1) # 全服拍品税率百分比
                givePlayerGold = int(math.ceil(bidderPrice * (100 - taxRate) / 100.0))
                # 个人拍卖收益邮件
                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice,
                          ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionGain}
                paramList = [itemID, auctionItem.BidderName, bidderPrice, taxRate, givePlayerGold]
                PlayerCompensation.SendMailByKey("PaimaiMail5", [playerID], [], paramList, gold=givePlayerGold, detail=detail)
            AddAuctionRecord(auctionItem, AuctionRecordResult_SellOK)
        else:
            # 仙盟拍品回收
            if familyID:
                endType = "Recycle"
                AddAuctionRecord(auctionItem, AuctionRecordResult_Recycle)
            # 个人拍品流拍,物品返还
            else:
                endType = "Return"
                # 流拍返还物品邮件
                paramList = []
                detail = {"ItemGUID":itemGUID}
                addItemList = [{"ItemID":itemID, "Count":itemCount, "IsBind":True, "UserData":auctionItem.UserData}]
                PlayerCompensation.SendMailByKey("PaimaiMail4", [playerID], addItemList, paramList, detail=detail)
                AddAuctionRecord(auctionItem, AuctionRecordResult_SellFail)
        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem), "EndType":endType, "EndEvent":endEvent}
        DR_AuctionHouse(None, "EndAuctionItem", drDict)
        GameWorld.DebugLog("结束竞拍: itemID=%s,itemCount=%s,familyID=%s,endType=%s,endEvent=%s,itemGUID=%s"
                           % (itemID, itemCount, familyID, endType, endEvent, itemGUID), playerID)
        auctionItemMgr.allAuctionItemDict.pop(itemGUID, None)
        if auctionItem in auctionItemMgr.allAuctionItemByEndTimeList:
            auctionItemMgr.allAuctionItemByEndTimeList.remove(auctionItem)
        if auctionItem in auctionItemMgr.worldAuctionItemList:
            auctionItemMgr.worldAuctionItemList.remove(auctionItem)
        for queryItemList in auctionItemMgr.worldAuctionItemQueryDict.values():
            if auctionItem in queryItemList:
                queryItemList.remove(auctionItem)
        if familyID and familyID in auctionItemMgr.familyAuctionItemDict:
            familyItemList = auctionItemMgr.familyAuctionItemDict[familyID]
            if auctionItem in familyItemList:
                familyItemList.remove(auctionItem)
        for nowBiddingItemList in auctionItemMgr.nowBiddingAuctionItemDict.values():
            if auctionItem in nowBiddingItemList:
                nowBiddingItemList.remove(auctionItem)
        for hisBiddingItemList in auctionItemMgr.hisBiddingAuctionItemDict.values():
            if auctionItem in hisBiddingItemList:
                hisBiddingItemList.remove(auctionItem)
        if playerID and playerID in auctionItemMgr.myAuctionItemDict:
            playerItemList = auctionItemMgr.myAuctionItemDict[playerID]
            if auctionItem in playerItemList:
                playerItemList.remove(auctionItem)
        for attentionItemList in auctionItemMgr.myAttentionItemDict.values():
            if auctionItem in attentionItemList:
                attentionItemList.remove(auctionItem)
        clearItem = ChPyNetSendPack.tagGCClearAuctionItem()
        clearItem.ItemGUID = itemGUID
        clearAuctionItemList.append(clearItem)
    if not clearAuctionItemList:
        return 0
    clearCount = len(clearAuctionItemList)
    clientPack = ChPyNetSendPack.tagGCClearAuctionItemInfo()
    clientPack.ClearAuctionItemList = clearAuctionItemList
    clientPack.ClearCount = len(clientPack.ClearAuctionItemList)
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetActivePlayerCount()):
        player = playerManager.GetActivePlayerAt(i)
        if player == None:
            continue
        if PlayerControl.GetIsTJG(player):
            continue
        NetPackCommon.SendFakePack(player, clientPack)
    return clearCount
def MapServer_AuctionHouseLogic(curPlayer, msgList, tick):
    ## 地图玩家拍卖处理
    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
    queryType, queryData = msgList
    result = []
    GameWorld.Log("收到地图拍卖行信息: queryType=%s,queryData=%s" % (queryType, queryData), playerID)
    if not GetAuctionHouseState():
        if curPlayer:
            PlayerControl.NotifyCode(curPlayer, "AuctionHouseClose")
        return
    # 查询玩家可否上架
    if queryType == "AddAuctionItemQuery":
        canSell = __CheckPlayerCanAddAuctionItem(curPlayer)
        if not canSell:
            return
        result = [canSell]
    # 执行上架物品
    elif queryType == "AddAuctionItem":
        addAuctionItemList = queryData
        __AddAuctionItemByList(curPlayer, addAuctionItemList)
        return
    # 查询玩家可否竞价物品
    elif queryType == "BidAuctionItemQuery":
        itemGUID, biddingPrice = queryData
        itemID, errInfo = __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, True)
        GameWorld.DebugLog("    itemID=%s,errInfo=%s" % (itemID, errInfo), playerID)
        if errInfo:
            return
        if not itemID:
            return
        result = [itemID]
    # 执行玩家竞价物品
    elif queryType == "BidAuctionItem":
        itemGUID, biddingPrice = queryData
        itemID, errInfo = __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, False)
        if errInfo:
            GameWorld.DebugLog("    errInfo=%s" % errInfo, playerID)
            drDict = {"ItemGUID":itemGUID, "BiddingPrice":biddingPrice, "ErrInfo":errInfo}
            DR_AuctionHouse(curPlayer, "BidAuctionItemError", drDict)
        return
    elif queryType == "ClearAuctionItem":
        __DoGMClearAuctionItem(curPlayer)
        return
    elif queryType == "PrintAuctionItem":
        __DoGMPrintAuctionItem(curPlayer)
        return
    resultMsg = str([queryType, queryData, result])
    if curPlayer:
        curPlayer.MapServer_QueryPlayerResult(0, 0, "AuctionHouse", resultMsg, len(resultMsg))
    return
def __CheckPlayerCanAddAuctionItem(curPlayer):
    ## 检查玩家可否上架拍品
    playerID = curPlayer.GetPlayerID()
    maxSellCount = IpyGameDataPY.GetFuncCfg("AuctionHouse", 2)
    if maxSellCount:
        playerAuctionItemList = GetPlayerAuctionItemList(playerID)
        playerSellCount = len(playerAuctionItemList)
        canSell = playerSellCount < maxSellCount
        if not canSell:
            PlayerControl.NotifyCode(curPlayer, "SellCountLimit")
        return canSell
    return True
def __AddAuctionItemByList(curPlayer, addAuctionItemList):
    ''' 批量添加拍品
    @param curPlayer: 可能为None
    '''
    notifyWorldAddItemList = [] # 新增全服拍品通知 [[itemGUID, itemID, playerID], ...]
    notifyFamilyAddItemDict = {} # 新增仙盟拍品通知 {familyID:[auctionItem, ...], ...}
    for playerID, familyID, familyPlayerIDList, itemData in addAuctionItemList:
        if not playerID and not familyID:
            continue
        auctionItem = __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData)
        if not auctionItem:
            continue
        if familyID:
            familyAddItemList = notifyFamilyAddItemDict.get(familyID, [])
            familyAddItemList.append(auctionItem)
            notifyFamilyAddItemDict[familyID] = familyAddItemList
        else:
            itemGUID = auctionItem.ItemGUID
            itemID = auctionItem.ItemID
            notifyWorldAddItemList.append([itemGUID, itemID, playerID])
    if notifyFamilyAddItemDict or notifyWorldAddItemList:
        isSortWorldItem = notifyWorldAddItemList != []
        __SortAuctionitem(isSortWorldItem=isSortWorldItem)
    # 通知新增仙盟拍品
    for familyID, familyAddItemList in notifyFamilyAddItemDict.items():
        Sync_FamilyAuctionItemInfo(None, familyID, familyAddItemList)
    # 通知全服拍品关注玩家
    __NotifyAuctionPlayerAddItem(notifyWorldAddItemList)
    return
def __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData):
    ## 添加拍品
    itemID = itemData.get("ItemID", 0)
    ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
    if not ipyData:
        return
    itemGUID = itemData.get("GUID", "")
    auctionItem = PyGameDataStruct.tagDBAuctionItem()
    auctionItem.ItemGUID = itemGUID
    auctionItem.PlayerID = playerID
    auctionItem.FamilyID = familyID
    auctionItem.ItemID = itemID
    auctionItem.Count = itemData.get("ItemCount", 0)
    auctionItem.AddTime = GameWorld.GetCurrentDataTimeStr()
    auctionItem.ItemType = itemData.get("ItemType", 0)
    auctionItem.ItemJobLimit = itemData.get("ItemJobLimit", 0)
    auctionItem.ItemClassLV = itemData.get("ItemClassLV", 0)
    auctionItem.UserData = itemData.get("UserData", "")
    auctionItem.UserDataLen = len(auctionItem.UserData)
    auctionItem.FamilyPlayerIDInfo = str(familyPlayerIDList)
    auctionItem.FamilyPlayerIDLen = len(auctionItem.FamilyPlayerIDInfo)
    if not __InitAuctionItemAttrEx(auctionItem):
        return
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    auctionItemMgr.allAuctionItemDict[auctionItem.ItemGUID] = auctionItem
    auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
    if familyID:
        auctionItem.AuctionType = AuctionType_Family
        familyItemList = auctionItemMgr.familyAuctionItemDict.get(familyID, [])
        familyItemList.append(auctionItem)
        auctionItemMgr.familyAuctionItemDict[familyID] = familyItemList
    else:
        auctionItem.AuctionType = AuctionType_World
        auctionItemMgr.worldAuctionItemList.append(auctionItem)
        # 添加进我的拍卖
        if playerID:
            myAuctionItemList = auctionItemMgr.myAuctionItemDict.get(playerID, [])
            myAuctionItemList.append(auctionItem)
            auctionItemMgr.myAuctionItemDict[playerID] = myAuctionItemList
            if curPlayer:
                Sync_PlayerAuctionItemInfo(curPlayer, auctionItem)
        # 添加进我的关注
        attentionMgr = PyDataManager.GetAuctionAttentionManager()
        for attentionPlayerID, attentionList in auctionItemMgr.myAttentionItemDict.items():
            if itemID not in GetPlayerAuctionAttention(attentionMgr, attentionPlayerID):
                continue
            if auctionItem not in attentionList:
                attentionList.append(auctionItem)
        if ipyData.GetNeedWorldNotify():
            PlayerControl.WorldNotify(0, "Paimai4", [itemID, ipyData.GetNoticeSaleMinutes()])
    drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
    DR_AuctionHouse(curPlayer, "AddAuctionItem", drDict)
    GameWorld.DebugLog("上架拍品: playerID=%s,familyID=%s,%s" % (playerID, familyID, drDict), playerID)
    GameWorld.DebugLog("更新拍品数: %s" % len(auctionItemMgr.allAuctionItemDict))
    return auctionItem
def __NotifyAuctionPlayerAddItem(notifyWorldAddItemList):
    ## 通知关注物品的玩家新上架物品了
    if not notifyWorldAddItemList:
        return
    attentionMgr = PyDataManager.GetAuctionAttentionManager()
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetActivePlayerCount()):
        player = playerManager.GetActivePlayerAt(i)
        if player == None:
            continue
        if PlayerControl.GetIsTJG(player):
            continue
        playerAttentionIDList = GetPlayerAuctionAttention(attentionMgr, player.GetPlayerID())
        if not playerAttentionIDList:
            continue
        infoPack = None
        for itemGUID, itemID, playerID in notifyWorldAddItemList:
            if playerID == player.GetPlayerID():
                # 自己上架的物品不通知
                continue
            if itemID not in playerAttentionIDList:
                continue
            if not infoPack:
                infoPack = ChPyNetSendPack.tagGCAddAuctionItemInfo()
            itemInfo = ChPyNetSendPack.tagGCAddAuctionItem()
            itemInfo.ItemGUID = itemGUID
            itemInfo.ItemID = itemID
            infoPack.AddAuctionItemList.append(itemInfo)
        if infoPack:
            infoPack.AddCount = len(infoPack.AddAuctionItemList)
            NetPackCommon.SendFakePack(player, infoPack)
    return
def __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, isOnlyCheck):
    ''' 玩家竞价物品
    @param curPlayer: 竞价的玩家
    @param itemGUID: 拍品GUID
    @param biddingPrice: 竞价
    @param isOnlyCheck: 是否仅检查可否竞价
    '''
    errInfo = ""
    itemID = 0
    auctionItem = GetAuctionItem(itemGUID)
    if not auctionItem:
        # 拍品不存在
        PlayerControl.NotifyCode(curPlayer, "Paimai3")
        errInfo = "not auctionItem"
        return itemID, errInfo
    itemID = auctionItem.ItemID
    ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
    if not ipyData:
        errInfo = "not ipyData"
        return itemID, errInfo
    playerID = curPlayer.GetPlayerID()
    addTimeStr = auctionItem.AddTime
    auctionType = auctionItem.AuctionType # 0-全服拍品,1-仙盟拍品
    addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
    curTime = int(time.time())
    passMinutes = (curTime - addTime) / 60
    noticeMinutes = ipyData.GetNoticeSaleMinutes()
    if passMinutes < noticeMinutes:
        GameWorld.ErrLog("拍品尚未开放竞价! itemGUID=%s,itemID=%s,addTimeStr=%s" % (itemGUID, itemID, addTimeStr), playerID)
        errInfo = "in notice time"
        return itemID, errInfo
    if auctionType == AuctionType_Family:
        itemFamilyID = auctionItem.FamilyID
        playerFamilyID = curPlayer.GetFamilyID()
        if not playerFamilyID or playerFamilyID != itemFamilyID:
            GameWorld.ErrLog("该拍品为仙盟私有拍品,非本仙盟人员不可竞拍! itemGUID=%s,itemID=%s,itemFamilyID=%s,playerFamilyID=%s"
                             % (itemGUID, itemID, itemFamilyID, playerFamilyID), playerID)
            errInfo = "is family auction item"
            return itemID, errInfo
    if curTime >= auctionItem.EndTime:
        GameWorld.ErrLog("拍品已结束竞价! itemGUID=%s,itemID=%s,addTimeStr=%s" % (itemGUID, itemID, addTimeStr), playerID)
        errInfo = "end bid"
        return itemID, errInfo
    nextPrice = ipyData.GetBasePrice() if not auctionItem.BidderPrice else (auctionItem.BidderPrice + ipyData.GetBiddingAdd())
    buyoutPrice = ipyData.GetBuyoutPrice() # 允许没有一口价的,代表可以一直竞价
    nextPrice = nextPrice if not buyoutPrice else min(nextPrice, buyoutPrice) # 不超过一口价
    if not (biddingPrice == nextPrice or (buyoutPrice and biddingPrice == buyoutPrice)):
        # 竞价价格错误
        PlayerControl.NotifyCode(curPlayer, "Paimai2")
        errInfo = "bid price error! biddingPrice=%s,nextPrice=%s,buyoutPrice=%s,itemGUID=%s,itemID=%s" % (biddingPrice, nextPrice, buyoutPrice, itemGUID, itemID)
        return itemID, errInfo
    if isOnlyCheck:
        queryTick = auctionItem.BiddingQueryTick
        if queryTick and tick - queryTick < 10000:
            # 有玩家正在竞价,请稍等
            PlayerControl.NotifyCode(curPlayer, "Paimai1")
            errInfo = "other player bidding"
            return itemID, errInfo
        auctionItem.BiddingQueryID = playerID
        auctionItem.BiddingQueryTick = tick
        return itemID, errInfo
    if auctionItem.BiddingQueryID != playerID:
        PlayerControl.NotifyCode(curPlayer, "Paimai2")
        errInfo = "bidding player error"
        return itemID, errInfo
    lastBidderID = auctionItem.BidderID
    lastBidderPrice = auctionItem.BidderPrice
    itemCount = auctionItem.Count
    isBuyout = buyoutPrice and biddingPrice >= buyoutPrice # 大于等于一口价,直接成交
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    nowBiddingAuctionItemDict = auctionItemMgr.nowBiddingAuctionItemDict
    hisBiddingAuctionItemDict = auctionItemMgr.hisBiddingAuctionItemDict
    # 邮件返还上个竞价者
    if lastBidderID and lastBidderPrice:
        detail = {"ItemID":itemID, "ItemGUID":itemGUID, "Count":itemCount, ChConfig.Def_MailMoneySource:ChConfig.Def_GiveMoney_AuctionBidReturn}
        if isBuyout:
            # 竞拍失败,仅通知
            paramList = [itemID, lastBidderPrice]
            PlayerCompensation.SendMailByKey("PaimaiMail2", [lastBidderID], [], paramList, gold=lastBidderPrice, detail=detail)
        else:
            # 竞拍失败,可继续竞价邮件
            paramList = [itemID, lastBidderPrice, itemGUID]
            PlayerCompensation.SendMailByKey("PaimaiMail1", [lastBidderID], [], paramList, gold=lastBidderPrice, detail=detail)
        AddAuctionRecord(auctionItem, AuctionRecordResult_BidFail)
        isSyncBiddingItem = False
        nowBiddingAuctionItemList = nowBiddingAuctionItemDict.get(lastBidderID, [])
        if auctionItem in nowBiddingAuctionItemList:
            nowBiddingAuctionItemList.remove(auctionItem)
            nowBiddingAuctionItemDict[lastBidderID] = nowBiddingAuctionItemList
            isSyncBiddingItem = True
        hisBiddingAuctionItemList = hisBiddingAuctionItemDict.get(lastBidderID, [])
        if auctionItem not in hisBiddingAuctionItemList:
            hisBiddingAuctionItemList.append(auctionItem)
            hisBiddingAuctionItemDict[lastBidderID] = hisBiddingAuctionItemList
            isSyncBiddingItem = True
        if isSyncBiddingItem:
            lastBidder = GameWorld.GetPlayerManager().FindPlayerByID(lastBidderID)
            if lastBidder and not PlayerControl.GetIsTJG(lastBidder):
                Sync_PlayerBiddingItemInfo(lastBidder, auctionItem)
    # 更新竞价信息
    auctionItem.BiddingQueryID = 0
    auctionItem.BiddingTick = 0
    auctionItem.BiddingTime = GameWorld.GetCurrentDataTimeStr()
    auctionItem.BidderID = playerID
    auctionItem.BidderName = curPlayer.GetName()
    auctionItem.BidderPrice = biddingPrice
    isSyncBiddingItem = False
    nowBiddingAuctionItemList = nowBiddingAuctionItemDict.get(playerID, [])
    if auctionItem not in nowBiddingAuctionItemList:
        nowBiddingAuctionItemList.append(auctionItem)
        nowBiddingAuctionItemDict[playerID] = nowBiddingAuctionItemList
        isSyncBiddingItem = True
    hisBiddingAuctionItemList = hisBiddingAuctionItemDict.get(playerID, [])
    if auctionItem in hisBiddingAuctionItemList:
        hisBiddingAuctionItemList.remove(auctionItem)
        hisBiddingAuctionItemDict[playerID] = hisBiddingAuctionItemList
        isSyncBiddingItem = True
    if isSyncBiddingItem:
        Sync_PlayerBiddingItemInfo(curPlayer, auctionItem)
    # 添加历史竞价者
    if str(playerID) not in auctionItem.BidderIDInfo:
        auctionItem.BidderIDInfo += "%s|" % playerID
        auctionItem.BidderIDLen = len(auctionItem.BidderIDInfo)
    GameWorld.DebugLog("玩家竞价拍品: itemGUID=%s,itemID=%s,isBuyout=%s,lastBidderID=%s,lastBidderPrice=%s,bidderIDInfo=%s"
                       % (itemGUID, itemID, isBuyout, lastBidderID, lastBidderPrice, auctionItem.BidderIDInfo), playerID)
    if isBuyout:
        __EndAuctionItem([auctionItem], "Buyout")
    else:
        if __AddAuctionItemEndTimeByBid(auctionItem):
            __SortAuctionitem(isSortWorldItem=False)
        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
        DR_AuctionHouse(curPlayer, "BidAuctionItem", drDict)
        __SyncRefreshAuctionItem([auctionItem])
    return itemID, errInfo
def __SyncRefreshAuctionItem(auctionItemList):
    ''' // B5 08 拍卖行刷新拍品 #tagGCRefreshAuctionItemInfo
        1-仙盟拍品转移到全服时通知; 2-拍品有人竞价时刷新
    '''
    refreshAuctionItemList = []
    for auctionItem in auctionItemList:
        refreshItem = ChPyNetSendPack.tagGCRefreshAuctionItem()
        refreshItem.ItemGUID = auctionItem.ItemGUID
        refreshItem.AuctionType = auctionItem.AuctionType
        refreshItem.AddTime = auctionItem.AddTime
        refreshItem.BidderID = auctionItem.BidderID
        refreshItem.BidderPrice = auctionItem.BidderPrice
        refreshAuctionItemList.append(refreshItem)
    if not refreshAuctionItemList:
        return
    clientPack = ChPyNetSendPack.tagGCRefreshAuctionItemInfo()
    clientPack.RefreshAuctionItemList = refreshAuctionItemList
    clientPack.RefreshCount = len(clientPack.RefreshAuctionItemList)
    playerManager = GameWorld.GetPlayerManager()
    for i in xrange(playerManager.GetActivePlayerCount()):
        player = playerManager.GetActivePlayerAt(i)
        if player == None:
            continue
        if PlayerControl.GetIsTJG(player):
            continue
        NetPackCommon.SendFakePack(player, clientPack)
    return
def __DoGMClearAuctionItem(curPlayer):
    if curPlayer.GetGMLevel() != 90:
        return
    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
    if not allAuctionItemByEndTimeList:
        GameWorld.DebugAnswer(curPlayer, "当前没有拍品!")
        return
    endItemList = []
    for auctionItem in allAuctionItemByEndTimeList:
        endItemList.append(auctionItem)
    clearCount = __EndAuctionItem(endItemList, "GMClear")
    GameWorld.DebugAnswer(curPlayer, "清空拍品数=%s" % clearCount)
    return
def __DoGMPrintAuctionItem(curPlayer):
    if curPlayer.GetGMLevel() != 90:
        return
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    allAuctionItemByEndTimeList = auctionItemMgr.allAuctionItemByEndTimeList
    GameWorld.DebugLog("TimeList总拍品数: =%s" % len(allAuctionItemByEndTimeList))
    for i, auctionItem in enumerate(allAuctionItemByEndTimeList):
        GameWorld.DebugLog("    i=%s, %s" % (i, __GetAuctionItemDRDict(auctionItem)))
    GameWorld.DebugLog("AllDict总拍品数: =%s" % len(auctionItemMgr.allAuctionItemDict))
    GameWorld.DebugLog("全服拍品个数: =%s" % len(auctionItemMgr.worldAuctionItemList))
    for familyID, familyItemList in auctionItemMgr.familyAuctionItemDict.items():
        GameWorld.DebugLog("仙盟拍品个数: familyID=%s, %s" % (familyID, len(familyItemList)))
    for playerID, myItemList in auctionItemMgr.myAuctionItemDict.items():
        GameWorld.DebugLog("玩家拍品个数: playerID=%s, %s" % (playerID, len(myItemList)))
    for playerID, nowBiddingItemList in auctionItemMgr.nowBiddingAuctionItemDict.items():
        GameWorld.DebugLog("玩家最高竞价拍品个数: playerID=%s, %s" % (playerID, len(nowBiddingItemList)))
    for playerID, hisBiddingItemList in auctionItemMgr.hisBiddingAuctionItemDict.items():
        GameWorld.DebugLog("玩家历史竞价拍品个数: playerID=%s, %s" % (playerID, len(hisBiddingItemList)))
    for playerID, attentionItemList in auctionItemMgr.myAttentionItemDict.items():
        GameWorld.DebugLog("玩家关注拍品个数: playerID=%s, %s" % (playerID, len(attentionItemList)))
    return
#// B5 10 拍卖行查询拍卖中的物品 #tagCGQueryAuctionItem
#
#struct    tagCGQueryAuctionItem
#{
#    tagHead    Head;
#    BYTE    Job;    //过滤职业,0为不限制
#    BYTE    ItemTypeCount;
#    DWORD    ItemTypeList[ItemTypeCount];    //指定的物品类型
#    BYTE    ClassLV;    //过滤阶数,0为不限制
#    BYTE    SpecItemIDCount;    //指定物品ID个数
#    DWORD    SpecItemIDList[SpecItemIDCount];    //指定物品ID
#    char    FromItemGUID[40];        //从哪个物品开始查询
#    BYTE    QueryDir;        //查询方向,1-往后查,2-往前查
#    BYTE    QueryCount;    //查询个数,0为全部
#};
def OnQueryAuctionItem(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    job = clientData.Job
    itemTypeList = clientData.ItemTypeList
    classLV = clientData.ClassLV
    specItemIDList = clientData.SpecItemIDList
    fromItemGUID = clientData.FromItemGUID
    queryDir = clientData.QueryDir
    queryCount = clientData.QueryCount
    __Sync_WorldAuctionItemQueryResult(curPlayer, job, itemTypeList, classLV, specItemIDList, fromItemGUID, queryDir, queryCount)
    return
#// B5 17 拍卖行查询定位目标拍品 #tagCGQueryTagAuctionItem
#
#struct    tagCGQueryTagAuctionItem
#{
#    tagHead        Head;
#    char        ItemGUID[40];
#    DWORD        ItemID;
#};
def OnQueryTagAuctionItem(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    tagItemGUID = clientData.ItemGUID
    queryDir = 3
    __Sync_WorldAuctionItemQueryResult(curPlayer, fromItemGUID=tagItemGUID, queryDir=queryDir)
    return
def __Sync_WorldAuctionItemQueryResult(curPlayer, job=0, itemTypeList=[], classLV=0, specItemIDList=[], fromItemGUID="", queryDir=1, queryCount=10):
    ## 根据过滤条件同步全服拍品列表,目前仅全服拍品需要通过查询服务器获得,个人拍品及仙盟拍品由于个数较少直接由上线或变更时主动同步
    fromAuctionItem = None
    if fromItemGUID:
        fromAuctionItem = GetAuctionItem(fromItemGUID)
        if not fromAuctionItem:
            GameWorld.DebugLog("查询的目标拍品不存在! fromItemGUID=%s" % fromItemGUID)
            PlayerControl.NotifyCode(curPlayer, "Paimai5")
            return
    # {(job, (itemType, ...), itemClassLV, (itemID, ...)):[tagDBAuctionItem, ...], ...}
    queryKey = (job, tuple(itemTypeList), classLV, tuple(specItemIDList))
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    worldAuctionItemQueryDict = auctionItemMgr.worldAuctionItemQueryDict
    if queryKey not in worldAuctionItemQueryDict:
        # 载入对应查询条件拍品缓存
        auctionItemQueryList = []
        for worldAuctionItem in auctionItemMgr.worldAuctionItemList:
            if job and worldAuctionItem.ItemJobLimit != job:
                continue
            if itemTypeList and worldAuctionItem.ItemType not in itemTypeList:
                continue
            if classLV and worldAuctionItem.ItemClassLV != classLV:
                continue
            if specItemIDList and worldAuctionItem.ItemID not in specItemIDList:
                continue
            auctionItemQueryList.append(worldAuctionItem)
        worldAuctionItemQueryDict[queryKey] = auctionItemQueryList
    else:
        auctionItemQueryList = worldAuctionItemQueryDict[queryKey]
    queryTotalCount = len(auctionItemQueryList)
    fromIndex = 0
    if fromAuctionItem:
        if fromAuctionItem not in auctionItemQueryList:
            GameWorld.ErrLog("查询的目标拍品不在所在的过滤的条件里! fromItemGUID=%s" % fromItemGUID)
            PlayerControl.NotifyCode(curPlayer, "Paimai5")
            return
        fromIndex = auctionItemQueryList.index(fromAuctionItem)
    # 向前查,其他的默认向后查
    if queryDir == 2:
        startIndex = max(0, fromIndex - queryCount + 1)
        syncItemList = auctionItemQueryList[startIndex:fromIndex + 1]
        queryRemainlCount = startIndex
    # 向后查
    else:
        syncItemList = auctionItemQueryList[fromIndex:fromIndex + queryCount]
        queryRemainlCount = max(0, queryTotalCount - fromIndex - queryCount)
    clientPack = ChPyNetSendPack.tagGCAuctionItemInfo()
    clientPack.Job = job
    clientPack.ItemTypeList = itemTypeList
    clientPack.ItemTypeCount = len(clientPack.ItemTypeList)
    clientPack.ClassLV = classLV
    clientPack.SpecItemIDList = specItemIDList
    clientPack.SpecItemIDCount = len(clientPack.SpecItemIDList)
    clientPack.FromItemGUID = fromItemGUID
    clientPack.QueryDir = queryDir
    clientPack.QueryCount = queryCount
    clientPack.QueryRemainlCount = queryRemainlCount
    clientPack.AuctionItemList = []
    for auctionItem in syncItemList:
        itemInfo = ChPyNetSendPack.tagGCAuctionItem()
        itemInfo.ItemGUID = auctionItem.ItemGUID
        itemInfo.FamilyID = auctionItem.FamilyID
        itemInfo.ItemID = auctionItem.ItemID
        itemInfo.ItemCount = auctionItem.Count
        itemInfo.AddTime = auctionItem.AddTime
        itemInfo.BidderPrice = auctionItem.BidderPrice
        itemInfo.UserData = auctionItem.UserData
        itemInfo.UserDataLen = auctionItem.UserDataLen
        clientPack.AuctionItemList.append(itemInfo)
    clientPack.AuctionItemCount = len(clientPack.AuctionItemList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
#// B5 12 拍卖行查询拍卖记录 #tagCGQueryAuctionRecord
#
#struct    tagCGQueryAuctionRecord
#{
#    tagHead    Head;
#    BYTE        RecordType;    //记录类型 0-我的拍品记录 1-仙盟拍品记录 2-我的竞拍记录
#};
def OnQueryAuctionRecord(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    recordType = clientData.RecordType
    Sync_PlayerAuctionRecordInfo(curPlayer, recordType, None, curPlayer.GetFamilyID())
    return
#// B5 16 拍卖行查询关注中的拍品 #tagCGQueryAttentionAuctionItem
#
#struct    tagCGQueryAttentionAuctionItem
#{
#    tagHead    Head;
#};
def OnQueryAttentionAuctionItem(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    myAttentionItemDict = auctionItemMgr.myAttentionItemDict
    if playerID in myAttentionItemDict:
        attentionItemList = myAttentionItemDict[playerID]
    else:
        attentionItemList = []
        attentionMgr = PyDataManager.GetAuctionAttentionManager()
        attentionItemIDList = GetPlayerAuctionAttention(attentionMgr, playerID)
        if attentionItemIDList:
            for auctionItem in auctionItemMgr.worldAuctionItemList:
                if auctionItem.ItemID not in attentionItemIDList:
                    continue
                attentionItemList.append(auctionItem)
            myAttentionItemDict[playerID] = attentionItemList
    clientPack = ChPyNetSendPack.tagGCAttentionAuctionItemInfo()
    clientPack.AuctionItemList = []
    for attentionItem in attentionItemList:
        itemObj = ChPyNetSendPack.tagGCAttentionAuctionItem()
        itemObj.ItemGUID = attentionItem.ItemGUID
        itemObj.FamilyID = attentionItem.FamilyID
        itemObj.ItemID = attentionItem.ItemID
        itemObj.ItemCount = attentionItem.Count
        itemObj.AddTime = attentionItem.AddTime
        itemObj.BidderPrice = attentionItem.BidderPrice
        itemObj.UserData = attentionItem.UserData
        itemObj.UserDataLen = attentionItem.UserDataLen
        clientPack.AuctionItemList.append(itemObj)
    clientPack.AuctionItemCount = len(clientPack.AuctionItemList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
#// B5 18 拍卖行修改关注物品 #tagCGAttentionAuctionItemChange
#
#struct    tagCGAttentionAuctionItemChange
#{
#    tagHead        Head;
#    DWORD        ItemID;
#    BYTE        IsAttention;    //是否关注,取消关注发0
#};
def OnAttentionAuctionItemChange(index, clientData, tick):
    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
    playerID = curPlayer.GetPlayerID()
    itemID = clientData.ItemID
    isAttention = clientData.IsAttention
    attentionMgr = PyDataManager.GetAuctionAttentionManager()
    if isAttention:
        if not AddPlayerAuctionAttention(attentionMgr, playerID, itemID):
            return
    else:
        if not DelPlayerAuctionAttention(attentionMgr, playerID, itemID):
            return
    Sync_PlayerAttentionAuctionItemID(curPlayer, True)
    # 关注信息变更,清空关注拍品实例缓存
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    auctionItemMgr.myAttentionItemDict.pop(playerID, None)
    return
def Sync_FamilyAuctionItemInfo(curPlayer, familyID, itemInfoList=[]):
    # // B5 05 拍卖行仙盟拍卖中的物品信息 #tagGCFamilyAuctionItemInfo
    if not familyID:
        return
    if not itemInfoList:
        itemInfoList = GetFamilyAuctionItemList(familyID)
    if not itemInfoList:
        return
    itemInfoPack = ChPyNetSendPack.tagGCFamilyAuctionItemInfo()
    itemInfoPack.AuctionItemList = []
    for auctionItem in itemInfoList:
        packItem = ChPyNetSendPack.tagGCFamilyAuctionItem()
        packItem.ItemGUID = auctionItem.ItemGUID
        packItem.FamilyID = auctionItem.FamilyID
        packItem.ItemID = auctionItem.ItemID
        packItem.ItemCount = auctionItem.Count
        packItem.AddTime = auctionItem.AddTime
        packItem.BidderPrice = auctionItem.BidderPrice
        packItem.UserData = auctionItem.UserData
        packItem.UserDataLen = auctionItem.UserDataLen
        packItem.FamilyPlayerIDInfo = auctionItem.FamilyPlayerIDInfo
        packItem.FamilyPlayerIDLen = auctionItem.FamilyPlayerIDLen
        packItem.AuctionType = auctionItem.AuctionType
        itemInfoPack.AuctionItemList.append(packItem)
    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
    if curPlayer:
        NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
    else:
        PlayerFamily.SendFamilyFakePack(familyID, itemInfoPack)
    return
def Sync_PlayerAuctionItemInfo(curPlayer, auctionItem=None):
    # // B5 02 拍卖行玩家拍卖中的物品信息 #tagGCPlayerAuctionItemInfo
    if auctionItem:
        syncItemList = [auctionItem]
    else:
        syncItemList = GetPlayerAuctionItemList(curPlayer.GetPlayerID())
    if not syncItemList:
        return
    itemInfoPack = ChPyNetSendPack.tagGCPlayerAuctionItemInfo()
    itemInfoPack.AuctionItemList = []
    for auctionItem in syncItemList:
        packItem = ChPyNetSendPack.tagGCPlayerAuctionItem()
        packItem.ItemGUID = auctionItem.ItemGUID
        packItem.FamilyID = auctionItem.FamilyID
        packItem.ItemID = auctionItem.ItemID
        packItem.ItemCount = auctionItem.Count
        packItem.AddTime = auctionItem.AddTime
        packItem.BidderPrice = auctionItem.BidderPrice
        packItem.UserData = auctionItem.UserData
        packItem.UserDataLen = auctionItem.UserDataLen
        itemInfoPack.AuctionItemList.append(packItem)
    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
    NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
    return
def Sync_PlayerAuctionRecordInfo(curPlayer, recordType, newRecordData=None, familyID=0):
    ## B5 03 拍卖行玩家拍卖记录 #tagGCPlayerAuctionRecordInfo
    if recordType == AuctionRecordType_MyAuction and curPlayer:
        syncRecordList = [newRecordData] if newRecordData else GetMyAuctionItemRecord(curPlayer.GetPlayerID())
    elif recordType == AuctionRecordType_FamilyAuction and familyID:
        syncRecordList = [newRecordData] if newRecordData else GetFamilyAuctionItemRecord(familyID)
    elif recordType == AuctionRecordType_MyBid and curPlayer:
        syncRecordList = [newRecordData] if newRecordData else GetMyBiddingItemRecord(curPlayer.GetPlayerID())
    else:
        return
    clientPack = ChPyNetSendPack.tagGCPlayerAuctionRecordInfo()
    clientPack.AuctionRecordList = []
    for recordData in syncRecordList:
        record = ChPyNetSendPack.tagGCPlayerAuctionRecord()
        record.ItemGUID = recordData.ItemGUID
        record.FamilyID = recordData.FamilyID
        record.RecordType = recordData.RecordType
        record.RecordResult = recordData.RecordResult
        record.RecordTime = recordData.RecordTime
        record.BidderPrice = recordData.BidderPrice
        record.BidderName = recordData.BidderName
        record.ItemID = recordData.ItemID
        record.ItemCount = recordData.Count
        record.UserData = recordData.UserData
        record.UserDataLen = recordData.UserDataLen
        clientPack.AuctionRecordList.append(record)
    clientPack.Count = len(clientPack.AuctionRecordList)
    if curPlayer:
        NetPackCommon.SendFakePack(curPlayer, clientPack)
    elif familyID and recordType == AuctionRecordType_FamilyAuction:
        PlayerFamily.SendFamilyFakePack(familyID, clientPack)
    return
def Sync_PlayerBiddingItemInfo(curPlayer, auctionItem=None):
    #// B5 10 拍卖行玩家竞价中的物品信息 #tagGCBiddingItemInfo
    # 上线同步、玩家相关的竞价拍品竞价变更时同步
    if auctionItem:
        syncItemList = [auctionItem]
    else:
        playerID = curPlayer.GetPlayerID()
        auctionItemMgr = PyDataManager.GetAuctionItemManager()
        nowBiddingAuctionItemList = auctionItemMgr.nowBiddingAuctionItemDict.get(playerID, [])
        hisBiddingAuctionItemList = auctionItemMgr.hisBiddingAuctionItemDict.get(playerID, [])
        syncItemList = nowBiddingAuctionItemList + hisBiddingAuctionItemList
    if not syncItemList:
        return
    itemInfoPack = ChPyNetSendPack.tagGCBiddingItemInfo()
    itemInfoPack.AuctionItemList = []
    for auctionItem in syncItemList:
        packItem = ChPyNetSendPack.tagGCBiddingItem()
        packItem.ItemGUID = auctionItem.ItemGUID
        packItem.FamilyID = auctionItem.FamilyID
        packItem.ItemID = auctionItem.ItemID
        packItem.ItemCount = auctionItem.Count
        packItem.AddTime = auctionItem.AddTime
        packItem.BidderID = auctionItem.BidderID
        packItem.BidderPrice = auctionItem.BidderPrice
        packItem.UserData = auctionItem.UserData
        packItem.UserDataLen = auctionItem.UserDataLen
        itemInfoPack.AuctionItemList.append(packItem)
    itemInfoPack.AuctionItemCount = len(itemInfoPack.AuctionItemList)
    NetPackCommon.SendFakePack(curPlayer, itemInfoPack)
    return
def Sync_PlayerAttentionAuctionItemID(curPlayer, isForce=False):
    # // B5 07 拍卖行关注的物品ID #tagGCAttentionAuctionItemID
    attentionMgr = PyDataManager.GetAuctionAttentionManager()
    attentionItemIDList = GetPlayerAuctionAttention(attentionMgr, curPlayer.GetPlayerID())
    if not attentionItemIDList and not isForce:
        return
    clientPack = ChPyNetSendPack.tagGCAttentionAuctionItemID()
    clientPack.AttentionItemIDList = attentionItemIDList
    clientPack.AttentionCount = len(clientPack.AttentionItemIDList)
    NetPackCommon.SendFakePack(curPlayer, clientPack)
    return
def DR_AuctionHouse(curPlayer, eventName, drDict):
    accID = "" if not curPlayer else curPlayer.GetAccID()
    playerID = 0 if not curPlayer else curPlayer.GetPlayerID()
    dataDict = {"EventName":eventName, "PlayerID":playerID, "AccID":accID}
    dataDict.update(drDict)
    DataRecordPack.SendEventPack("AuctionHouse", dataDict, curPlayer)
    return