ServerPython/CoreServerGroup/GameServer/Script/GameWorldLogic/AuctionHouse.py
@@ -20,14 +20,19 @@
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
@@ -48,7 +53,8 @@
AuctionRecordResult_BidOK, # 竞价成功
AuctionRecordResult_BidFail, # 竞价失败
AuctionRecordResult_MoveToWorld, # 仙盟拍品转移到全服拍品
) = range(6)
AuctionRecordResult_Unsell, # 下架
) = range(7)
# 当前拍品归类 0-全服拍品 1-仙盟私有拍品
AuctionType_World = 0
@@ -214,10 +220,22 @@
def __InitAuctionAttentionAttrEx(attentionData):
    ## 初始化拍卖关注实例附加属性
    setattr(attentionData, "AttentionItemIDList", [] if not attentionData.AttentionInfo else json.loads(attentionData.AttentionInfo))
    setattr(attentionData, "AttentionItemIDList", [])
    if attentionData.AttentionInfo.startswith("[") and attentionData.AttentionInfo.endswith("]"):
        attentionData.AttentionItemIDList = json.loads(attentionData.AttentionInfo)
    return
##-------------------------------------------------------------------------------------------------
def OnGameServerInitOK():
    ## 服务器启动成功
    allAuctionItemByEndTimeList = PyDataManager.GetAuctionItemManager().allAuctionItemByEndTimeList
    # 由于服务器未启动成功时取不到正确的开服天,所以启动成功后刷新一下拍品系统回购时间
    for auctionItem in allAuctionItemByEndTimeList:
        __SetAuctionItemSysBuyTime(auctionItem)
    __SortAuctionitem()
    return
def OnLoadAuctionItemDataEx(dbData):
    ## 加载拍卖物品表时附加处理
@@ -231,6 +249,7 @@
    
    if dbData.AuctionType == AuctionType_World:
        pyAuctionItemMgr.worldAuctionItemList.append(dbData)
        __OnCalcWorldAuctionItemCount(dbData, 1)
        
    if familyID:
        familyItemList = pyAuctionItemMgr.familyAuctionItemDict.get(familyID, [])
@@ -267,9 +286,73 @@
    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
    # 注意: 因为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
    canSysBuyItemTypeList = IpyGameDataPY.GetFuncEvalCfg("AuctionItemSystem", 2)
    if itemType not in canSysBuyItemTypeList:
        #GameWorld.DebugLog("该拍品类型不可被系统一口价,不设置系统一口价时间! itemID=%s,itemType=%s" % (itemID, itemType))
        return
    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值
@@ -326,8 +409,281 @@
            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
@@ -356,6 +712,89 @@
    __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:
@@ -380,6 +819,7 @@
        
        auctionItemMgr.allAuctionItemByEndTimeList.append(auctionItem)
        auctionItemMgr.worldAuctionItemList.append(auctionItem)
        __OnCalcWorldAuctionItemCount(auctionItem, 1)
        notifyWorldAddItemList.append([itemGUID, itemID, playerID])
        
        AddAuctionRecord(auctionItem, AuctionRecordResult_MoveToWorld)
@@ -427,9 +867,10 @@
    if isSortWorldItem:
        auctionItemMgr.worldAuctionItemList.sort(key=operator.attrgetter("Sortpriority", "AddTime"))
        auctionItemMgr.worldAuctionItemQueryDict = {} # 重置全服拍品条件查询,下次有玩家查询时再重新刷新
        auctionItemMgr.sysBuyoutItemByTimeList.sort(key=operator.attrgetter("SysBuyTime"))
    return
def __EndAuctionItem(endItemList, endEvent):
def __EndAuctionItem(endItemList, endEvent, funcAutoBuyout=False, buyPlayer=None):
    ''' 结束拍品竞拍
    @param delItemStateDict: 删除的拍品竞拍状态
    '''
@@ -451,15 +892,26 @@
        bidderPrice = auctionItem.BidderPrice # 当前竞价,有人竞价的话代表竞拍成功
        endType = ""
        # 有人竞价,成交
        if bidderID and bidderPrice:
        if bidderPrice:
            endType = "OK"
            
            # 竞拍成功邮件,发放物品
            paramList = [bidderPrice]
            detail = {"ItemGUID":itemGUID}
            addItemList = [{"ItemID":itemID, "Count":itemCount, "IsAuctionItem":False, "UserData":auctionItem.UserData}]
            PlayerCompensation.SendMailByKey("PaimaiMail3", [bidderID], addItemList, paramList, detail=detail)
            AddAuctionRecord(auctionItem, AuctionRecordResult_BidOK)
            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:
@@ -475,8 +927,8 @@
                    
                    # 仙盟拍品收益邮件
                    detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice, "FamilyPlayerIDList":familyPlayerIDList}
                    paramList = [itemID, itemID, auctionItem.BidderName, bidderPrice, taxRate, giveGoldAverage, personMaxRate]
                    PlayerCompensation.SendMailByKey("PaimaiMail6", familyPlayerIDList, [], paramList, gold=giveGoldAverage,
                    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))
@@ -488,31 +940,42 @@
                
                # 个人拍卖收益邮件
                detail = {"ItemGUID":itemGUID, "ItemID":itemID, "Count":itemCount, "BidderPrice":bidderPrice}
                paramList = [itemID, itemID, auctionItem.BidderName, bidderPrice, taxRate, givePlayerGold]
                PlayerCompensation.SendMailByKey("PaimaiMail5", [playerID], [], paramList, gold=givePlayerGold,
                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])
            #策划需求屏蔽掉成交广播
            #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)
            # 个人拍品流拍,物品返还
            else:
            elif playerID:
                endType = "Return"
                
                # 流拍返还物品邮件
                # 返还物品邮件
                paramList = []
                detail = {"ItemGUID":itemGUID}
                addItemList = [{"ItemID":itemID, "Count":itemCount, "IsAuctionItem":False, "UserData":auctionItem.UserData}]
                PlayerCompensation.SendMailByKey("PaimaiMail4", [playerID], addItemList, paramList, detail=detail)
                AddAuctionRecord(auctionItem, AuctionRecordResult_SellFail)
                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)
@@ -527,6 +990,7 @@
            
        if auctionItem in auctionItemMgr.worldAuctionItemList:
            auctionItemMgr.worldAuctionItemList.remove(auctionItem)
            __OnCalcWorldAuctionItemCount(auctionItem, -1)
            
        for queryItemList in auctionItemMgr.worldAuctionItemQueryDict.values():
            if auctionItem in queryItemList:
@@ -537,6 +1001,9 @@
            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)
@@ -625,6 +1092,19 @@
            
        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
@@ -660,8 +1140,9 @@
    notifyAddItemList = [] # 新增拍品通知 [[itemGUID, itemID, playerID], ...]
    notifyFamilyAddItemDict = {} # 新增仙盟拍品通知 {familyID:[auctionItem, ...], ...}
    for playerID, familyID, familyPlayerIDList, itemData in addAuctionItemList:
        if not playerID and not familyID:
            continue
        #系统拍品玩家ID、仙盟ID都为0,可上架,这里不再限制
        #if not playerID and not familyID:
        #    continue
        
        auctionItem = __DoAddAuctionItem(curPlayer, playerID, familyID, familyPlayerIDList, itemData)
        if not auctionItem:
@@ -710,7 +1191,9 @@
    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
    
@@ -719,13 +1202,12 @@
    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)
        __OnCalcWorldAuctionItemCount(auctionItem, 1)
        
        # 添加进我的拍卖
        if playerID:
@@ -786,12 +1268,38 @@
            NetPackCommon.SendFakePack(player, infoPack)
    return
def __DoPlayerBidAuctionItem(curPlayer, itemGUID, biddingPrice, tick, isOnlyCheck):
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 = ""
@@ -852,7 +1360,7 @@
        auctionItem.BiddingQueryTick = tick
        return itemID, errInfo
    
    if auctionItem.BiddingQueryID != playerID:
    if not funcAutoBuyout and auctionItem.BiddingQueryID != playerID:
        PlayerControl.NotifyCode(curPlayer, "Paimai2")
        errInfo = "bidding player error"
        return itemID, errInfo
@@ -873,12 +1381,12 @@
        if isBuyout:
            # 竞拍失败,仅通知
            paramList = [itemID, itemID, lastBidderPrice]
            PlayerCompensation.SendMailByKey("PaimaiMail2", [lastBidderID], [], paramList, gold=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, gold=lastBidderPrice,
            PlayerCompensation.SendMailByKey("PaimaiMail1", [lastBidderID], [], paramList, goldPaper=lastBidderPrice,
                                             detail=detail, moneySource=ChConfig.Def_GiveMoney_AuctionBidReturn)
        AddAuctionRecord(auctionItem, AuctionRecordResult_BidFail)
        
@@ -927,8 +1435,12 @@
    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")
    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)
@@ -939,6 +1451,183 @@
        __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
@@ -1002,9 +1691,12 @@
    for i, auctionItem in enumerate(allAuctionItemByEndTimeList):
        GameWorld.DebugLog("    i=%s, %s" % (i, __GetAuctionItemDRDict(auctionItem)))
        
    GameWorld.DebugLog("AllDict总拍品数: =%s" % len(auctionItemMgr.allAuctionItemDict))
    GameWorld.DebugLog("AllDict总拍品数: %s" % len(auctionItemMgr.allAuctionItemDict))
    
    GameWorld.DebugLog("全服拍品个数: =%s" % len(auctionItemMgr.worldAuctionItemList))
    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)))
        
@@ -1378,11 +2070,11 @@
    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)
#    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):
@@ -1393,3 +2085,12 @@
    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