hxp
2025-06-30 388823edfe6308cba6f76ca6dc4f20022c5cb2be
ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
@@ -6,91 +6,2091 @@
#
# @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 IPY_PlayerDefine
import ChPyNetSendPack
import PlayerDBGSEvent
import IpyGameDataPY
import NetPackCommon
import PlayerBourse
import PlayerFamily
import ShareDefine
import PyGameData
import ChConfig
import random
import datetime
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, # 仙盟拍品转移到全服拍品
AuctionRecordResult_Unsell, # 下架
) = range(7)
# 当前拍品归类 0-全服拍品 1-仙盟私有拍品
AuctionType_World = 0
AuctionType_Family = 1
'''
竞价流程:
1. MapServer 先验证价格是否够
2. GameServer 验证是否可以竞价
3. MapServer 扣除玩家货币
4. GameServer  进行竞价,变更下次竞价价格
基于以上流程,所以玩家竞价时需先锁定物品,防止竞价流程未完结时其他玩家请求竞价同一拍品判断竞价价格错误
锁定时长同样适用于拍品竞价时间结束时的保护时间
'''
BiddingQueryLockTick = 10000
#拍卖行状态开关
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 attentionData.AttentionInfo.startswith("[") and attentionData.AttentionInfo.endswith("]"):
        attentionData.AttentionItemIDList = json.loads(attentionData.AttentionInfo)
    return
##-------------------------------------------------------------------------------------------------
def OnGameServerInitOK():
    ## 服务器启动成功
    
    def __init__(self):
    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
    # 由于服务器未启动成功时取不到正确的开服天,所以启动成功后刷新一下拍品系统回购时间
    for auctionItem in allAuctionItemByEndTimeList:
        __SetAuctionItemSysBuyTime(auctionItem)
    __SortAuctionitem()
    return
def OnLoadAuctionItemDataEx(dbData):
    ## 加载拍卖物品表时附加处理
    __InitAuctionItemAttrEx(dbData)
    playerID = dbData.PlayerID
    familyID = dbData.FamilyID
    bidderID = dbData.BidderID
    pyAuctionItemMgr = PyDataManager.GetAuctionItemManager()
    pyAuctionItemMgr.allAuctionItemByEndTimeList.append(dbData)
    if dbData.AuctionType == AuctionType_World:
        pyAuctionItemMgr.worldAuctionItemList.append(dbData)
        __OnCalcWorldAuctionItemCount(dbData, 1)
    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值
    setattr(auctionItem, "SysBuyTime", 0) # 系统一口价时间
    __SetAuctionItemSysBuyTime(auctionItem)
    __SetAuctionItemEndTime(auctionItem, ipyData)
    return True
def __SetAuctionItemSysBuyTime(auctionItem):
    ## 更新系统一口价该拍品时间
    if not GameWorld.GetGameWorld().GetDictByKey(ChConfig.Def_WorldKey_GameWorldInitOK):
        #GameWorld.DebugLog("服务器未启动好,取不到正确的开服天,不处理拍品系统回购时间!")
        return
    #itemGUID = auctionItem.ItemGUID
    itemID = auctionItem.ItemID
    itemType = auctionItem.ItemType
    playerID = auctionItem.PlayerID
    familyID = auctionItem.FamilyID
    if not playerID and not familyID:
        #GameWorld.DebugLog("该拍品为系统上架的拍品,不设置系统一口价时间! GUID=%s,itemID=%s" % (itemGUID, itemID))
        return
    
    ## ===========================================================================================
    if familyID:
        #GameWorld.DebugLog("该拍品为仙盟拍品,不设置系统一口价时间! GUID=%s,itemID=%s,familyID=%s" % (itemGUID, itemID, familyID))
        return
    
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
    # 注意: 因为GameServer没传是否套装,所以暂时按策划的ID规则来处理,最后一位代表是否套装
    if itemID % 10 != 0:
        #GameWorld.DebugLog("该拍品为套装拍品,不设置系统一口价时间! itemID=%s" % (itemID))
        return
    if auctionItem.BidderID:
        #GameWorld.DebugLog("该拍品已经有人竞价,不设置系统一口价时间! itemID=%s,bidderID=%s" % (itemID, auctionItem.BidderID))
        return
    classLV = auctionItem.ItemClassLV
    classSysBuyRateDict = IpyGameDataPY.GetFuncEvalCfg("AuctionItemSystem", 5)
    if classLV in classSysBuyRateDict:
        sysBuyRate = classSysBuyRateDict[classLV]
        if not GameWorld.CanHappen(sysBuyRate, 100):
            #GameWorld.DebugLog("该拍品阶概率不回收,不设置系统一口价时间! itemID=%s,classLV=%s,sysBuyRate=%s" % (itemID, classLV, sysBuyRate))
            return
        
        GameWorld.Log("Save AuctionItem count :%s" % cnt)
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    canSysBuyItemTypeList = IpyGameDataPY.GetFuncEvalCfg("AuctionItemSystem", 2)
    if itemType not in canSysBuyItemTypeList:
        #GameWorld.DebugLog("该拍品类型不可被系统一口价,不设置系统一口价时间! itemID=%s,itemType=%s" % (itemID, itemType))
        return
    
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load AuctionItem count :%s" % cnt)
        return pos
    sysBuyTimeRange = {}
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1
    oscDaySysBuyTimeRangeDict = IpyGameDataPY.GetFuncEvalCfg("AuctionItemSystem", 1, {})
    openServerDayList = oscDaySysBuyTimeRangeDict.keys()
    openServerDayList.sort()
    for oscDay in openServerDayList:
        if openServerDay <= oscDay:
            sysBuyTimeRange = oscDaySysBuyTimeRangeDict[oscDay]
            break
    if len(sysBuyTimeRange) != 2:
        #GameWorld.DebugLog("该开服天没有配置系统中间商回购支持! openServerDay=%s, %s" % (openServerDay, oscDaySysBuyTimeRangeDict))
        return
    randMinutes = random.randint(sysBuyTimeRange[0], sysBuyTimeRange[1])
    addTimeStr = auctionItem.AddTime
    addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
    auctionItem.SysBuyTime = addTime + randMinutes * 60
    pyAuctionItemMgr = PyDataManager.GetAuctionItemManager()
    pyAuctionItemMgr.sysBuyoutItemByTimeList.append(auctionItem)
    #GameWorld.DebugLog("更新拍品系统一口价时间: GUID=%s,itemID=%s,addTime=%s(%s),openServerDay=%s,randMinutes=%s(%s),sysBuyTime=%s"
    #                   % (itemGUID, itemID, addTime, addTimeStr, openServerDay, randMinutes, sysBuyTimeRange, auctionItem.SysBuyTime))
    return
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
    GameWorld.DebugLog("拍品加时: EndTime=%s,updEndTime=%s" % (auctionItem.EndTime, endTime))
    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 IsFamilyMemberBiddingAuctionItem(familyID, memberID):
    ## 仙盟成员是否最高竞价拍品中
    familyAuctionItemList = GetFamilyAuctionItemList(familyID)
    for auctionItem in familyAuctionItemList:
        if auctionItem.BidderID == memberID:
            return True
    return False
def __OnCalcWorldAuctionItemCount(auctionItem, changeCount):
    ## 世界拍品数量变更统计处理
    if auctionItem.AuctionType != AuctionType_World:
        return
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    itemID = auctionItem.ItemID
    #GameWorld.DebugLog("世界拍品数量变更统计: itemID=%s,changeCount=%s" % (itemID, changeCount))
    itemIDStr = str(itemID)
    classLV = auctionItem.ItemClassLV
    jobLimit = auctionItem.ItemJobLimit
    # 统计有职业限制的境界装备
    if jobLimit and classLV and len(itemIDStr) == 7:
        color = int(itemIDStr[3:4])
        isSuit = int(itemIDStr[-1])
        jobEquipKey = (jobLimit, classLV, color, isSuit) # 职业,阶,颜色,是否套装
        befCount = auctionItemMgr.worldAuctionJobEquipCountDict.get(jobEquipKey, 0)
        updCount = max(befCount + changeCount, 0)
        auctionItemMgr.worldAuctionJobEquipCountDict[jobEquipKey] = updCount
        #GameWorld.DebugLog("    职业境界装备数量变更: jobLimit=%s,classLV=%s,color=%s,isSuit=%s,befCount=%s,updCount=%s"
        #                   % (jobLimit, classLV, color, isSuit, befCount, updCount))
    # 其他的直接用itemID统计
    else:
        befCount = auctionItemMgr.worldAuctionItemCountDict.get(itemID, 0)
        updCount = max(befCount + changeCount, 0)
        auctionItemMgr.worldAuctionItemCountDict[itemID] = updCount
        #GameWorld.DebugLog("    物品ID数量变更: itemID=%s,befCount=%s,updCount=%s" % (itemID, befCount, updCount))
    return
def __GetAuctionSystemItemInfo():
    key = "AuctionSystemItem"
    openServerDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1
    AuctionSystemItem = IpyGameDataPY.GetConfigEx(key)
    reloadSign = openServerDay
    if AuctionSystemItem and AuctionSystemItem[0] == reloadSign:
        GameWorld.DebugLog("已经加载过本日系统拍品处理信息!openServerDay=%s" % openServerDay)
        return AuctionSystemItem[1]
    # 因为后面的时间判断都是精确到分的,而处理此逻辑的时候可能不是0秒,所以这里的datetime取当前时间精确到分的
    serverTime = GameWorld.GetServerTime()
    curDateStr = "%d-%d-%d" % (serverTime.year, serverTime.month, serverTime.day)
    curDateTimeStr = "%d-%d-%d %02d:%02d:00" % (serverTime.year, serverTime.month, serverTime.day, serverTime.hour, serverTime.minute)
    curDateTime = datetime.datetime.strptime(curDateTimeStr, ChConfig.TYPE_Time_Format)
    GameWorld.Log("===== 加载本日系统拍品信息: %s,openServerDay=%s =====" % (curDateTime, openServerDay))
    addSystemAuctionItemInfo = []
    ipyDataMgr = IpyGameDataPY.IPY_Data()
    for index in xrange(ipyDataMgr.GetAuctionSystemItemCount()):
        ipyData = ipyDataMgr.GetAuctionSystemItemByIndex(index)
        cfgID = ipyData.GetCfgID()
        startDateStr = ipyData.GetStartDate()
        endDateStr = ipyData.GetEndDate()
        startTimeStr = ipyData.GetStartTime()
        endTimeStr = ipyData.GetEndTime()
        auctionCount = ipyData.GetAuctionCount()
        randMinuteRange = ipyData.GetRandMinuteRange()
        replenishAuctionCount = ipyData.GetReplenishAuctionCount()
        GameWorld.DebugLog("cfgID=%s,startDateStr=%s,endDateStr=%s,startTimeStr=%s,endTimeStr=%s,auctionCount=%s,randMinuteRange=%s,replenishAuctionCount=%s"
                           % (cfgID, startDateStr, endDateStr, startTimeStr, endTimeStr, auctionCount, randMinuteRange, replenishAuctionCount))
        if not startDateStr:
            startDateStr = curDateStr
        elif startDateStr.isdigit():
            startServerDay = int(startDateStr)
            openServerDateTime = curDateTime + datetime.timedelta(days=(startServerDay-openServerDay))
            startDateStr = "%d-%d-%d" % (openServerDateTime.year, openServerDateTime.month, openServerDateTime.day)
        if not endDateStr:
            endDateStr = curDateStr
        elif endDateStr.isdigit():
            endServerDay = int(endDateStr)
            endServerDateTime = curDateTime + datetime.timedelta(days=(endServerDay-openServerDay))
            endDateStr = "%d-%d-%d" % (endServerDateTime.year, endServerDateTime.month, endServerDateTime.day)
        startDate = datetime.datetime.strptime("%s 00:00:00" % (startDateStr), ChConfig.TYPE_Time_Format)
        endDate = datetime.datetime.strptime("%s 23:59:00" % (endDateStr), ChConfig.TYPE_Time_Format)
        if serverTime < startDate or serverTime > endDate:
            GameWorld.DebugLog("    不在上架日期内,不处理!")
            continue
        if not startTimeStr:
            startTimeStr = "00:00"
        if not endTimeStr:
            endTimeStr = "23:59"
        startDatetime = datetime.datetime.strptime("%s %s:00" % (curDateStr, startTimeStr), ChConfig.TYPE_Time_Format)
        endDatetime = datetime.datetime.strptime("%s %s:00" % (curDateStr, endTimeStr), ChConfig.TYPE_Time_Format)
        if serverTime >= endDatetime:
            GameWorld.DebugLog("    已超过今日的上架时间段,不处理!")
            continue
        if serverTime > startDatetime:
            startDatetime = curDateTime
        addAuctionItemDatetimeList = []
        nextAddAuctionItemDatetime = startDatetime
        for _ in xrange(auctionCount):
            if len(randMinuteRange) == 2:
                nextAddMinutes = random.randint(randMinuteRange[0], randMinuteRange[1])
            elif len(randMinuteRange) == 1:
                nextAddMinutes = randMinuteRange[0]
            else:
                continue
            nextAddAuctionItemDatetime += datetime.timedelta(minutes=nextAddMinutes)
            if nextAddAuctionItemDatetime > endDatetime:
                break
            GameWorld.DebugLog("    添加上架系统拍品时间: nextAddMinutes=%s %s" % (nextAddMinutes, nextAddAuctionItemDatetime))
            addAuctionItemDatetimeList.append(nextAddAuctionItemDatetime)
        # 动态补充拍品模式
        if replenishAuctionCount:
            GameWorld.DebugLog("    添加动态补充系统拍品计划: %s" % replenishAuctionCount)
        # 指定上架拍品模式
        elif addAuctionItemDatetimeList:
            GameWorld.DebugLog("    添加上架系统拍品时间计划: %s" % addAuctionItemDatetimeList)
        else:
            continue
        addSystemAuctionItemInfo.append([cfgID, ipyData, startDatetime, endDatetime, addAuctionItemDatetimeList])
    AuctionSystemItem = IpyGameDataPY.SetConfigEx(key, [reloadSign, addSystemAuctionItemInfo])
    GameWorld.Log("本日系统拍品信息加载完毕!reloadSign=%s" % (reloadSign))
    GameWorld.Log("本日系统拍品上架时间信息: %s" % addSystemAuctionItemInfo)
    GameWorld.Log("=============================================================")
    return AuctionSystemItem[1]
def OnAuctionItemMinuteProcess(tick):
    ## 拍卖行拍品定时处理,每整分钟触发一次
    # 这里时间需精确到分钟,不然后面的比较会匹配不到
    curDateTime = GameWorld.GetServerTime()
    curDateTime = datetime.datetime.strptime("%d-%d-%d %d:%d:00" % (curDateTime.year, curDateTime.month, curDateTime.day,
                                                                    curDateTime.hour, curDateTime.minute), ChConfig.TYPE_Time_Format)
    randMailKey = ""
    sysAuctionItemListDict = IpyGameDataPY.GetConfigEx("SysWaitAuctionItemListDict") # 系统等待上架的拍品列表字典 {cfgID:[待上架拍品列表], ...}
    if not sysAuctionItemListDict:
        sysAuctionItemListDict = {}
    curTime = int(time.time())
    addItemTime = curTime
    openJobList = IpyGameDataPY.GetFuncEvalCfg("OpenJob", 1)
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    curWorldLV = PlayerDBGSEvent.GetDBGSTrig_ByKey(ShareDefine.Def_Notify_WorldKey_WorldAverageLv)
    addSystemAuctionItemInfo = __GetAuctionSystemItemInfo()
    for cfgID, ipyData, startDatetime, endDatetime, addAuctionItemDatetimeList in addSystemAuctionItemInfo:
        #cfgID = ipyData.GetCfgID()
        if cfgID in sysAuctionItemListDict:
            #GameWorld.DebugLog("    队列中还有未处理的拍品时不处理,防止重复添加!cfgID=%s" % (cfgID))
            continue
        worldLVRange = ipyData.GetWorldLVRange()
        if worldLVRange and len(worldLVRange) == 2:
            worldLVMin, worldLVMax = worldLVRange
            if curWorldLV < worldLVMin or curWorldLV > worldLVMax:
                #GameWorld.DebugLog("    不满足当前世界等级范围条件,不处理该系统上架拍品计划!cfgID=%s,curWorldLV=%s" % (cfgID, curWorldLV))
                continue
        if curDateTime < startDatetime or curDateTime > endDatetime:
            #GameWorld.DebugLog("    不在规定的时间内,不处理该系统上架拍品计划!cfgID=%s" % (cfgID))
            continue
        randSecondRange = ipyData.GetAddRandSecondRange()
        if len(randSecondRange) != 2:
            #GameWorld.DebugLog("    随机上架秒数格式错误,不处理该系统上架拍品计划!cfgID=%s" % (cfgID))
            continue
        addItemInfoList = []
        replenishAuctionCount = ipyData.GetReplenishAuctionCount()
        # 动态模式
        if replenishAuctionCount:
            replenishCDSeconds = ipyData.GetReplenishCDMinutes() * 60
            lastReplenishTime = auctionItemMgr.worldAuctionReplenishTimeDict.get(cfgID, 0)
            if curTime - lastReplenishTime < replenishCDSeconds:
                continue
            auctionItemMgr.worldAuctionReplenishTimeDict[cfgID] = curTime
            replenishItemID = ipyData.GetReplenishItemID()
            if replenishItemID:
                curItemIDCount = auctionItemMgr.worldAuctionItemCountDict.get(replenishItemID, 0)
                if curItemIDCount >= replenishAuctionCount:
                    continue
                addItemIDCount = replenishAuctionCount - curItemIDCount
                GameWorld.DebugLog("    动态补充拍品队列: cfgID=%s,replenishItemID=%s,addItemIDCount=%s" % (cfgID, replenishItemID, addItemIDCount))
                for _ in xrange(addItemIDCount):
                    addItemInfoList.append(replenishItemID)
            else:
                replenishEquipPlaces = ipyData.GetReplenishEquipPlaces()
                rpClassLV, rpColor, rpIsSuit = ipyData.GetReplenishEquipInfo()
                rpStar = 0
                for job in openJobList:
                    jobEquipKey = (job, rpClassLV, rpColor, rpIsSuit)
                    curJobEquipCount = auctionItemMgr.worldAuctionJobEquipCountDict.get(jobEquipKey, 0)
                    if curJobEquipCount >= replenishAuctionCount:
                        continue
                    addEquipCount = replenishAuctionCount - curJobEquipCount
                    GameWorld.DebugLog("    动态补充拍品队列: cfgID=%s,addEquipCount=%s,job=%s" % (cfgID, addEquipCount, job))
                    for _ in xrange(addEquipCount):
                        addItemInfoList.append([rpClassLV, rpColor, replenishEquipPlaces, rpIsSuit, rpStar, [job]])
            random.shuffle(addItemInfoList) # 动态模式待添加拍品打乱下顺序,防止批量添加同一职业物品
        # 指定模式
        elif curDateTime in addAuctionItemDatetimeList:
            addCountWeightList = ipyData.GetItemCountWeightList()
            auctionItemWeightList = ipyData.GetAuctionItemWeightList()
            addCount = GameWorld.GetResultByWeightList(addCountWeightList)
            GameWorld.DebugLog("    指定补充拍品队列: cfgID=%s,addCount=%s" % (cfgID, addCount))
            for _ in xrange(addCount):
                itemInfo = GameWorld.GetResultByWeightList(auctionItemWeightList)
                if itemInfo != None:
                    addItemInfoList.append(itemInfo)
            randMailKeyList = ipyData.GetRandMailKeyList()
            if randMailKeyList:
                randMailKey = random.choice(randMailKeyList)
        sysWaitAuctionItemList = []
        for itemInfo in addItemInfoList:
            randSeconds = random.randint(randSecondRange[0], randSecondRange[1])
            addItemTime = addItemTime + randSeconds
            sysWaitAuctionItemList.append([addItemTime, itemInfo])
        sysAuctionItemListDict[cfgID] = sysWaitAuctionItemList
    IpyGameDataPY.SetConfigEx("SysWaitAuctionItemListDict", sysAuctionItemListDict)
    #GameWorld.DebugLog("等待系统上架的拍品列表: %s" % sysAuctionItemListDict)
    # 随机邮件通知
    if randMailKey:
        playerIDList = []
        addItemList = []
        playerManager = GameWorld.GetPlayerManager()
        for i in xrange(playerManager.GetActivePlayerCount()):
            player = playerManager.GetActivePlayerAt(i)
            if player == None:
                continue
            if PlayerControl.GetIsTJG(player):
                continue
            playerIDList.append(player.GetPlayerID())
        PlayerCompensation.SendMailByKey(randMailKey, playerIDList, addItemList)
    return
def __DoSysWaitAddAuctionItem(tick):
    SysWaitAuctionItemListDict = IpyGameDataPY.GetConfigEx("SysWaitAuctionItemListDict") # 系统等待上架的拍品列表
    if not SysWaitAuctionItemListDict:
        return
    curTime = int(time.time())
    for cfgID, sysAuctionItemList in SysWaitAuctionItemListDict.items():
        doCount = len(sysAuctionItemList)
        while doCount > 0 and sysAuctionItemList:
            doCount -= 1
            addItemTime, itemInfo = sysAuctionItemList[0]
            if curTime < addItemTime:
                #GameWorld.DebugLog("未到系统等待上架的拍品时间,不处理! curTime=%s,sysAuctionItemList=%s" % (curTime, sysAuctionItemList))
                break
            sysAuctionItemList.pop(0)
            GameWorld.DebugLog("系统等待上架的拍品时间已到,可上架! curTime=%s >= addItemTime=%s,itemInfo=%s,sysAuctionItemList=%s" % (curTime, addItemTime, itemInfo, sysAuctionItemList))
            DoAddSystemAuctionItem([itemInfo])
        if not sysAuctionItemList:
            SysWaitAuctionItemListDict.pop(cfgID)
    return
def OnAuctionItemTimeProcess(curTime, tick):
    ## 拍卖行拍品定时处理,每秒触发一次
    __DoSysBuyoutItemByTime(curTime)
    __DoSysWaitAddAuctionItem(tick)
    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
    if not allAuctionItemByEndTimeList:
        return
    index = 0
    endItemList = [] # 结束竞价的拍品列表
    moveToWorldItemList = [] # 转移到全服拍卖的仙盟拍品列表
    doCount = len(allAuctionItemByEndTimeList)
    while doCount > 0 and allAuctionItemByEndTimeList:
        doCount -= 1
        auctionItem = allAuctionItemByEndTimeList[index]
        if curTime <= auctionItem.EndTime:
            break
        if auctionItem.BiddingQueryTick and tick - auctionItem.BiddingQueryTick < BiddingQueryLockTick:
            index += 1
            continue
        allAuctionItemByEndTimeList.pop(index)
        # 没有人竞价的仙盟拍品
        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 __DoSysBuyoutItemByTime(curTime):
    ## 系统一口价拍品
    sysBuyoutItemByTimeList = PyDataManager.GetAuctionItemManager().sysBuyoutItemByTimeList
    if not sysBuyoutItemByTimeList:
        return
    sysAuctionItemList = []
    color = 4 # 固定橙色
    isSuit = 0 # 固定非套装
    star = 0 # 固定0星
    endItemList = [] # 结束竞价的拍品列表
    doCount = len(sysBuyoutItemByTimeList)
    while doCount > 0 and sysBuyoutItemByTimeList:
        doCount -= 1
        auctionItem = sysBuyoutItemByTimeList[0]
        if curTime <= auctionItem.SysBuyTime:
            break
        sysBuyoutItemByTimeList.pop(0)
        if auctionItem.BidderPrice or auctionItem.BidderID:
            continue
        classLV = auctionItem.ItemClassLV
        if not classLV:
            continue
        itemID = auctionItem.ItemID
        ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
        if not ipyData:
            continue
        buyoutPrice = ipyData.GetBuyoutPrice()
        if not buyoutPrice:
            continue
        buyoutPrice *= auctionItem.Count
        auctionItem.BidderPrice = buyoutPrice # 没人竞价的系统直接一口价回收
        endItemList.append(auctionItem)
        # 生成系统补上架的装备信息
        randPlaceList = IpyGameDataPY.GetFuncEvalCfg("AuctionItemSystem", 4)
        if not randPlaceList:
            curPlace = itemID % 1000 / 10 # 倒数2、3位代表部位
            placeList = [curPlace]
        else:
            placeList = randPlaceList
        totalPlayerCount = 0
        jobWeightList = []
        openJobList = IpyGameDataPY.GetFuncEvalCfg("OpenJob", 1)
        for job in openJobList:
            jobPlayerCount = len(PyGameData.g_onedayJobPlayerLoginoffTimeDict.get(job, {}))
            jobPlayerCount = max(1, jobPlayerCount) # 人数默认至少1个
            totalPlayerCount += jobPlayerCount
            jobWeightList.append([jobPlayerCount, job])
            GameWorld.DebugLog("职业人数: job=%s,count=%s" % (job, jobPlayerCount))
        maxJobPer = IpyGameDataPY.GetFuncCfg("AuctionItemSystem", 3) # 单职业最大百分比
        if maxJobPer and maxJobPer < 100 and totalPlayerCount:
            minJobPer = 100 - maxJobPer # 单职业至少百分比
            for jobWeightInfo in jobWeightList:
                jobPlayerCount = jobWeightInfo[0]
                jobPer = int(jobPlayerCount * 100.0 / totalPlayerCount)
                jobPer = min(max(minJobPer, jobPer), maxJobPer)
                jobWeightInfo[0] = jobPer
        GameWorld.DebugLog("随机上架职业装备比重: jobWeightList=%s" % jobWeightList)
        job = GameWorld.GetResultByWeightList(jobWeightList)
        itemJobList = [job] if job != None else openJobList
        sysAuctionItemList.append([classLV, color, placeList, isSuit, star, itemJobList])
    if not endItemList:
        return
    __EndAuctionItem(endItemList, "SysBuyout")
    # 系统回收拍品后立即按规则补上架拍品
    if sysAuctionItemList:
        DoAddSystemAuctionItem(sysAuctionItemList)
    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)
        __OnCalcWorldAuctionItemCount(auctionItem, 1)
        notifyWorldAddItemList.append([itemGUID, itemID, playerID])
        AddAuctionRecord(auctionItem, AuctionRecordResult_MoveToWorld)
        # 添加进我的关注
        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 = {} # 重置全服拍品条件查询,下次有玩家查询时再重新刷新
        auctionItemMgr.sysBuyoutItemByTimeList.sort(key=operator.attrgetter("SysBuyTime"))
    return
def __EndAuctionItem(endItemList, endEvent, funcAutoBuyout=False, buyPlayer=None):
    ''' 结束拍品竞拍
    @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 bidderPrice:
            endType = "OK"
            # 竞拍成功邮件,发放物品
            if bidderID:
                mailTypeKey = "PaimaiMail3"
                paramList = [bidderPrice]
                detail = {"ItemGUID":itemGUID}
                addItemList = [{"ItemID":itemID, "Count":itemCount, "IsAuctionItem":False, "UserData":auctionItem.UserData}]
                if funcAutoBuyout:
                    # 功能自动购买的不给物品,由功能根据功能需求处理
                    pass
                ## 如果有玩家的,直接给到背包
                elif buyPlayer and buyPlayer.GetPlayerID() == bidderID:
                    mailInfo = [mailTypeKey, addItemList, paramList, detail]
                    resultMsg = str([itemGUID, itemID, itemCount, auctionItem.UserData, mailInfo])
                    buyPlayer.MapServer_QueryPlayerResult(0, 0, "AuctionHouseGiveItem", resultMsg, len(resultMsg))
                else:
                    PlayerCompensation.SendMailByKey(mailTypeKey, [bidderID], addItemList, paramList, detail=detail)
                AddAuctionRecord(auctionItem, AuctionRecordResult_BidOK)
            # 拍卖成功收益,都以玩家收益向上取整
            if familyID and auctionItem.FamilyPlayerIDInfo:
                familyPlayerIDList = json.loads(auctionItem.FamilyPlayerIDInfo)
                if familyPlayerIDList:
                    taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 2) # 仙盟拍品税率百分比
                    personMaxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 3) # 仙盟拍品个人最大收益百分比
                    taxGold = max(1, int(bidderPrice * taxRate / 100.0)) # 最少收税1
                    giveTotalGold = max(0, bidderPrice - taxGold)
                    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}
                    paramList = [itemID, itemID, bidderPrice, taxRate, giveGoldAverage, personMaxRate]
                    PlayerCompensation.SendMailByKey("PaimaiMail8", familyPlayerIDList, [], paramList, goldPaper=giveGoldAverage,
                                                     detail=detail, moneySource=ChConfig.Def_GiveMoney_AuctionGain)
                else:
                    GameWorld.ErrLog("仙盟拍品没有人获得收益!familyID=%s,itemID=%s,itemGUID=%s" % (familyID, itemID, itemGUID))
            elif playerID:
                taxRate = IpyGameDataPY.GetFuncCfg("AuctionTaxrate", 1) # 全服拍品税率百分比
                taxGold = max(1, int(bidderPrice * taxRate / 100.0)) # 最少收税1
                givePlayerGold = max(0, bidderPrice - taxGold)
                # 个人拍卖收益邮件
                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice}
                paramList = [itemID, itemID, bidderPrice, taxRate, givePlayerGold]
                PlayerCompensation.SendMailByKey("PaimaiMail7", [playerID], [], paramList, goldPaper=givePlayerGold,
                                                 detail=detail, moneySource=ChConfig.Def_GiveMoney_AuctionGain)
            else:
                GameWorld.Log("系统拍品成交: itemGUID=%s,itemID=%s" % (itemGUID, itemID))
            AddAuctionRecord(auctionItem, AuctionRecordResult_SellOK)
            #策划需求屏蔽掉成交广播
            #ipyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
            #if ipyData and ipyData.GetNeedWorldNotify():
            #    PlayerControl.WorldNotify(0, "Paimai6", [auctionItem.BidderName, bidderID, auctionItem.AuctionType, bidderPrice, itemID])
        else:
            # 仙盟拍品回收
            if familyID:
                endType = "Recycle"
                AddAuctionRecord(auctionItem, AuctionRecordResult_Recycle)
            # 个人拍品流拍,物品返还
            elif playerID:
                endType = "Return"
                # 返还物品邮件
                paramList = []
                detail = {"ItemGUID":itemGUID}
                addItemList = [{"ItemID":itemID, "Count":itemCount, "IsAuctionItem":True, "UserData":auctionItem.UserData}]
                # 下架
                if endEvent == "Unsell":
                    PlayerCompensation.SendMailByKey("PaimaiMail9", [playerID], addItemList, paramList, detail=detail)
                    AddAuctionRecord(auctionItem, AuctionRecordResult_Unsell)
                else:
                    PlayerCompensation.SendMailByKey("PaimaiMail4", [playerID], addItemList, paramList, detail=detail)
                    AddAuctionRecord(auctionItem, AuctionRecordResult_SellFail)
            else:
                endType = "SystemDelete"
                GameWorld.Log("系统拍品流拍: itemGUID=%s,itemID=%s" % (itemGUID, itemID))
        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)
            __OnCalcWorldAuctionItemCount(auctionItem, -1)
        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)
        if auctionItem in auctionItemMgr.sysBuyoutItemByTimeList:
            auctionItemMgr.sysBuyoutItemByTimeList.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 == "UnsellAuctionItem":
        itemGUID = queryData[0]
        __DoUnsellAuctionItem(curPlayer, itemGUID)
        return
    # 升星自动购买
    elif queryType == "EquipStarAutoBuy":
        buyResult = __DoEquipStarAutoBuyEquip(curPlayer, queryData,  tick)
        if buyResult == None:
            return
        result = buyResult
    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
    '''
    isSortWorldItem = False
    notifyAddItemList = [] # 新增拍品通知 [[itemGUID, itemID, playerID], ...]
    notifyFamilyAddItemDict = {} # 新增仙盟拍品通知 {familyID:[auctionItem, ...], ...}
    for playerID, familyID, familyPlayerIDList, itemData in addAuctionItemList:
        #系统拍品玩家ID、仙盟ID都为0,可上架,这里不再限制
        #if not playerID and not familyID:
        #    continue
        auctionItem = __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData)
        if not auctionItem:
            continue
        itemGUID = auctionItem.ItemGUID
        itemID = auctionItem.ItemID
        notifyAddItemList.append([itemGUID, itemID, playerID])
        if familyID:
            familyAddItemList = notifyFamilyAddItemDict.get(familyID, [])
            familyAddItemList.append(auctionItem)
            notifyFamilyAddItemDict[familyID] = familyAddItemList
        else:
            isSortWorldItem = True
    if notifyAddItemList:
        __SortAuctionitem(isSortWorldItem=isSortWorldItem)
    # 通知新增仙盟拍品
    for familyID, familyAddItemList in notifyFamilyAddItemDict.items():
        Sync_FamilyAuctionItemInfo(None, familyID, familyAddItemList)
    # 通知拍品关注玩家
    __NotifyAuctionPlayerAddItem(notifyAddItemList)
    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)
    auctionItem.AuctionType = AuctionType_Family if familyID else AuctionType_World
    GameWorld.Log("上架拍品: playerID=%s,familyID=%s,itemID=%s,auctionType=%s" % (playerID, familyID, itemID, auctionItem.AuctionType))
    if not __InitAuctionItemAttrEx(auctionItem):
        return
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    auctionItemMgr.allAuctionItemDict[auctionItem.ItemGUID] = auctionItem
    auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
    if familyID:
        familyItemList = auctionItemMgr.familyAuctionItemDict.get(familyID, [])
        familyItemList.append(auctionItem)
        auctionItemMgr.familyAuctionItemDict[familyID] = familyItemList
    else:
        auctionItemMgr.worldAuctionItemList.append(auctionItem)
        __OnCalcWorldAuctionItemCount(auctionItem, 1)
        # 添加进我的拍卖
        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(notifyAddItemList):
    ## 通知关注物品的玩家新上架物品了
    if not notifyAddItemList:
        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 notifyAddItemList:
            if playerID and 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 __DoUnsellAuctionItem(curPlayer, itemGUID):
    ## 下架拍品
    auctionItem = GetAuctionItem(itemGUID)
    if not auctionItem:
        # 拍品不存在
        PlayerControl.NotifyCode(curPlayer, "Paimai3")
        return
    playerID = curPlayer.GetPlayerID()
    itemID = auctionItem.ItemID
    if auctionItem.FamilyID:
        GameWorld.ErrLog("仙盟拍品无法下架!itemGUID=%s,itemID=%s,itemFamilyID=%s"
                         % (itemGUID, itemID, auctionItem.FamilyID), playerID)
        return
    if auctionItem.PlayerID != playerID:
        GameWorld.ErrLog("不是玩家自己的拍品无法下架!itemGUID=%s,itemID=%s,itemPlayerID=%s"
                         % (itemGUID, itemID, auctionItem.PlayerID), playerID)
        return
    if auctionItem.BidderPrice:
        # 竞价中的拍品不能下架
        PlayerControl.NotifyCode(curPlayer, "Paimai9")
        return
    __EndAuctionItem([auctionItem], "Unsell")
    return
def __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, isOnlyCheck, funcAutoBuyout=False):
    ''' 玩家竞价物品
    @param curPlayer: 竞价的玩家
    @param itemGUID: 拍品GUID
    @param biddingPrice: 竞价
    @param isOnlyCheck: 是否仅检查可否竞价
    @param funcAutoBuyout: 是否功能自动购买
    '''
    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() * auctionItem.Count if not auctionItem.BidderPrice else (auctionItem.BidderPrice + ipyData.GetBiddingAdd() * auctionItem.Count)
    buyoutPrice = ipyData.GetBuyoutPrice() * auctionItem.Count # 允许没有一口价的,代表可以一直竞价
    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 < BiddingQueryLockTick:
            # 有玩家正在竞价,请稍等
            PlayerControl.NotifyCode(curPlayer, "Paimai1")
            errInfo = "other player bidding"
            return itemID, errInfo
        auctionItem.BiddingQueryID = playerID
        auctionItem.BiddingQueryTick = tick
        return itemID, errInfo
    if not funcAutoBuyout and 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}
        if isBuyout:
            # 竞拍失败,仅通知
            paramList = [itemID, itemID, lastBidderPrice]
            PlayerCompensation.SendMailByKey("PaimaiMail2", [lastBidderID], [], paramList, goldPaper=lastBidderPrice,
                                             detail=detail, moneySource=ChConfig.Def_GiveMoney_AuctionBidReturn)
        else:
            # 竞拍失败,可继续竞价邮件
            paramList = [itemID, itemID, lastBidderPrice, itemGUID]
            PlayerCompensation.SendMailByKey("PaimaiMail1", [lastBidderID], [], paramList, goldPaper=lastBidderPrice,
                                             detail=detail, moneySource=ChConfig.Def_GiveMoney_AuctionBidReturn)
        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.BiddingQueryTick = 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 auctionItem in auctionItemMgr.sysBuyoutItemByTimeList:
        auctionItemMgr.sysBuyoutItemByTimeList.remove(auctionItem)
        #GameWorld.DebugLog("拍品有人竞价了,移除系统一口价拍品列表!")
    if isBuyout:
        __EndAuctionItem([auctionItem], "Buyout", funcAutoBuyout, buyPlayer=curPlayer)
    else:
        if __AddAuctionItemEndTimeByBid(auctionItem):
            __SortAuctionitem(isSortWorldItem=False)
        drDict = {"AuctionItemInfo":__GetAuctionItemDRDict(auctionItem)}
        DR_AuctionHouse(curPlayer, "BidAuctionItem", drDict)
        __SyncRefreshAuctionItem([auctionItem])
    return itemID, errInfo
def __DoEquipStarAutoBuyEquip(curPlayer, queryData, tick):
    ## 升星自动购买
    classLV, equipPlace, curPartStar, equipPackIndex, isAutoBuyPreview, curRate, delEquipGUIDDict, delItemInfoDict, lackItemCostMoney, playerGoldPaper = queryData
    GameWorld.DebugLog("升星自动购买装备: classLV=%s, equipPlace=%s, curPartStar=%s, equipPackIndex=%s" % (classLV, equipPlace, curPartStar, equipPackIndex))
    GameWorld.DebugLog("    是否预览 %s, curRate=%s,lackItemCostMoney=%s, playerGoldPaper=%s" % (isAutoBuyPreview, curRate, lackItemCostMoney, playerGoldPaper))
    nextStar = curPartStar + 1
    ipyData = IpyGameDataPY.GetIpyGameData("EquipStarUp", classLV, equipPlace, nextStar)
    if not ipyData:
        return
    costEquipPlaceList = ipyData.GetCostEquipPlace()
    costEquipColorList = ipyData.GetCostEquipColor()
    isJobLimit = ipyData.GetIsJobLimit()
    unSuitRate = ipyData.GetUnSuitRate()
    curTime = int(time.time())
    fullRate = IpyGameDataPY.GetFuncCfg("EquipStarRate", 4)
    autoBuyOtherClassItemDict = {}
    buyEquipCostMoney = 0
    autoBuyItemList = []
    auctionItemMgr = PyDataManager.GetAuctionItemManager()
    #GameWorld.DebugLog("世界拍品个数: %s" % len(auctionItemMgr.worldAuctionItemList))
    for i, worldAuctionItem in enumerate(auctionItemMgr.worldAuctionItemList):
        itemID = worldAuctionItem.ItemID
        aucItemJob = worldAuctionItem.ItemJobLimit
        if not aucItemJob:
            #GameWorld.DebugLog("    %s 职业通用的, 不购买!itemID=%s" % (i, itemID))
            continue
        if isJobLimit and aucItemJob != curPlayer.GetJob():
            #GameWorld.DebugLog("    %s 职业不可用, 不购买!itemID=%s,aucItemJob=%s != %s" % (i, itemID, aucItemJob, curPlayer.GetJob()))
            continue
        itemIDStr = str(itemID)
        aucItemColor = int(itemIDStr[3:4])
        aucItemPlace = int(itemIDStr[4:6])
        aucItemIsSuit = int(itemIDStr[-1])
        if aucItemColor not in costEquipColorList:
            #GameWorld.DebugLog("    %s 颜色限制, 不购买!itemID=%s,aucItemColor=%s not in %s" % (i, itemID, aucItemColor, costEquipColorList))
            continue
        if aucItemPlace not in costEquipPlaceList:
            #GameWorld.DebugLog("    %s 装备位限制, 不购买!itemID=%s,aucItemPlace=%s not in %s" % (i, itemID, aucItemPlace, costEquipPlaceList))
            continue
        if aucItemIsSuit:
            #套装不允许自动购买
            #GameWorld.DebugLog("    %s 套装, 不购买!itemID=%s" % (i, itemID))
            continue
        aucIpyData = IpyGameDataPY.GetIpyGameData("AuctionItem", itemID)
        if not aucIpyData:
            continue
        buyoutPrice = aucIpyData.GetBuyoutPrice()
        if not buyoutPrice:
            #GameWorld.DebugLog("    %s 没有一口价, 不购买!itemID=%s,buyoutPrice=%s" % (i, itemID, buyoutPrice))
            continue
        if curTime > worldAuctionItem.EndTime:
            #GameWorld.DebugLog("    %s 拍品已结束竞价, 不购买!itemID=%s" % (i, itemID))
            continue
        noticeMinutes = aucIpyData.GetNoticeSaleMinutes()
        if noticeMinutes:
            addTimeStr = worldAuctionItem.AddTime
            addTime = GameWorld.ChangeTimeStrToNum(addTimeStr)
            passMinutes = (curTime - addTime) / 60
            if passMinutes < noticeMinutes:
                #GameWorld.DebugLog("    %s 拍品尚未开放竞价, 不购买!itemID=%s" % (i, itemID))
                continue
        aucItemClassLV = worldAuctionItem.ItemClassLV
        # 本阶的直接处理
        if aucItemClassLV == classLV:
            autoBuyItemList.append([worldAuctionItem, buyoutPrice])
            curRate += unSuitRate
            buyEquipCostMoney += buyoutPrice
            GameWorld.DebugLog("    %s 本阶优先购买!itemID=%s,classLV=%s,curRate=%s,buyoutPrice=%s,buyEquipCostMoney=%s"
                               % (i, itemID, classLV, curRate, buyoutPrice, buyEquipCostMoney))
            if curRate >= fullRate:
                curRate = 100
                GameWorld.DebugLog("        自动购买本阶概率已满足!curRate=%s" % (curRate))
                break
        # 其他阶的需要按阶的优先级进行处理
        else:
            if aucItemClassLV not in autoBuyOtherClassItemDict:
                autoBuyOtherClassItemDict[aucItemClassLV] = []
            classItemList = autoBuyOtherClassItemDict[aucItemClassLV]
            classItemList.append([worldAuctionItem, buyoutPrice])
            GameWorld.DebugLog("    %s 非本阶, 暂不处理! itemID=%s,aucItemClassLV=%s" % (i, itemID, aucItemClassLV))
    # 未满概率时再购买其他阶
    if curRate < 100:
        lowClassList, highClassList = [], []
        for othClassLV in autoBuyOtherClassItemDict.keys():
            if othClassLV <= classLV:
                lowClassList.append(othClassLV)
            else:
                highClassList.append(othClassLV)
        lowClassList.sort(reverse=True)
        highClassList.sort()
        buyClassLVList = lowClassList + highClassList
        GameWorld.DebugLog("本阶概率未满,检查购买其他阶! curRate=%s,buyClassLVList=%s" % (curRate, buyClassLVList))
        diffClassChangeRatePerInfo = IpyGameDataPY.GetFuncEvalCfg("EquipStarRate", 1)
        unSuitRateRange = IpyGameDataPY.GetFuncEvalCfg("EquipStarRate", 2)
        for othClassLV in buyClassLVList:
            classItemList = autoBuyOtherClassItemDict[othClassLV]
            for worldAuctionItem, buyoutPrice in classItemList:
                baseRate = unSuitRate
                minRate, maxRate = unSuitRateRange
                costClassLV = worldAuctionItem.ItemClassLV
                itemID = worldAuctionItem.ItemID
                #吞高阶
                if costClassLV > classLV:
                    diffClassChangeRatePer = diffClassChangeRatePerInfo[0] * (costClassLV - classLV)
                    addRate = int(math.ceil(round(baseRate * (100 + diffClassChangeRatePer) /100.0, 2)))
                    GameWorld.DebugLog("    吞高阶 itemID=%s,costClassLV=%s,classLV=%s,baseRate=%s,diffClassChangeRatePer=%s,addRate=%s"
                                       % (itemID, costClassLV, classLV, baseRate, diffClassChangeRatePer, addRate))
                #吞低阶
                elif costClassLV < classLV:
                    diffClassChangeRatePer = diffClassChangeRatePerInfo[1] * (classLV - costClassLV)
                    addRate = int(math.ceil(round(baseRate * (100 - diffClassChangeRatePer) /100.0, 2)))
                    GameWorld.DebugLog("    吞低阶 itemID=%s,costClassLV=%s,classLV=%s,baseRate=%s,diffClassChangeRatePer=%s,addRate=%s"
                                       % (itemID, costClassLV, classLV, baseRate, diffClassChangeRatePer, addRate))
                else:
                    addRate = baseRate
                addRate = max(minRate, min(addRate, maxRate))
                autoBuyItemList.append([worldAuctionItem, buyoutPrice])
                curRate += addRate
                buyEquipCostMoney += buyoutPrice
                GameWorld.DebugLog("        curRate=%s,buyoutPrice=%s,buyEquipCostMoney=%s" % (curRate, buyoutPrice, buyEquipCostMoney))
                if curRate >= fullRate:
                    GameWorld.DebugLog("        自动购买补充其他阶概率已满足!curRate=%s" % (curRate))
                    curRate = 100
                    break
            if curRate >= fullRate:
                break
    totalCostMoney = lackItemCostMoney + buyEquipCostMoney
    GameWorld.DebugLog("    lackItemCostMoney=%s,buyEquipCostMoney=%s,totalCostMoney=%s,curRate=%s" % (lackItemCostMoney, buyEquipCostMoney, totalCostMoney, curRate))
    if isAutoBuyPreview:
        __SyncEquipStarAutoBuyCostInfo(curPlayer, classLV, equipPlace, curPartStar, curRate, totalCostMoney)
        return
    if curRate < 100:
        # 自动购买必须满概率
        # 因为确认购买不是实时的,所以存在拍卖行预览消耗装备可能被其他玩家买走导致无法满赶驴,所以这里需要补同步一次
        __SyncEquipStarAutoBuyCostInfo(curPlayer, classLV, equipPlace, curPartStar, curRate, totalCostMoney)
        PlayerControl.NotifyCode(curPlayer, "AutoBuyEquipLackEquip")
        return
    if playerGoldPaper < totalCostMoney:
        # 因为确认购买不是实时的,所以存在拍卖行预览消耗的价格与实际购买可能出现消耗价格不一致的情况,所以这里需要补同步一次
        __SyncEquipStarAutoBuyCostInfo(curPlayer, classLV, equipPlace, curPartStar, curRate, totalCostMoney)
        PlayerControl.NotifyCode(curPlayer, "AutoBuyEquipLackMoney", [IPY_PlayerDefine.TYPE_Price_Gold_Paper])
        return
    for worldAuctionItem, buyoutPrice in autoBuyItemList:
        # 这里认为一定可以购买成功,不对返回值做处理,即使无法购买也认为购买成功,玩家的消耗照常扣除
        __DoPlayerBidAuctionItem(curPlayer, worldAuctionItem.ItemGUID, buyoutPrice, tick, False, funcAutoBuyout=True)
    return classLV, equipPlace, curPartStar, equipPackIndex, curRate, delEquipGUIDDict, delItemInfoDict, lackItemCostMoney, buyEquipCostMoney
def __SyncEquipStarAutoBuyCostInfo(curPlayer, classLV, equipPlace, curPartStar, curRate, totalCostMoney):
    ## 通知自动购买预览结果
    costInfo = ChPyNetSendPack.tagGCEquipStarAutoBuyCostInfo()
    costInfo.ClassLV = classLV
    costInfo.EquipPlace = equipPlace
    costInfo.CurStar = curPartStar
    costInfo.CurRate = curRate
    costInfo.AutoBuyCostMoney = totalCostMoney
    NetPackCommon.SendFakePack(curPlayer, costInfo)
    return
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
        refreshItem.BiddingTime = auctionItem.BiddingTime
        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))
    GameWorld.DebugLog("系统一口价拍品个数: %s" % len(auctionItemMgr.sysBuyoutItemByTimeList))
    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, isNotify=True)
    return
def __Sync_WorldAuctionItemQueryResult(curPlayer, job=0, itemTypeList=[], classLV=0, specItemIDList=[], fromItemGUID="", queryDir=1, queryCount=10, isNotify=False):
    ## 根据过滤条件同步全服拍品列表,目前仅全服拍品需要通过查询服务器获得,个人拍品及仙盟拍品由于个数较少直接由上线或变更时主动同步
    fromAuctionItem = None
    if fromItemGUID:
        fromAuctionItem = GetAuctionItem(fromItemGUID)
        if not fromAuctionItem:
            GameWorld.DebugLog("查询的目标拍品不存在! fromItemGUID=%s" % fromItemGUID)
            if isNotify:
                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 and worldAuctionItem.ItemJobLimit:
                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)
            if isNotify:
                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.BiddingTime = auctionItem.BiddingTime
        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.BiddingTime = attentionItem.BiddingTime
        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.BiddingTime = auctionItem.BiddingTime
        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.BiddingTime = auctionItem.BiddingTime
        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.BiddingTime = auctionItem.BiddingTime
        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
def DoAddFamilyAuctionItem(mapID, familyAuctionItemDict):
    ''' 上架仙盟拍品,因为仙盟拍品默认上架,所以使用批量上架
    @param familyAuctionItemDict: {仙盟ID:[[享受收益的成员ID, ...], [[拍品ID,个数], [拍品ID,个数,是否拍品], ...]], ...}
    '''
    GameWorld.Log("发送地图上架仙盟拍品: mapID=%s, %s" % (mapID, familyAuctionItemDict))
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_AddFamilyAuctionItem, [mapID, familyAuctionItemDict])
    return
def DoAddSystemAuctionItem(sysAuctionItemList):
    ''' 上架系统拍品
    @param sysAuctionItemList: [物品ID, [阶,颜色,[部位, ...],是否套装,星级,[可选参数职业, ...]], ...]
    '''
    mapID = ChConfig.Def_FBMapID_MainCity
    GameWorld.Log("发送地图上架系统拍品: mapID=%s, %s" % (mapID, sysAuctionItemList))
    GameWorld.SendMapServerMsgEx(ShareDefine.Def_Notify_WorldKey_AddSystemAuctionItem, [mapID, sysAuctionItemList])
    return