ServerPython/CoreServerGroup/GameServer/Script/Player/PlayerCompensation.py
@@ -28,24 +28,29 @@
import NetPackCommon
import CommFunc
import ChPyNetSendPack
import PyGameDataStruct
import CrossRealmMsg
import DataRecordPack
import ReadChConfig
import PlayerDBOper
import EventReport
import IpyGameDataPY
import PlayerControl
import PyDataManager
import PyGameData
import datetime
import uuid
import math
import json
#领取状态  个位数标识 (可领取即已通知过玩家该封邮件,需要在邮件发生时和上线时设置状态
#0 未通知,1 不可领取, 2 可领取, 3 已领取
#0 未通知,1 不可领取, 2 可领取, 3 已领取,4 已删除
(
Unknown_State,
Disable_State,
Enable_State,
Yet_State
) = range(4)
Yet_State,
Del_State
) = range(5)
CheckState_OK = 0 # 已审核
CheckState_No = 1 # 未审核
@@ -68,16 +73,228 @@
Def_RequestState = "CompensationRequestState"
#==================================================================================================
class CrossPersonalCompensationManager(object):
    ## 跨服邮件管理,注意该类只处理数据逻辑,功能相关逻辑不要写在该类,不然重读脚本不会生效
    def __init__(self):
        self.playerMailDict = {} # 玩家补偿列表 {playerID:{GUID:tagDBCrossPersonalCompensation, ...}, ...}
        return
    # 保存数据 存数据库和realtimebackup
    def GetSaveData(self):
        savaData = ""
        cntData = ""
        cnt = 0
        for mailDict in self.playerMailDict.values():
            for mailObj in mailDict.values():
                cnt += 1
                savaData += mailObj.getBuffer()
        GameWorld.Log("Save DBCrossPersonalCompensation count :%s, len=%s" % (cnt, len(savaData)))
        return CommFunc.WriteDWORD(cntData, cnt) + savaData
    # 从数据库载入数据
    def LoadPyGameData(self, datas, pos, dataslen):
        cnt, pos = CommFunc.ReadDWORD(datas, pos)
        GameWorld.Log("Load DBCrossPersonalCompensation count :%s" % cnt)
        for _ in xrange(cnt):
            mailObj = PyGameDataStruct.tagDBCrossPersonalCompensation()
            mailObj.clear()
            pos += mailObj.readData(datas, pos, dataslen)
            playerID = mailObj.PlayerID
            guid = mailObj.GUID
            if playerID not in self.playerMailDict:
                self.playerMailDict[playerID] = {}
            mailDict = self.playerMailDict[playerID]
            mailDict[guid] = mailObj
        return pos
def Sync_CrossMailPlayerIDToClientServer(serverGroupID=0):
    ''' 同步有跨服邮件的玩家ID到子服
    @param serverGroupID: 为0时同步所有子服
    '''
    crossMailMgr = PyDataManager.GetCrossPersonalCompensationManager()
    addMailPlayerIDList = crossMailMgr.playerMailDict.keys()
    if not addMailPlayerIDList:
        return
    # 同步子服务器
    serverGroupIDList = [serverGroupID] if serverGroupID else []
    dataMsg = {"IDType":"Add", "PlayerIDList":addMailPlayerIDList}
    CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_MailPlayerIDList, dataMsg, serverGroupIDList)
    return
def CrossServerMsg_MailPlayerIDList(dataMsg):
    ## 收到跨服服务器同步有跨服邮件的玩家ID列表
    idType = dataMsg["IDType"]
    playerIDList = dataMsg["PlayerIDList"]
    for playerID in playerIDList:
        if idType == "Del":
            if playerID in PyGameData.g_crossMailPlayerDict:
                PyGameData.g_crossMailPlayerDict.pop(playerID)
            continue
        if idType == "Add":
            if playerID in PyGameData.g_crossMailPlayerDict:
                continue
            PyGameData.g_crossMailPlayerDict[playerID] = 0
            player = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
            if not player or not player.GetInitOK():
                continue
            RequestToGetCrossMail(player)
    return
def RequestToGetCrossMail(curPlayer):
    ## 请求同步跨服邮件内容
    playerID = curPlayer.GetPlayerID()
    if playerID not in PyGameData.g_crossMailPlayerDict:
        return
    lastTick = PyGameData.g_crossMailPlayerDict[playerID]
    tick = GameWorld.GetGameWorld().GetTick()
    if tick - lastTick <= 30000:
        return
    PyGameData.g_crossMailPlayerDict[playerID] = tick
    dataMsg = {"CMD":"Get", "PlayerID":playerID}
    CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_MailContent, dataMsg)
    return
def ClientServerMsg_MailContent(serverGroupID, msgData, tick):
    ## 收到子服玩家请求同步跨服邮件
    GameWorld.Log("收到子服玩家同步个人邮件命令: serverGroupID=%s, %s" % (serverGroupID, msgData))
    reqCMD = msgData["CMD"]
    playerID = msgData["PlayerID"]
    guidList = msgData.get("GuidList", [])
    crossMailMgr = PyDataManager.GetCrossPersonalCompensationManager()
    if playerID not in crossMailMgr.playerMailDict:
        return
    playerMailDict = crossMailMgr.playerMailDict[playerID]
    if reqCMD == "Get":
        SyncTickAttrKey = "SyncTick"
        getMailList = []
        for guid, mailObj in playerMailDict.items():
            if hasattr(mailObj, SyncTickAttrKey):
                getTick = getattr(mailObj, SyncTickAttrKey)
                if tick - getTick <= 30000:
                    GameWorld.DebugLog("短时间内重复请求领取的邮件不同步,防止重复发放! GUID=%s" % guid)
                    continue
            setattr(mailObj, SyncTickAttrKey, tick)
            crossMailDict = {"PlayerID":mailObj.PlayerID, "GUID":mailObj.GUID, "LimitTime":mailObj.LimitTime,
                             "Text":mailObj.Text, "ItemInfo":mailObj.ItemInfo, "Detail":mailObj.Detail,
                             "Gold":mailObj.Gold, "GoldPaper":mailObj.GoldPaper, "Silver":mailObj.Silver,
                             "MoneySource":mailObj.MoneySource
                             }
            getMailList.append(crossMailDict)
        if getMailList:
            CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_MailContent, getMailList, [serverGroupID])
    elif reqCMD == "GetOK":
        for guid in guidList:
            playerMailDict.pop(guid, None)
            DataRecordPack.DR_GiveCompensationSuccess(playerID, guid)
        if not playerMailDict:
            crossMailMgr.playerMailDict.pop(playerID, None)
            dataMsg = {"IDType":"Del", "PlayerIDList":[playerID]}
            CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_MailPlayerIDList, dataMsg, [serverGroupID])
    return
def CrossServerMsg_MailContent(getMailList):
    ## 收到跨服服务器同步的待发送邮件内容
    playerGetGUIDInfo = {}
    for mailDict in getMailList:
        GUID = mailDict.get("GUID", "")
        playerID = mailDict.get("PlayerID", 0)
        if not GUID or not playerID:
            continue
        GameWorld.Log("收到跨服个人邮件内容:%s" % mailDict, playerID)
        addItemDictList = eval(mailDict.get("ItemInfo", "[]"))
        LimitTime = mailDict.get("LimitTime", "")
        Text = mailDict.get("Text", "")
        gold = GameWorld.ToIntDef(mailDict.get("Gold"))
        goldPaper = GameWorld.ToIntDef(mailDict.get("GoldPaper"))
        silver = GameWorld.ToIntDef(mailDict.get("Silver"))
        moneySource = GameWorld.ToIntDef(mailDict.get("MoneySource"), ChConfig.Def_GiveMoney_Mail)
        detail = mailDict.get("Detail", "")
        AddPersonalItem(GUID, addItemDictList, [playerID], LimitTime, Text, gold, goldPaper, silver, detail, moneySource)
        if playerID not in playerGetGUIDInfo:
            playerGetGUIDInfo[playerID] = []
        guidList = playerGetGUIDInfo[playerID]
        guidList.append(GUID)
    for playerID, guidList in playerGetGUIDInfo.items():
        dataMsg = {"CMD":"GetOK", "PlayerID":playerID, "GuidList":guidList}
        CrossRealmMsg.SendMsgToCrossServer(ShareDefine.ClientServerMsg_MailContent, dataMsg)
    return
def __AddPlayerCrossMail(addItemDictList, PlayerIDList, LimitTime, Text, gold, goldPaper, silver, detail, moneySource):
    ## 添加跨服玩家个人补偿邮件
    addMailPlayerIDList = []
    crossMailMgr = PyDataManager.GetCrossPersonalCompensationManager()
    for playerID in PlayerIDList:
        GUID = str(uuid.uuid1()) # 由于跨服邮件由每个玩家独自同步个人邮件,所以插入时每个人的邮件单独GUID,防止批量发送同内容邮件时重复插入奖励物品
        mailObj = PyGameDataStruct.tagDBCrossPersonalCompensation()
        mailObj.PlayerID = playerID
        mailObj.GUID = GUID
        mailObj.LimitTime = LimitTime
        mailObj.Text = Text
        mailObj.TextLen = len(mailObj.Text)
        mailObj.Gold = gold
        mailObj.GoldPaper = goldPaper
        mailObj.Silver = silver
        mailObj.ItemInfo = json.dumps(addItemDictList, ensure_ascii=False)
        mailObj.ItemLen = len(mailObj.ItemInfo)
        mailObj.Detail = detail
        mailObj.DetailLen = len(mailObj.Detail)
        mailObj.MoneySource = moneySource
        if playerID not in crossMailMgr.playerMailDict:
            crossMailMgr.playerMailDict[playerID] = {}
            addMailPlayerIDList.append(playerID)
        playerMailDict = crossMailMgr.playerMailDict[playerID]
        playerMailDict[GUID] = mailObj
        #添加流向
        addDict = {"LimitTime":LimitTime, "Text":Text, "Gold":gold, "GoldPaper":goldPaper, "Silver":silver,
                   "ItemListLen":len(addItemDictList), "Detail":detail, "MoneySource":moneySource, "CrossMail":True}
        DataRecordPack.DR_AddPersonalCompensation(PlayerIDList, GUID, addItemDictList, addDict)
    if addMailPlayerIDList:
        dataMsg = {"IDType":"Add", "PlayerIDList":addMailPlayerIDList}
        CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_MailPlayerIDList, dataMsg)
    return
#==================================================================================================
## 根据物品信息字典,生成补偿物品实例,用于GM工具添加补偿
#  @param curItemDict 
#  @return IpyCompensationItem
def MakeCompensationItem(curItemDict):
    if not __checkMailItemDict(curItemDict):
        return
    curItemData = IPY_GameServer.IpyCompensationItem()
    #curItemData.GUID = curItemDict['GUID']
    curItemData.ItemID = curItemDict['ItemID']
    curItemData.Count = curItemDict['Count']
    #curItemData.IsBind = curItemDict.get('IsBind',0)
    curItemData.IsBind = 1 if not curItemDict.get('IsAuctionItem',0) else 0
    curItemData.IsBind = curItemDict.get('IsAuctionItem',0)
    curItemData.UserData = curItemDict.get('UserData', '')
    return curItemData
@@ -86,14 +303,14 @@
#  @param addItemList [(itemID, itemCnt, 是否拍品), {或物品信息字典}, ...]
#  @return GUID
def SendPersonalItemMailEx(title, content, getDays, playerIDList, addItemList, gold = 0, goldPaper = 0, silver = 0, 
                           detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
                           detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    limitTime = str(GameWorld.GetDatetimeByDiffDays(getDays))
    limitTime = limitTime.split(".")[0]
    return SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource)
    return SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource, crossMail)
    
def SendPersonalItemMailBatch(batchMailInfoList):
    ## 批量发送邮件
    mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource = batchMailInfoList
    mailTypeKey, batchPlayerIDList, batchAddItemList, batchParamList, batchGold, batchGoldPaper, batchSilver, batchDetail, moneySource, crossMail = batchMailInfoList
    
    lenPlayerID = len(batchPlayerIDList)
    lenItem = len(batchAddItemList)
@@ -109,6 +326,8 @@
    limitTime = limitTime.split(".")[0]
    
    for i, playerIDList in enumerate(batchPlayerIDList):
        if not playerIDList:
            continue
        addItemList = batchAddItemList[i] if lenItem == lenPlayerID else []
        paramList = batchParamList[i] if lenParam == lenPlayerID else []
        gold = batchGold[i] if lenGold == lenPlayerID else 0
@@ -116,19 +335,77 @@
        silver = batchSilver[i] if lenSilver == lenPlayerID else 0
        detail = batchDetail[i] if lenDetail == lenPlayerID else ""
        content = "<MailTemplate>%s</MailTemplate>%s" % (mailTypeKey, str(paramList))
        SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource)
        SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold, goldPaper, silver, detail=detail, moneySource=moneySource, crossMail=crossMail)
        
    return
def SendMailByKey(mailTypeKey, playerIDList, addItemList, paramList=[], gold=0, goldPaper=0, silver=0, 
                  detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
                  detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    if not mailTypeKey:
        mailTypeKey = ShareDefine.DefaultLackSpaceMailType
    GameWorld.DebugLog("SendMailByKey %s, playerIDList=%s, addItemList=%s, paramList=%s, gold=%s, goldPaper=%s, silver=%s, moneySource=%s" 
                       % (mailTypeKey, playerIDList, addItemList, paramList, gold, goldPaper, silver, moneySource))
    title = ""
    content = "<MailTemplate>%s</MailTemplate>%s" % (mailTypeKey, json.dumps(paramList, ensure_ascii=False))
    return SendPersonalItemMailEx(title, content, 30, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource)
    return SendPersonalItemMailEx(title, content, 30, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource, crossMail)
def CrossServerMsg_SendMail(msgData):
    ## 收到跨服服务器同步的发送邮件
    mailTypeKey = msgData["MailTypeKey"]
    playerIDList = msgData["Player"]
    addItemList = msgData.get("Item", [])
    paramList = msgData.get("Param", [])
    SendMailByKey(mailTypeKey, playerIDList, addItemList, paramList)
    return
def __checkMailItemIDCount(itemID, itemCount, isAuctionItem):
    ## 检查是否合法的邮件物品ID Count 数据
    if not (isinstance(itemID, int) or isinstance(itemID, long)):
        return
    if not (isinstance(itemCount, int) or isinstance(itemCount, long)):
        return
    if not (isinstance(isAuctionItem, int) or isinstance(isAuctionItem, long) or isinstance(isAuctionItem, bool)):
        return
    if itemID > ShareDefine.Def_UpperLimit_DWord or itemCount > ShareDefine.Def_UpperLimit_DWord or isAuctionItem > 255:
        return
    return True
def __checkMailItemDict(curItemDict):
    ## 检查是否合法的邮件物品dict信息  {"ItemID":xxx, "Count":xxx, "IsAuctionItem":xxx}
    if not isinstance(curItemDict, dict):
        return
    if "ItemID" not in curItemDict or "Count" not in curItemDict:
        return
    itemID = curItemDict["ItemID"]
    itemCount = curItemDict["Count"]
    isAuctionItem = curItemDict.get("IsAuctionItem", 0)
    if not __checkMailItemIDCount(itemID, itemCount, isAuctionItem):
        return
    return curItemDict
def __checkMailItemList(addItemList):
    ## 检查是否合法的邮件物品列表
    addItemDictList = []
    for itemInfo in addItemList:
        if isinstance(itemInfo, dict):
            if not __checkMailItemDict(itemInfo):
                return False, []
            addItemDictList.append(itemInfo)
            continue
        if (isinstance(itemInfo, list) or isinstance(itemInfo, tuple)) and (len(itemInfo) == 3 or len(itemInfo) == 2):
            itemID, itemCount = itemInfo[:2]
            isAuctionItem = itemInfo[2] if len(itemInfo) > 2 else 0
            if not __checkMailItemIDCount(itemID, itemCount, isAuctionItem):
                return False, []
            addItemDict = {}
            addItemDict['ItemID'] = itemID
            addItemDict['Count'] = itemCount
            addItemDict['IsAuctionItem'] = isAuctionItem
            addItemDictList.append(addItemDict)
        else:
            return False, []
    return True, addItemDictList
#  此处货币playerIDList发放统一,如根据玩家不同而变,则应需修改
## 功能发放物品补偿/奖励邮件
@@ -136,7 +413,7 @@
#  @return GUID
#  @remarks addItemList支持append字典
def SendPersonalItemMail(title, content, limitTime, playerIDList, addItemList, gold = 0, goldPaper = 0, silver = 0, 
                         detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
                         detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False, mailType=0):
    if not playerIDList:
        return ""
    
@@ -148,45 +425,42 @@
    if not curServerTime or curServerTime >= limitTime:
        GameWorld.DebugLog("功能发放物品补偿/奖励邮件,领取时间已超时,默认不添加!LimitTime=%s" % limitTime)
        return ""
    isOK, addItemDictList = __checkMailItemList(addItemList)
    if not isOK:
        GameWorld.ErrLog("发送个人邮件错误: title=%s,content=%s,playerIDList=%s,addItemList=%s,gold=%s,goldPaper=%s,silver=%s,detail=%s,moneySource=%s,crossMail=%s"
                         % (title, content, playerIDList, addItemList, gold, goldPaper, silver, detail, moneySource, crossMail))
        return
    
    addItemDictList = []
    for itemInfo in addItemList:
        if isinstance(itemInfo, dict):
            addItemDictList.append(itemInfo)
            continue
        if len(itemInfo) == 3:
            itemID, itemCnt, isAuctionItem = itemInfo
        else:
            continue
        addItemDict = {}
        addItemDict['ItemID'] = itemID
        addItemDict['Count'] = itemCnt
        addItemDict['IsAuctionItem'] = isAuctionItem
        addItemDictList.append(addItemDict)
    gold = min(gold, ShareDefine.Def_UpperLimit_DWord)
    goldPaper = min(goldPaper, ShareDefine.Def_UpperLimit_DWord)
    silver = min(silver, ShareDefine.Def_UpperLimit_DWord)
    perMailItemCnt = IpyGameDataPY.GetFuncCfg("MailMaxItemCnt")
    mailCnt = max(1, int(math.ceil(len(addItemDictList)/float(perMailItemCnt)))) # 一封邮件最多5个物品
    for i in xrange(mailCnt):
        if i != 0:
            gold, goldPaper, silver = 0, 0, 0 # 拆分后非第一封的邮件货币归零,防止重复给货币奖励
        startIndex = i*perMailItemCnt
        GUID = str(uuid.uuid1())
        AddPersonalItem(GUID, addItemDictList[startIndex:startIndex + perMailItemCnt], playerIDList, 
                                           limitTime, "%s<$_$>%s<$_$>%s" % (ChConfig.Def_Mail_SenderSys, title, content),
                                           gold, goldPaper, silver, detail, moneySource)
                                           limitTime, GetMailText(title, content, mailType),
                                           gold, goldPaper, silver, detail, moneySource, crossMail)
    return GUID
## 发送纯文字个人补偿
#  @param limitTime 可以传空
#  @return None
def SendPersonalAsTextMail(PlayerID, title, content, limitTime):
def SendPersonalAsTextMail(PlayerID, title, content, limitTime, mailType=0):
    if GameWorld.IsCrossServer():
        return
    GUID = str(uuid.uuid1())
    PyAddPersonalCompensation(GUID, PlayerID, GameWorld.GetCurrentDataTimeStr(), limitTime, 
                              "%s<$_$>%s<$_$>%s" % (ChConfig.Def_Mail_SenderSys,title, content))
                              GetMailText(title, content, mailType))
    return
def GetMailText(title, content, mailType=0, sender=ChConfig.Def_Mail_SenderSys):
    ## 获取邮件字段 Text 内容
    return "%s<$_$>%s<$_$>%s<$_$>%s" % (sender, title, content, mailType)
def GetEntireCompensationInfo(checkState, limitLVType, limitLV):
    return checkState * 1000000 + limitLVType * 100000 + limitLV
@@ -196,8 +470,52 @@
    limitLVType = (mailInfo - checkState * 1000000) / 100000
    return checkState, limitLVType, limitLV
def QueryCompensationPersonalInfo(playerID):
    '''个人补偿邮件查询
    '''
    retList = []
    compensationMgr = GameWorld.GetCompensationMgr()
    tempSign = "<MailTemplate>"
    tempSignEnd = "</MailTemplate>"
    curPersonalCount = compensationMgr.GetPersonalCompensationCount(playerID)
    GameWorld.DebugLog("QueryCompensationPersonalInfo %s" % curPersonalCount)
    for i in xrange(curPersonalCount):
        compensation = compensationMgr.PersonalCompensationAt(playerID, i)
        contentList = compensation.Text.split("<$_$>")
        if len(contentList) < 3:
            continue
        sender, title, content = contentList[:3]
        mailType = GameWorld.ToIntDef(contentList[3]) if len(contentList) > 3 else 0
        if tempSign in content and tempSignEnd in content:
            title = content[content.index(tempSign) + len(tempSign):content.index(tempSignEnd)]
            content = ""
        GUID = compensation.GUID
        itemList = []
        curGUIDItemCount = compensationMgr.FindItemCount(GUID)
        for i in xrange(curGUIDItemCount):
            curItem = compensationMgr.FindItemAt(GUID, i)
            itemID = curItem.ItemID
            if not itemID:
                continue
            itemList.append([itemID, curItem.Count, curItem.IsBind, curItem.UserData])
        recState = compensationMgr.FindPlayerRecState(playerID, GUID)
        infoDict = {"GUID":GUID, "Gold":compensation.Gold, "GoldPaper":compensation.GoldPaper, "Silver":compensation.Silver,
                    "Sender":sender, "Title":title, "Content":content, "RecState":recState, "MailType":mailType,
                    "CreateTime":compensation.CreateTime, "LimitTime":compensation.LimitTime, "ItemList":itemList}
        retList.append(infoDict)
    return retList
def QueryCompensationInfo(fromDate, toDate, guid, searchTitle, searchContent, searchState=None, maxCount=10):
    '''补偿邮件查询
    '''全服补偿邮件查询
    '''
    
    compensationMgr = GameWorld.GetCompensationMgr()
@@ -238,9 +556,10 @@
        return
    
    contentList = compensation.Text.split("<$_$>")
    if len(contentList) != 3:
    if len(contentList) < 3:
        return
    sender, title, content = contentList
    sender, title, content = contentList[:3]
    mailType = GameWorld.ToIntDef(contentList[3]) if len(contentList) > 3 else 0
    
    if searchTitle and searchTitle not in title:
        return
@@ -262,9 +581,9 @@
            continue
        itemList.append([itemID, curItem.Count, curItem.IsBind, curItem.UserData])
        
    compensationDict = {"GUID":GUID, "CheckState":checkState, "LimitLVType":limitLVType, "LimitLV":limitLV,
    compensationDict = {"GUID":GUID, "CheckState":checkState, "LimitLVType":limitLVType, "LimitLV":limitLV, "MailType":mailType,
                        "Gold":compensation.Gold, "GoldPaper":compensation.GoldPaper, "Silver":compensation.Silver,
                        "PlayerJob":compensation.PlayerJob, "Sender":sender, "Title":title, "Content":content,
                        "PlayerJob":compensation.PlayerJob, "Sender":sender, "Title":title, "Content":content, "OnlyServerID":compensation.ServerID,
                        "CreateTime":compensation.CreateTime, "LimitTime":compensation.LimitTime, "ItemList":itemList}
    return compensationDict
@@ -326,6 +645,47 @@
            
    return successGUIDList
def SendEntireMail(mailTypeKey, getDays, limitLV, limitLVType, addItemList=[], paramList=[], \
                   gold=0, goldPaper=0, silver=0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail, GUID="", mailType=0):
    ''' 发送全服邮件
    @param mailTypeKey: 邮件模板key
    @param getDays: 有效天数
    @param limitLV: 领取最低等级限制
    @param limitLVType: 等级不足的升级后是否可领 0-不可,1-可以
    '''
    if not mailTypeKey or getDays <= 0:
        return
    isOK, addItemDictList = __checkMailItemList(addItemList)
    if not isOK:
        GameWorld.ErrLog("发送全服邮件错误: mailTypeKey=%s,addItemList=%s,paramList=%s,gold=%s,goldPaper=%s,silver=%s,detail=%s,moneySource=%s"
                     % (mailTypeKey, addItemList, paramList, gold, goldPaper, silver, detail, moneySource))
        return
    gold = min(gold, ShareDefine.Def_UpperLimit_DWord)
    goldPaper = min(goldPaper, ShareDefine.Def_UpperLimit_DWord)
    silver = min(silver, ShareDefine.Def_UpperLimit_DWord)
    if not GUID:
        GUID = str(uuid.uuid1())
    limitTime = str(GameWorld.GetDatetimeByDiffDays(getDays))
    limitTime = limitTime.split(".")[0]
    sender = ChConfig.Def_Mail_SenderSys
    title = ""
    content = "<MailTemplate>%s</MailTemplate>%s" % (mailTypeKey, json.dumps(paramList, ensure_ascii=False))
    checkState = 0 # 邮件审核状态,为兼容老邮件,默认0-已审核,1-未审核
    mailInfo = GetEntireCompensationInfo(checkState, limitLVType, limitLV)
    PlayerJob = 127 # 默认全职业可领
    serverID = 0 # 默认所有服务器ID
    AddEntireItem(GUID, addItemDictList, limitTime, mailInfo, PlayerJob, GetMailText(title, content, mailType, sender),
                  gold, goldPaper, silver, detail, serverID)
    return
## 添加全服补偿
#  @param addItemDictList, LimitTime, mailInfo, PlayerJob,  Text 
#  @return None
@@ -341,11 +701,16 @@
    #添加补偿包的所有物品
    for addItemDict in addItemDictList:
        curItemData = MakeCompensationItem(addItemDict)
        if not curItemData:
            return
        GameWorld.GetCompensationMgr().AddCompensationItem(GUID, curItemData)
        
    createTime = GameWorld.GetCurrentDataTimeStr()
    
    #添加全服领取补偿条件
    gold = min(gold, ShareDefine.Def_UpperLimit_DWord)
    goldPaper = min(goldPaper, ShareDefine.Def_UpperLimit_DWord)
    silver = min(silver, ShareDefine.Def_UpperLimit_DWord)
    GameWorld.GetCompensationMgr().AddEntireCompensationItem(GUID, createTime, LimitTime, mailInfo, 
                                                             PlayerJob, Text, gold, goldPaper, silver, serverID)
    checkState, limitLVType, limitLV = ParseEntireCompensationInfo(mailInfo)
@@ -387,14 +752,19 @@
    GameWorld.DebugLog("新增个人邮件: totalCount=%s,maxMailCount=%s" % (totalCount, maxMailCount), PlayerID)
    if delCount > 0:
        GameWorld.Log("个人邮件达到上限,需要删除!delCount=%s" % (delCount), PlayerID)
        delGUIDList = GetPlayerDelMailGUIDList(PlayerID)
        #先取得要删除的GUID
        delGUIDs = []
        for i in xrange(delCount):
            curIpyPersonalData = GameWorld.GetCompensationMgr().PersonalCompensationAt(PlayerID, i)
            delGUIDs.append(curIpyPersonalData.GUID)
        for _ in xrange(delCount):
            if not delGUIDList:
                break
            delGUID = delGUIDList.pop(0)
            curIpyPersonalData = GameWorld.GetCompensationMgr().FindPersonalCompensation(PlayerID, delGUID)
            if curIpyPersonalData.GUID == delGUID:
                delGUIDs.append(curIpyPersonalData.GUID)
            
        for guid in delGUIDs:
            ClearPersonalCompensation(PlayerID, guid)
            ClearPersonalCompensation(PlayerID, guid, "MaxCountLimiit")
            GameWorld.Log("    DeletePersonalCompensation GUID = %s" % guid, PlayerID)
            
            if curPlayer:
@@ -403,23 +773,52 @@
            
    #此处没有下发通知
    mailType = moneySource - ChConfig.Def_GiveMoney_Unknown # type类型为byte,存值时需要处理下
    gold = min(gold, ShareDefine.Def_UpperLimit_DWord)
    goldPaper = min(goldPaper, ShareDefine.Def_UpperLimit_DWord)
    silver = min(silver, ShareDefine.Def_UpperLimit_DWord)
    GameWorld.GetCompensationMgr().AddPersonalCompensation(GUID, PlayerID, CreateTime,
                                                           LimitTime, Text, mailType, gold, goldPaper, silver)
    if PlayerID in PyGameData.g_playerDelMailGUIDDict:
        guidList = PyGameData.g_playerDelMailGUIDDict[PlayerID]
        guidList.append(GUID)
    return
def GetPlayerDelMailGUIDList(playerID):
    ## 获取待删除的个人邮件GUID列表
    if playerID not in PyGameData.g_playerDelMailGUIDDict:
        timeGUIDList = []
        curPersonalCount = GameWorld.GetCompensationMgr().GetPersonalCompensationCount(playerID)
        for i in xrange(curPersonalCount):
            curMail = GameWorld.GetCompensationMgr().PersonalCompensationAt(playerID, i)
            timeGUIDList.append([curMail.CreateTime, curMail.GUID])
        timeGUIDList.sort() # 按创建时间升序排序
        delGUIDList = []
        for _, guid in timeGUIDList:
            if guid not in delGUIDList:
                delGUIDList.append(guid)
        PyGameData.g_playerDelMailGUIDDict[playerID] = delGUIDList
    return PyGameData.g_playerDelMailGUIDDict[playerID]
#  此处货币playerIDList发放统一,如根据玩家不同而变,则应需修改
## 添加个人补偿
#  @param addItemDict, PlayerIDList, LimitTime, Text 
#  @return None
def AddPersonalItem(GUID, addItemDictList, PlayerIDList, LimitTime, Text, gold = 0, goldPaper = 0, silver = 0, detail="", moneySource=ChConfig.Def_GiveMoney_Mail):
def AddPersonalItem(GUID, addItemDictList, PlayerIDList, LimitTime, Text, gold = 0, goldPaper = 0, silver = 0,
                    detail="", moneySource=ChConfig.Def_GiveMoney_Mail, crossMail=False):
    if GameWorld.IsCrossServer():
        if crossMail:
            __AddPlayerCrossMail(addItemDictList, PlayerIDList, LimitTime, Text, gold, goldPaper, silver, detail, moneySource)
        return
    GameWorld.DebugLog("Compensation### AddPersonalItem GUID:%s ItemDict:\n%s "%(GUID, addItemDictList))
    
    #添加补偿包的所有物品
    for addItemDict in addItemDictList:
        curItemData = MakeCompensationItem(addItemDict)
        if not curItemData:
            return
        GameWorld.GetCompensationMgr().AddCompensationItem(GUID, curItemData)
        
    #offlinePlayerIDList = []
@@ -531,24 +930,38 @@
# 提取接收邮件下发
def NotifyPlayerCompensation(curPlayer):
    RequestToGetCrossMail(curPlayer)
    notifyList = SeekPlayerCompensation(curPlayer)
    SyncQueryCompensationResult(curPlayer, notifyList)
    return
# 删除个人邮件表 状态表,同一个GUID 可以多人领取,不能同时删除物品
def ClearPersonalCompensation(curPlayerID, curGUID):
def ClearPersonalCompensation(curPlayerID, curGUID, eventName=""):
    GameWorld.GetCompensationMgr().DeletePersonalCompensation(curPlayerID, curGUID)
    GameWorld.GetCompensationMgr().DeletePlayerCompensationRec(curPlayerID, curGUID)
    
    #存在多人邮件的情况,故删除物品需检查是否最后一个领取者才可删除
    if GameWorld.GetCompensationMgr().GetPersonalCountByGUID(curGUID) == 0:
        GameWorld.GetCompensationMgr().DeleteCompensationItem(curGUID)
    if curPlayerID in PyGameData.g_playerDelMailGUIDDict:
        guidList = PyGameData.g_playerDelMailGUIDDict[curPlayerID]
        if curGUID in guidList:
            guidList.remove(curGUID)
    if eventName:
        # 有特殊操作删除的才记录,常规领取删除的默认无eventName,不记录
        DataRecordPack.DR_DelPersonalCompensation(curPlayerID, curGUID, eventName)
    return
# 设置领取状态
def SetPrizeState(curPlayerID, GUID, prizeState, readState):
    state = readState*10 + prizeState
    GameWorld.GetCompensationMgr().AddPlayerRec(curPlayerID, GUID, state)
    return
def GetPrizeGetState(curPlayerID, GUID):
    ## 获取领取状态
    return GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, GUID) % 10
##查找玩家可领取的补偿列表
#  @param curPlayer 
@@ -585,9 +998,9 @@
        states = GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, curRequire.GUID)
        readState = states/10    # 可读       
        curState = states%10    # 领取状态
        if curState in (Disable_State, Yet_State):
        if curState in (Disable_State, Del_State):
            #不可领 或 已领
            #GameWorld.DebugLog("该玩家当前全服邮件状态不下发: curState=%s,GUID=%s" % (curState, curRequire.GUID), curPlayerID)
            continue
        
        limitTime = datetime.datetime.strptime(curRequire.LimitTime, ChConfig.TYPE_Time_Format)
@@ -596,6 +1009,11 @@
            SetPrizeState(curPlayerID, curRequire.GUID, Disable_State, readState)
            continue
        
        if limitLVType == LimitLVType_Not and curPlayer.GetCreateRoleTime() > curRequire.CreateTime:
            #GameWorld.DebugLog("升级后不可领取的邮件发送时间后创角的玩家默认不可领取! CreateRoleTime=%s > %s" % (curPlayer.GetCreateRoleTime(), curRequire.CreateTime))
            SetPrizeState(curPlayerID, curRequire.GUID, Disable_State, readState)
            continue
        if limitLV > curLV:
            #等级不足
            if limitLVType == LimitLVType_Not:
@@ -608,8 +1026,13 @@
                SetPrizeState(curPlayerID, curRequire.GUID, Disable_State, readState)
                continue
            
        if curRequire.ServerID and curRequire.ServerID != GameWorld.GetPlayerServerID(curPlayer):
            # 指定服务器邮件
            SetPrizeState(curPlayerID, curRequire.GUID, Disable_State, readState)
            continue
        #可以用的奖励
        if Enable_State != curState:
        if Enable_State != curState and curState != Yet_State:
            SetPrizeState(curPlayerID, curRequire.GUID, Enable_State, readState)
        allList.append((curRequire.GUID, curRequire.Text, curRequire.CreateTime,
                        curRequire.Gold, curRequire.GoldPaper, curRequire.Silver))
@@ -632,7 +1055,11 @@
        subPack.Gold = gold
        subPack.GoldPaper = goldPaper
        subPack.Silver = silver 
        subPack.IsRead = GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayer.GetPlayerID(), GUID)/10
        resState = GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayer.GetPlayerID(), GUID)
        if resState % 10 == Yet_State:
            subPack.IsRead = Yet_State # 已领取
        else:
            subPack.IsRead = resState / 10 # 是否已读
        for index in xrange(curGUIDItemCount):
            curItem = GameWorld.GetCompensationMgr().FindItemAt(GUID, index)
            subPackItem = ChPyNetSendPack.tagGCCompensationItem()
@@ -646,6 +1073,24 @@
        sendPack.PackList.append(subPack)
    sendPack.Count = len(sendPack.PackList)
    NetPackCommon.SendFakePack(curPlayer, sendPack)
    return
def SyncQueryCompensationResultByGUID(curPlayer, notifyGUIDList):
    curPlayerID = curPlayer.GetPlayerID()
    notifyList = []
    compensationMgr = GameWorld.GetCompensationMgr()
    for GUID in notifyGUIDList:
        findCompensation = compensationMgr.FindPersonalCompensation(curPlayerID, GUID)
        if findCompensation.PlayerID != curPlayerID:
            findCompensation = compensationMgr.FindEntireCompensation(GUID)
            if findCompensation.GUID != GUID:
                # 找不到邮件
                continue
        notifyList.append((findCompensation.GUID, findCompensation.Text, findCompensation.CreateTime,
                           findCompensation.Gold, findCompensation.GoldPaper, findCompensation.Silver))
    if not notifyList:
        return
    SyncQueryCompensationResult(curPlayer, notifyList)
    return
##03 03 玩家请求领取补偿#tagMGRequestCompensation
@@ -668,7 +1113,7 @@
    GameWorld.DebugLog("Compensation### OnMGRequestCompensation myPlayerID %s GUID %s"%(myPlayerID, GUID))
    
    compensationType, curEntireRequire = CheckRequestCompensation(curPlayer, GUID) 
    if compensationType == Unknow_CompensationType:
    if compensationType == Unknow_CompensationType or not curEntireRequire:
        #领取失败
        curPlayer.SetDict(Def_RequestState, 0)#解锁
        GameWorld.DebugLog("Compensation### OnMGRequestCompensation no found")
@@ -691,6 +1136,15 @@
def CheckRequestCompensation(curPlayer, GUID, isPersonnal = True):
    curPlayerID = curPlayer.GetID()
    
    states = GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, GUID)
    readState = states/10    # 可读
    curState = states%10    # 领取状态
    if curState in (Disable_State, Yet_State, Del_State):
        #不可领 或 已领
        #GameWorld.DebugLog("当前邮件不可领取或已领: states=%s,curState=%s,GUID=%s" % (states, curState, GUID), curPlayerID)
        return Unknow_CompensationType, None
    if isPersonnal:
        #在个人补偿中
        curPersonalCompensation = GameWorld.GetCompensationMgr().FindPersonalCompensation(curPlayerID, GUID)
@@ -700,13 +1154,6 @@
        
    #---------
    #在全服补偿中
    states = GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, GUID)
    readState = states/10    # 可读
    curState = states%10    # 领取状态
    if curState in (Disable_State, Yet_State):
        #不可领 或 已领
        return Unknow_CompensationType, None
    curEntireRequire = GameWorld.GetCompensationMgr().FindEntireCompensation(GUID)
    if curEntireRequire.GUID != GUID:
        #不再全服补偿中,删除记录
@@ -760,8 +1207,11 @@
#  @return None
def SendGMRequestCompensationResult(routeIndex, mapID, curPlayer, GUID, compensationType, curEntireRequire):
    
    Text, gold, goldPaper, silver, moneySource, createTime = curEntireRequire.Text, curEntireRequire.Gold, \
        curEntireRequire.GoldPaper, curEntireRequire.Silver, curEntireRequire.Type, curEntireRequire.CreateTime
    Text, gold, goldPaper, silver, createTime = curEntireRequire.Text, curEntireRequire.Gold, \
        curEntireRequire.GoldPaper, curEntireRequire.Silver, curEntireRequire.CreateTime
    # 全服邮件没有 Type 字段
    moneySource = curEntireRequire.Type if compensationType == Personal_CompensationType else 0
    sendPack = ChGameToMapPyPack.tagGMRequestCompensationResult() 
    sendPack.PlayerID = curPlayer.GetID()
    sendPack.CompensationType = compensationType
@@ -810,15 +1260,20 @@
##玩家领取补偿物品发放成功
#  @param curPlayer, GUID
#  @return None
def GiveCompensationSuccess(curPlayer, GUID, CompensationType):
def GiveCompensationSuccess(curPlayer, GUID, CompensationType, isDel=False):
    curPlayerID = curPlayer.GetID()
    NotifyCompensationResult(curPlayer, GUID, 1)
    SetPrizeState(curPlayerID, GUID, Yet_State, Read_State_Yes) # 设置为已领取
    #流向记录
    DataRecordPack.DR_GiveCompensationSuccess(curPlayerID, GUID)
    if not isDel:
        SyncQueryCompensationResultByGUID(curPlayer, [GUID])
        return
    NotifyCompensationResult(curPlayer, GUID, 1)
    #全服奖励领取记录变更为已领取
    if CompensationType == Entire_CompensationType:
        GameWorld.GetCompensationMgr().AddPlayerRec(curPlayerID, GUID, 10 + Yet_State)
        SetPrizeState(curPlayerID, GUID, Del_State, Read_State_Yes)
        return
    #个人奖励领取条目执行删除
    if CompensationType == Personal_CompensationType:
@@ -853,11 +1308,51 @@
    return
##清理超时30天的个人邮件, 否则流失玩家在不断合服情况下数据会累积
# 个人邮件暂无过期时间设定,只有30天清理逻辑,以创建时间为准
#  @param None
#  @return None
def ClearUpPersonalCompensation():
    #校验过期补偿
    tempSign = "<MailTemplate>"
    tempSignEnd = "</MailTemplate>"
    curTime = datetime.datetime.today()
    needClearGUIDList = []
    allCnt = GameWorld.GetCompensationMgr().GetAllPersonalCompensationCount()
    for i in xrange(allCnt):
        curMail = GameWorld.GetCompensationMgr().AtAllPersonalCompensation(i)
        mailText = curMail.Text
        # 通知类模板邮件,过天可直接删除
        if tempSign in mailText and tempSignEnd in mailText and CheckCanDelCompensation(curMail, curMail.GUID, curMail.PlayerID):
            tempKey = mailText[mailText.index(tempSign) + len(tempSign):mailText.index(tempSignEnd)]
            notClearMailKeyList = IpyGameDataPY.GetFuncEvalCfg("MailSet", 1)
            if tempKey not in notClearMailKeyList:
                #GameWorld.DebugLog("过天删除无附件通知类邮件模板! tempKey=%s %s, %s" % (tempKey, curMail.PlayerID, curMail.GUID))
                needClearGUIDList.append([curMail.PlayerID, curMail.GUID])
                player = GameWorld.GetPlayerManager().FindPlayerByID(curMail.PlayerID)
                if player and player.GetInitOK():
                    NotifyCompensationResult(player, curMail.GUID, 1)
                continue
        # 超过接收邮件30天则完全删除此邮件
        limitTime = datetime.datetime.strptime(curMail.CreateTime, ChConfig.TYPE_Time_Format) + datetime.timedelta(days = 30)
        if limitTime < curTime:
            needClearGUIDList.append([curMail.PlayerID, curMail.GUID])
    GameWorld.Log("ClearUpPersonalCompensation count=%s"%len(needClearGUIDList))
    #删除过期补偿信息, 没有主动通知在线玩家
    for playerID, GUID in needClearGUIDList:
        ClearPersonalCompensation(playerID, GUID, "Timeout")
    return
## 清理超时补偿, 个人邮件在超过上限后才会自动删除
#  @param None
#  @return None
def ClearUpTimeOutCompensation():
    
    ClearUpPersonalCompensation()
    ClearUpEntireCompensation()
    return
@@ -913,7 +1408,13 @@
    #在个人补偿中
    curPersonalCompensation = GameWorld.GetCompensationMgr().FindPersonalCompensation(curPlayerID, GUID)
    if curPersonalCompensation.PlayerID == curPlayerID:
        ClearPersonalCompensation(curPlayerID, GUID)
        if not CheckCanDelCompensation(curPersonalCompensation, GUID, curPlayerID):
            #有附件不可删除
            GameWorld.DebugLog("该个人邮件不可删除: %s" % GUID, curPlayerID)
            NotifyCompensationResult(curPlayer, GUID, 0)
            return
        ClearPersonalCompensation(curPlayerID, GUID, "ClientDel")
        NotifyCompensationResult(curPlayer, GUID, 1)
        #GameWorld.DebugLog("个人补偿中OnDelCompensation:%s"%GUID)
        return
@@ -928,12 +1429,34 @@
    #全服邮件
    curEntireRequire = GameWorld.GetCompensationMgr().FindEntireCompensation(GUID)
    if curEntireRequire.GUID == GUID:
        SetPrizeState(curPlayerID, GUID, Disable_State, GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, GUID)/10)
        if not CheckCanDelCompensation(curEntireRequire, GUID, curPlayerID):
            #有附件不可删除
            GameWorld.DebugLog("该全服邮件不可删除: %s" % GUID, curPlayerID)
            NotifyCompensationResult(curPlayer, GUID, 0)
            return
        SetPrizeState(curPlayerID, GUID, Del_State, GameWorld.GetCompensationMgr().FindPlayerRecState(curPlayerID, GUID)/10)
        NotifyCompensationResult(curPlayer, GUID, 1)
        #GameWorld.DebugLog("全服邮件OnDelCompensation:%s"%GUID)
        return
    NotifyCompensationResult(curPlayer, GUID, 0)
# 有附件的情况玩家不可主动删除邮件,避免误操作;系统可删除不用调用此接口
def CheckCanDelCompensation(mailObj, GUID, playerID=0):
    if playerID:
        if GetPrizeGetState(playerID, GUID) == Yet_State:
            #GameWorld.DebugLog("已领取的邮件可删除: %s" % GUID, playerID)
            return True
    if mailObj.Gold or mailObj.GoldPaper or mailObj.Silver:
        # 有附加货币不可删除
        return False
    if GameWorld.GetCompensationMgr().FindItemCount(GUID):
        # 有附件物品不可删除
        return False
    return True
        
# 邮件删除情况   
def NotifyCompensationResult(curPlayer, GUID, result):