From eb2b495812782c219d963559e840d1be46c5c846 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期三, 14 五月 2025 11:46:47 +0800
Subject: [PATCH] 16 卡牌服务端(邮件功能;)

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py                     |   11 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py                     |   64 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py                 |  369 ++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBFamily.py          |    5 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerMail.py               |  515 +++++++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py                    |   20 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini                             |   12 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DataRecordPack.py                  |   24 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py                 |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py                       |    8 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBMail.py            |  418 ++++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py |    2 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py       |    4 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBStruct.py                     |  402 +++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Mail.py                |  158 ++++
 15 files changed, 2,003 insertions(+), 11 deletions(-)

diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
index e8d28a9..572a9ab 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/PyNetPack.ini
@@ -34,6 +34,18 @@
 PacketSubCMD_6=0x08
 PacketCallFunc_6=OnItemTimeout
 
+;邮件
+[PlayerMail]
+ScriptName = Player\PlayerMail.py
+Writer = hxp
+Releaser = hxp
+RegType = 0
+RegisterPackCount = 1
+
+PacketCMD_1=0xA5
+PacketSubCMD_1=0x37
+PacketCallFunc_1=OnRequestMail
+
 ;法器
 [PlayerFaQi]
 ScriptName = Player\PlayerFaQi.py
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
index 763fff9..fa57fb5 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetPack.py
@@ -14125,6 +14125,70 @@
 
 
 #------------------------------------------------------
+# A5 37 请求邮件操作 #tagCMRequestMail
+
+class  tagCMRequestMail(Structure):
+    Head = tagHead()
+    GUID = ""    #(char GUID[36])//邮件GUID,可传空,默认针对个人邮件的批量处理,如批量领取、批量删除已领邮件等;全服邮件暂时限制只能单封邮件处理
+    ReqType = 0    #(BYTE ReqType)//0-设置已读,1-领取邮件,2-删除邮件
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA5
+        self.Head.SubCmd = 0x37
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.GUID,_pos = CommFunc.ReadString(_lpData, _pos,36)
+        self.ReqType,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA5
+        self.Head.SubCmd = 0x37
+        self.GUID = ""
+        self.ReqType = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 36
+        length += 1
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteString(data, 36, self.GUID)
+        data = CommFunc.WriteBYTE(data, self.ReqType)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                GUID:%s,
+                                ReqType:%d
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.GUID,
+                                self.ReqType
+                                )
+        return DumpString
+
+
+m_NAtagCMRequestMail=tagCMRequestMail()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagCMRequestMail.Head.Cmd,m_NAtagCMRequestMail.Head.SubCmd))] = m_NAtagCMRequestMail
+
+
+#------------------------------------------------------
 # A5 68 请求寻宝 #tagCMRequestTreasure
 
 class  tagCMRequestTreasure(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
index 693db8a..aa30ab6 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -27626,6 +27626,375 @@
 
 
 #------------------------------------------------------
+# A3 62 邮件列表 #tagMCMailList
+
+class  tagMCMailItem(Structure):
+    ItemID = 0    #(DWORD ItemID)//物品ID
+    Count = 0    #(DWORD Count)//数量
+    IsBind = 0    #(BYTE IsBind)//是否绑定
+    UserDataLen = 0    #(WORD UserDataLen)
+    UserData = ""    #(String UserData)//自定义数据	
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.ItemID,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadDWORD(_lpData, _pos)
+        self.IsBind,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.UserDataLen,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.UserData,_pos = CommFunc.ReadString(_lpData, _pos,self.UserDataLen)
+        return _pos
+
+    def Clear(self):
+        self.ItemID = 0
+        self.Count = 0
+        self.IsBind = 0
+        self.UserDataLen = 0
+        self.UserData = ""
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 4
+        length += 4
+        length += 1
+        length += 2
+        length += len(self.UserData)
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteDWORD(data, self.ItemID)
+        data = CommFunc.WriteDWORD(data, self.Count)
+        data = CommFunc.WriteBYTE(data, self.IsBind)
+        data = CommFunc.WriteWORD(data, self.UserDataLen)
+        data = CommFunc.WriteString(data, self.UserDataLen, self.UserData)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                ItemID:%d,
+                                Count:%d,
+                                IsBind:%d,
+                                UserDataLen:%d,
+                                UserData:%s
+                                '''\
+                                %(
+                                self.ItemID,
+                                self.Count,
+                                self.IsBind,
+                                self.UserDataLen,
+                                self.UserData
+                                )
+        return DumpString
+
+
+class  tagMCMail(Structure):
+    GUID = ""    #(char GUID[36])//邮件GUID
+    Type = 0    #(BYTE Type)//邮件类型,暂时默认0
+    CreateTime = ""    #(char CreateTime[30])//创建时间
+    LimitDays = 0    #(BYTE LimitDays)//有效天数
+    TitleLen = 0    #(BYTE TitleLen)
+    Title = ""    #(String Title)//标题
+    TextLen = 0    #(WORD TextLen)
+    Text = ""    #(String Text)//内容
+    MailState = 0    #(BYTE MailState)//邮件状态: 0-未知;1-未读;2-已读;3-已领;
+    Count = 0    #(BYTE Count)//物品数
+    Items = list()    #(vector<tagMCMailItem> Items)//物品信息
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.GUID,_pos = CommFunc.ReadString(_lpData, _pos,36)
+        self.Type,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.CreateTime,_pos = CommFunc.ReadString(_lpData, _pos,30)
+        self.LimitDays,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.TitleLen,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Title,_pos = CommFunc.ReadString(_lpData, _pos,self.TitleLen)
+        self.TextLen,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        self.Text,_pos = CommFunc.ReadString(_lpData, _pos,self.TextLen)
+        self.MailState,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.Count):
+            temItems = tagMCMailItem()
+            _pos = temItems.ReadData(_lpData, _pos)
+            self.Items.append(temItems)
+        return _pos
+
+    def Clear(self):
+        self.GUID = ""
+        self.Type = 0
+        self.CreateTime = ""
+        self.LimitDays = 0
+        self.TitleLen = 0
+        self.Title = ""
+        self.TextLen = 0
+        self.Text = ""
+        self.MailState = 0
+        self.Count = 0
+        self.Items = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 36
+        length += 1
+        length += 30
+        length += 1
+        length += 1
+        length += len(self.Title)
+        length += 2
+        length += len(self.Text)
+        length += 1
+        length += 1
+        for i in range(self.Count):
+            length += self.Items[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, 36, self.GUID)
+        data = CommFunc.WriteBYTE(data, self.Type)
+        data = CommFunc.WriteString(data, 30, self.CreateTime)
+        data = CommFunc.WriteBYTE(data, self.LimitDays)
+        data = CommFunc.WriteBYTE(data, self.TitleLen)
+        data = CommFunc.WriteString(data, self.TitleLen, self.Title)
+        data = CommFunc.WriteWORD(data, self.TextLen)
+        data = CommFunc.WriteString(data, self.TextLen, self.Text)
+        data = CommFunc.WriteBYTE(data, self.MailState)
+        data = CommFunc.WriteBYTE(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.Items[i].GetLength(), self.Items[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                GUID:%s,
+                                Type:%d,
+                                CreateTime:%s,
+                                LimitDays:%d,
+                                TitleLen:%d,
+                                Title:%s,
+                                TextLen:%d,
+                                Text:%s,
+                                MailState:%d,
+                                Count:%d,
+                                Items:%s
+                                '''\
+                                %(
+                                self.GUID,
+                                self.Type,
+                                self.CreateTime,
+                                self.LimitDays,
+                                self.TitleLen,
+                                self.Title,
+                                self.TextLen,
+                                self.Text,
+                                self.MailState,
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+class  tagMCMailList(Structure):
+    Head = tagHead()
+    IsServerMail = 0    #(BYTE IsServerMail)//是否全服邮件,如公告、维护、更新、补偿等重要邮件
+    Count = 0    #(WORD Count)
+    MailList = list()    #(vector<tagMCMail> MailList)//邮件列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x62
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.IsServerMail,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        for i in range(self.Count):
+            temMailList = tagMCMail()
+            _pos = temMailList.ReadData(_lpData, _pos)
+            self.MailList.append(temMailList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x62
+        self.IsServerMail = 0
+        self.Count = 0
+        self.MailList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        length += 2
+        for i in range(self.Count):
+            length += self.MailList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.IsServerMail)
+        data = CommFunc.WriteWORD(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.MailList[i].GetLength(), self.MailList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                IsServerMail:%d,
+                                Count:%d,
+                                MailList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.IsServerMail,
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCMailList=tagMCMailList()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCMailList.Head.Cmd,m_NAtagMCMailList.Head.SubCmd))] = m_NAtagMCMailList
+
+
+#------------------------------------------------------
+# A3 63 邮件状态变更 #tagMCMailStateChange
+
+class  tagMCMailState(Structure):
+    GUID = ""    #(char GUID[36])//邮件GUID
+    MailState = 0    #(BYTE MailState)//邮件状态: 0-未知;1-未读;2-已读;3-已领;4-已删
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        self.GUID,_pos = CommFunc.ReadString(_lpData, _pos,36)
+        self.MailState,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        return _pos
+
+    def Clear(self):
+        self.GUID = ""
+        self.MailState = 0
+        return
+
+    def GetLength(self):
+        length = 0
+        length += 36
+        length += 1
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, 36, self.GUID)
+        data = CommFunc.WriteBYTE(data, self.MailState)
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                GUID:%s,
+                                MailState:%d
+                                '''\
+                                %(
+                                self.GUID,
+                                self.MailState
+                                )
+        return DumpString
+
+
+class  tagMCMailStateChange(Structure):
+    Head = tagHead()
+    Count = 0    #(WORD Count)
+    MailList = list()    #(vector<tagMCMailState> MailList)//邮件列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x63
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.Count,_pos = CommFunc.ReadWORD(_lpData, _pos)
+        for i in range(self.Count):
+            temMailList = tagMCMailState()
+            _pos = temMailList.ReadData(_lpData, _pos)
+            self.MailList.append(temMailList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x63
+        self.Count = 0
+        self.MailList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 2
+        for i in range(self.Count):
+            length += self.MailList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteWORD(data, self.Count)
+        for i in range(self.Count):
+            data = CommFunc.WriteString(data, self.MailList[i].GetLength(), self.MailList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                Count:%d,
+                                MailList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.Count,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCMailStateChange=tagMCMailStateChange()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCMailStateChange.Head.Cmd,m_NAtagMCMailStateChange.Head.SubCmd))] = m_NAtagMCMailStateChange
+
+
+#------------------------------------------------------
 # A3 46 大师经验信息 #tagMCGreatMasterExp
 
 class  tagMCGreatMasterExp(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py
index 977f92f..47ff107 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBDataMgr.py
@@ -19,6 +19,7 @@
 import PyGameData
 import DBPlayerViewCache
 import DBFamily
+import DBMail
 
 import binascii
 import time
@@ -43,9 +44,13 @@
 def OnServerClose():
     return
 
-def OnMinute(curMinute):
+def OnMinute(curTime):
+    curMinute = curTime.minute
     ServerDataBackup()
     DBFamily.OnMinute(curMinute)
+    return
+
+def OnDayEx():
     return
 
 #------------------------------------------- 备档 ---------------------------------------------------
@@ -160,17 +165,21 @@
     def __init__(self):
         self.PlayerViewCacheMgr = DBPlayerViewCache.PlayerViewCacheMgr()
         self.FamilyMgr = DBFamily.FamilyMgr()
+        self.MailMgr = DBMail.MailMgr()
         return
     
     def GetSaveData(self):
         buff = ""
         buff += self.PlayerViewCacheMgr.GetSaveData()
         buff += self.FamilyMgr.GetSaveData()
+        buff += self.MailMgr.GetSaveData()
         return buff
     
     def LoadGameData(self, gameBuffer, pos):
-        pos = self.PlayerViewCacheMgr.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
-        pos = self.FamilyMgr.LoadPyGameData(gameBuffer, pos, len(gameBuffer))
+        dataslen = len(gameBuffer)
+        pos = self.PlayerViewCacheMgr.LoadPyGameData(gameBuffer, pos, dataslen)
+        pos = self.FamilyMgr.LoadPyGameData(gameBuffer, pos, dataslen)
+        pos = self.MailMgr.LoadPyGameData(gameBuffer, pos, dataslen)
         return pos
     
 def GetDBDataMgr():
@@ -195,3 +204,8 @@
     ## 家族Action数据管理器
     return GetFamilyMgr().GetFamilyActionMgr()
 
+def GetMailMgr():
+    ## 邮件数据管理器
+    dbDataMgr = GetDBDataMgr()
+    return dbDataMgr.MailMgr
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBStruct.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBStruct.py
index 8fce029..c258a1a 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBStruct.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/DBStruct.py
@@ -1236,3 +1236,405 @@
         else:
             self.FamilyName = Str[:33]
             
+
+# 邮件个人邮件表 #tagDBMailPersonal
+class tagDBMailPersonal(Structure):
+    _pack_ = 1
+    _fields_ = [
+        ('PlayerID', ctypes.c_ulong),
+        ('GUID', ctypes.c_char * 36),
+        ('Type', ctypes.c_ubyte),
+        ('CreateTime', ctypes.c_char * 30),
+        ('LimitDays', ctypes.c_ubyte),
+        ('TitleLen', ctypes.c_ubyte),
+        ('Title', ctypes.c_char_p),
+        ('TextLen', ctypes.c_ushort),
+        ('Text', ctypes.c_char_p),
+        ('MailState', ctypes.c_ubyte),
+        ('ADOResult', ctypes.c_ulong),
+    ]
+
+    def __init__(self):
+        Structure.__init__(self)
+        self.clear()
+
+    def clear(self):
+        self.PlayerID = 0
+        self.GUID = ''
+        self.Type = 0
+        self.CreateTime = ''
+        self.LimitDays = 0
+        self.TitleLen = 0
+        self.Title = ''
+        self.TextLen = 0
+        self.Text = ''
+        self.MailState = 0
+
+    def readData(self, buf, pos = 0, length = 0):
+        if not pos <= length:
+            return -1
+        if len(buf) < pos + self.getLength():
+            return -1
+        self.clear()
+        self.PlayerID, pos = CommFunc.ReadDWORD(buf, pos)
+        self.GUID, pos = CommFunc.ReadString(buf, pos, 36)
+        self.Type, pos = CommFunc.ReadBYTE(buf, pos)
+        self.CreateTime, pos = CommFunc.ReadString(buf, pos, 30)
+        self.LimitDays, pos = CommFunc.ReadBYTE(buf, pos)
+        self.TitleLen, pos = CommFunc.ReadBYTE(buf, pos)
+        tmp, pos = CommFunc.ReadString(buf, pos, self.TitleLen)
+        self.Title = ctypes.c_char_p(tmp)
+        self.TextLen, pos = CommFunc.ReadWORD(buf, pos)
+        tmp, pos = CommFunc.ReadString(buf, pos, self.TextLen)
+        self.Text = ctypes.c_char_p(tmp)
+        self.MailState, pos = CommFunc.ReadBYTE(buf, pos)
+        return self.getLength()
+
+    def getBuffer(self):
+        buf = ''
+        buf = CommFunc.WriteDWORD(buf, self.PlayerID)
+        buf = CommFunc.WriteString(buf, sizeof(ctypes.c_char) * 36, self.GUID)
+        buf = CommFunc.WriteBYTE(buf, self.Type)
+        buf = CommFunc.WriteString(buf, sizeof(ctypes.c_char) * 30, self.CreateTime)
+        buf = CommFunc.WriteBYTE(buf, self.LimitDays)
+        buf = CommFunc.WriteBYTE(buf, self.TitleLen)
+        buf = CommFunc.WriteString(buf, self.TitleLen, self.Title)
+        buf = CommFunc.WriteWORD(buf, self.TextLen)
+        buf = CommFunc.WriteString(buf, self.TextLen, self.Text)
+        buf = CommFunc.WriteBYTE(buf, self.MailState)
+        return buf
+
+    def getLength(self):
+        length = 0
+        length += sizeof(ctypes.c_ulong)
+        length += sizeof(ctypes.c_char) * 36
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_char) * 30
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_ubyte)
+        length += self.TitleLen
+        length += sizeof(ctypes.c_ushort)
+        length += self.TextLen
+        length += sizeof(ctypes.c_ubyte)
+        return length
+
+    def outputString(self):
+        output = '''// 邮件个人邮件表 #tagDBMailPersonal:
+            PlayerID = %s,
+            GUID = %s,
+            Type = %s,
+            CreateTime = %s,
+            LimitDays = %s,
+            TitleLen = %s,
+            Title = %s,
+            TextLen = %s,
+            Text = %s,
+            MailState = %s,
+            ADOResult = %s,
+            '''%(
+                self.PlayerID,
+                self.GUID,
+                self.Type,
+                self.CreateTime,
+                self.LimitDays,
+                self.TitleLen,
+                self.Title,
+                self.TextLen,
+                self.Text,
+                self.MailState,
+                self.ADOResult,
+            )
+        return output
+
+    #Char数组类型Set接口,使用该接口对此类型数据赋值,防止赋值的数据过长报错
+    def SetGUID(self,Str):
+        if len(Str)<=36:
+            self.GUID = Str
+        else:
+            self.GUID = Str[:36]
+            
+    def SetCreateTime(self,Str):
+        if len(Str)<=30:
+            self.CreateTime = Str
+        else:
+            self.CreateTime = Str[:30]
+            
+
+# 邮件全服邮件表 #tagDBMailServer
+class tagDBMailServer(Structure):
+    _pack_ = 1
+    _fields_ = [
+        ('GUID', ctypes.c_char * 36),
+        ('Type', ctypes.c_ubyte),
+        ('CreateTime', ctypes.c_char * 30),
+        ('LimitDays', ctypes.c_ubyte),
+        ('TitleLen', ctypes.c_ubyte),
+        ('Title', ctypes.c_char_p),
+        ('TextLen', ctypes.c_ushort),
+        ('Text', ctypes.c_char_p),
+        ('LimitLV', ctypes.c_ushort),
+        ('LimitLVType', ctypes.c_ubyte),
+        ('CheckState', ctypes.c_ubyte),
+        ('ADOResult', ctypes.c_ulong),
+    ]
+
+    def __init__(self):
+        Structure.__init__(self)
+        self.clear()
+
+    def clear(self):
+        self.GUID = ''
+        self.Type = 0
+        self.CreateTime = ''
+        self.LimitDays = 0
+        self.TitleLen = 0
+        self.Title = ''
+        self.TextLen = 0
+        self.Text = ''
+        self.LimitLV = 0
+        self.LimitLVType = 0
+        self.CheckState = 0
+
+    def readData(self, buf, pos = 0, length = 0):
+        if not pos <= length:
+            return -1
+        if len(buf) < pos + self.getLength():
+            return -1
+        self.clear()
+        self.GUID, pos = CommFunc.ReadString(buf, pos, 36)
+        self.Type, pos = CommFunc.ReadBYTE(buf, pos)
+        self.CreateTime, pos = CommFunc.ReadString(buf, pos, 30)
+        self.LimitDays, pos = CommFunc.ReadBYTE(buf, pos)
+        self.TitleLen, pos = CommFunc.ReadBYTE(buf, pos)
+        tmp, pos = CommFunc.ReadString(buf, pos, self.TitleLen)
+        self.Title = ctypes.c_char_p(tmp)
+        self.TextLen, pos = CommFunc.ReadWORD(buf, pos)
+        tmp, pos = CommFunc.ReadString(buf, pos, self.TextLen)
+        self.Text = ctypes.c_char_p(tmp)
+        self.LimitLV, pos = CommFunc.ReadWORD(buf, pos)
+        self.LimitLVType, pos = CommFunc.ReadBYTE(buf, pos)
+        self.CheckState, pos = CommFunc.ReadBYTE(buf, pos)
+        return self.getLength()
+
+    def getBuffer(self):
+        buf = ''
+        buf = CommFunc.WriteString(buf, sizeof(ctypes.c_char) * 36, self.GUID)
+        buf = CommFunc.WriteBYTE(buf, self.Type)
+        buf = CommFunc.WriteString(buf, sizeof(ctypes.c_char) * 30, self.CreateTime)
+        buf = CommFunc.WriteBYTE(buf, self.LimitDays)
+        buf = CommFunc.WriteBYTE(buf, self.TitleLen)
+        buf = CommFunc.WriteString(buf, self.TitleLen, self.Title)
+        buf = CommFunc.WriteWORD(buf, self.TextLen)
+        buf = CommFunc.WriteString(buf, self.TextLen, self.Text)
+        buf = CommFunc.WriteWORD(buf, self.LimitLV)
+        buf = CommFunc.WriteBYTE(buf, self.LimitLVType)
+        buf = CommFunc.WriteBYTE(buf, self.CheckState)
+        return buf
+
+    def getLength(self):
+        length = 0
+        length += sizeof(ctypes.c_char) * 36
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_char) * 30
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_ubyte)
+        length += self.TitleLen
+        length += sizeof(ctypes.c_ushort)
+        length += self.TextLen
+        length += sizeof(ctypes.c_ushort)
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_ubyte)
+        return length
+
+    def outputString(self):
+        output = '''// 邮件全服邮件表 #tagDBMailServer:
+            GUID = %s,
+            Type = %s,
+            CreateTime = %s,
+            LimitDays = %s,
+            TitleLen = %s,
+            Title = %s,
+            TextLen = %s,
+            Text = %s,
+            LimitLV = %s,
+            LimitLVType = %s,
+            CheckState = %s,
+            ADOResult = %s,
+            '''%(
+                self.GUID,
+                self.Type,
+                self.CreateTime,
+                self.LimitDays,
+                self.TitleLen,
+                self.Title,
+                self.TextLen,
+                self.Text,
+                self.LimitLV,
+                self.LimitLVType,
+                self.CheckState,
+                self.ADOResult,
+            )
+        return output
+
+    #Char数组类型Set接口,使用该接口对此类型数据赋值,防止赋值的数据过长报错
+    def SetGUID(self,Str):
+        if len(Str)<=36:
+            self.GUID = Str
+        else:
+            self.GUID = Str[:36]
+            
+    def SetCreateTime(self,Str):
+        if len(Str)<=30:
+            self.CreateTime = Str
+        else:
+            self.CreateTime = Str[:30]
+            
+            
+
+# 邮件物品表 #tagDBMailItem
+class tagDBMailItem(Structure):
+    _pack_ = 1
+    _fields_ = [
+        ('GUID', ctypes.c_char * 36),
+        ('ItemID', ctypes.c_ulong),
+        ('Count', ctypes.c_ulong),
+        ('IsBind', ctypes.c_ubyte),
+        ('UserDataLen', ctypes.c_ushort),
+        ('UserData', ctypes.c_char_p),
+        ('ADOResult', ctypes.c_ulong),
+    ]
+
+    def __init__(self):
+        Structure.__init__(self)
+        self.clear()
+
+    def clear(self):
+        self.GUID = ''
+        self.ItemID = 0
+        self.Count = 0
+        self.IsBind = 0
+        self.UserDataLen = 0
+        self.UserData = ''
+
+    def readData(self, buf, pos = 0, length = 0):
+        if not pos <= length:
+            return -1
+        if len(buf) < pos + self.getLength():
+            return -1
+        self.clear()
+        self.GUID, pos = CommFunc.ReadString(buf, pos, 36)
+        self.ItemID, pos = CommFunc.ReadDWORD(buf, pos)
+        self.Count, pos = CommFunc.ReadDWORD(buf, pos)
+        self.IsBind, pos = CommFunc.ReadBYTE(buf, pos)
+        self.UserDataLen, pos = CommFunc.ReadWORD(buf, pos)
+        tmp, pos = CommFunc.ReadString(buf, pos, self.UserDataLen)
+        self.UserData = ctypes.c_char_p(tmp)
+        return self.getLength()
+
+    def getBuffer(self):
+        buf = ''
+        buf = CommFunc.WriteString(buf, sizeof(ctypes.c_char) * 36, self.GUID)
+        buf = CommFunc.WriteDWORD(buf, self.ItemID)
+        buf = CommFunc.WriteDWORD(buf, self.Count)
+        buf = CommFunc.WriteBYTE(buf, self.IsBind)
+        buf = CommFunc.WriteWORD(buf, self.UserDataLen)
+        buf = CommFunc.WriteString(buf, self.UserDataLen, self.UserData)
+        return buf
+
+    def getLength(self):
+        length = 0
+        length += sizeof(ctypes.c_char) * 36
+        length += sizeof(ctypes.c_ulong)
+        length += sizeof(ctypes.c_ulong)
+        length += sizeof(ctypes.c_ubyte)
+        length += sizeof(ctypes.c_ushort)
+        length += self.UserDataLen
+        return length
+
+    def outputString(self):
+        output = '''// 邮件物品表 #tagDBMailItem:
+            GUID = %s,
+            ItemID = %s,
+            Count = %s,
+            IsBind = %s,
+            UserDataLen = %s,
+            UserData = %s,
+            ADOResult = %s,
+            '''%(
+                self.GUID,
+                self.ItemID,
+                self.Count,
+                self.IsBind,
+                self.UserDataLen,
+                self.UserData,
+                self.ADOResult,
+            )
+        return output
+
+    #Char数组类型Set接口,使用该接口对此类型数据赋值,防止赋值的数据过长报错
+    def SetGUID(self,Str):
+        if len(Str)<=36:
+            self.GUID = Str
+        else:
+            self.GUID = Str[:36]
+            
+
+# 邮件全服记录表 #tagDBMailPlayerRec
+class tagDBMailPlayerRec(Structure):
+    _pack_ = 1
+    _fields_ = [
+        ('PlayerID', ctypes.c_ulong),
+        ('GUID', ctypes.c_char * 36),
+        ('MailState', ctypes.c_ubyte),
+        ('ADOResult', ctypes.c_ulong),
+    ]
+
+    def __init__(self):
+        Structure.__init__(self)
+        self.clear()
+
+
+    def clear(self):
+        memset(addressof(self), 0, self.getLength())
+
+    def readData(self, buf, pos = 0, length = 0):
+        if not pos <= length:
+            return -1
+        if len(buf) < pos + self.getLength():
+            return -1
+        self.clear()
+        self.PlayerID, pos = CommFunc.ReadDWORD(buf, pos)
+        self.GUID, pos = CommFunc.ReadString(buf, pos, 36)
+        self.MailState, pos = CommFunc.ReadBYTE(buf, pos)
+        return self.getLength()
+
+
+    def getBuffer(self):
+        buf = create_string_buffer(self.getLength())
+        memmove(addressof(buf), addressof(self), self.getLength())
+        return string_at(addressof(buf), self.getLength())
+
+    def getLength(self):
+        return sizeof(tagDBMailPlayerRec)
+
+    def outputString(self):
+        output = '''// 邮件全服记录表 #tagDBMailPlayerRec:
+            PlayerID = %s,
+            GUID = %s,
+            MailState = %s,
+            ADOResult = %s,
+            '''%(
+                self.PlayerID,
+                self.GUID,
+                self.MailState,
+                self.ADOResult,
+            )
+        return output
+
+    #Char数组类型Set接口,使用该接口对此类型数据赋值,防止赋值的数据过长报错
+    def SetGUID(self,Str):
+        if len(Str)<=36:
+            self.GUID = Str
+        else:
+            self.GUID = Str[:36]
+            
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBFamily.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBFamily.py
index d688586..44d3fd7 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBFamily.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBFamily.py
@@ -352,7 +352,7 @@
     def RefrshFightPowerTotal(self, checkChange=False):
         ## 刷新总战力
         if checkChange and self.__memFightPowerChange == False: # 默认None,首次必刷新
-            GameWorld.DebugLog("没有成员战力变化可不刷新仙盟总战力! familyID=%s" % self.GetID())
+            #GameWorld.DebugLog("没有成员战力变化可不刷新仙盟总战力! familyID=%s" % self.GetID())
             return
         familyFightPowerTotal = 0
         for index in range(self.GetCount()):
@@ -604,8 +604,7 @@
             family = self.FindFamily(familyID)
             if not family:
                 continue
-            member = family.InitMemberInstance(dbData)
-            GameWorld.Log("    member:%s,familyID=%s" % (member.GetPlayerID(), familyID))
+            family.InitMemberInstance(dbData)
             
         # 行为
         cnt, pos = CommFunc.ReadDWORD(datas, pos)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBMail.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBMail.py
new file mode 100644
index 0000000..bd9a051
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DB/StructData/DBMail.py
@@ -0,0 +1,418 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package DB.StructData.DBMail
+#
+# @todo:DB邮件
+# @author hxp
+# @date 2025-05-14
+# @version 1.0
+#
+# 详细描述: DB邮件
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-05-14 12:00"""
+#-------------------------------------------------------------------------------
+
+import DBStruct
+import CommFunc
+import ShareDefine
+import GameWorld
+    
+class MailItem():
+    ## 邮件物品 - 个人、全服邮件通用
+    
+    def __init__(self, dbData=None):
+        self.__dbData = DBStruct.tagDBMailItem() if not dbData else dbData
+        return
+    
+    def GetGUID(self): return self.__dbData.GUID
+    def GetItemID(self): return self.__dbData.ItemID
+    def GetCount(self): return self.__dbData.Count
+    def GetIsBind(self): return self.__dbData.IsBind
+    def GetUserData(self): return self.__dbData.UserData
+    def GetBuffer(self): return self.__dbData.getBuffer()
+    
+class ServerMail():
+    ## 全服邮件
+    
+    def __init__(self, dbData=None):
+        self.__dbData = DBStruct.tagDBMailServer() if not dbData else dbData
+        return
+    
+    def GetGUID(self): return self.__dbData.GUID
+    def GetType(self): return self.__dbData.Type
+    def GetCreateTime(self): return self.__dbData.CreateTime
+    def GetLimitDays(self): return self.__dbData.LimitDays
+    def GetTitle(self): return self.__dbData.Title
+    def GetText(self): return self.__dbData.Text
+    def GetLimitLV(self): return self.__dbData.LimitLV # 限制可领的最低玩家等级
+    def SetLimitLV(self, limitLV): self.__dbData.LimitLV = limitLV
+    def GetLimitLVType(self): return self.__dbData.LimitLVType # 升级达到等级条件后是否可领;0-不可,1-可以
+    def SetLimitLVType(self, limitLVType): self.__dbData.LimitLVType = limitLVType
+    def GetCheckState(self): return self.__dbData.CheckState # 需要审核状态:0-已审核;1-未审核
+    def SetCheckState(self, checkState): self.__dbData.CheckState = checkState
+    def GetBuffer(self): return self.__dbData.getBuffer()
+    
+class PersonalMail():
+    ## 个人邮件
+    
+    def __init__(self, dbData=None):
+        self.__dbData = DBStruct.tagDBMailPersonal() if not dbData else dbData
+        return
+    
+    def GetPlayerID(self): return self.__dbData.PlayerID
+    def GetGUID(self): return self.__dbData.GUID
+    def GetType(self): return self.__dbData.Type
+    def GetCreateTime(self): return self.__dbData.CreateTime
+    def GetLimitDays(self): return self.__dbData.LimitDays
+    def GetTitle(self): return self.__dbData.Title
+    def GetText(self): return self.__dbData.Text
+    def GetMailState(self): return self.__dbData.MailState
+    def SetMailState(self, mailState): self.__dbData.MailState = mailState
+    def GetBuffer(self): return self.__dbData.getBuffer()
+    
+class MailMgr():
+    
+    def __init__(self):
+        self.__personalMailDict = {} # 个人邮件 {playerID:{GUID:PersonalMail, ...}, ...}
+        self.__mailItemDict = {} # 邮件物品 {GUID:[MailItem, ...], ...}
+        self.__serverMailDict = {} # 全服邮件 {GUID:ServerMail, ...}
+        self.__serverMailPlayerStateDict = {} # 全服邮件玩家状态 {GUID:{playerID:mailState, ...}, ...}
+        return
+    
+    def __InitPersonalMailInstance(self, dbData):
+        '''初始化功能数据实例,创建或加载数据时通用,功能一般不调用
+        @param dbData: 实例对应绑定的dbData
+        @return: 成功返回实例对象,失败返回None
+        '''
+        mailObj = PersonalMail(dbData)
+        playerID = mailObj.GetPlayerID()
+        if playerID not in self.__personalMailDict:
+            self.__personalMailDict[playerID] = {}
+        mailDict = self.__personalMailDict[playerID]
+        mailDict[mailObj.GetGUID()] = mailObj
+        return mailObj
+    
+    def __InitServerMailInstance(self, dbData):
+        '''初始化功能数据实例,创建或加载数据时通用,功能一般不调用
+        @param dbData: 实例对应绑定的dbData
+        @return: 成功返回实例对象,失败返回None
+        '''
+        mailObj = ServerMail(dbData)
+        guid = mailObj.GetGUID()
+        self.__serverMailDict[guid] = mailObj
+        return mailObj
+    
+    def __InitMailItemInstance(self, dbData):
+        '''初始化功能数据实例,创建或加载数据时通用,功能一般不调用
+        @param dbData: 实例对应绑定的dbData
+        @return: 成功返回实例对象,失败返回None
+        '''
+        mailItem = MailItem(dbData)
+        guid = mailItem.GetGUID()
+        if guid not in self.__mailItemDict:
+            self.__mailItemDict[guid] = []
+        mailItemList = self.__mailItemDict[guid]
+        mailItemList.append(mailItem)
+        return mailItem
+    
+    def __AddMailItem(self, guid, itemList):
+        '''添加邮件物品,全服、个人邮件通用,邮件物品只关联guid,功能使用层级单封邮件物品上限不限,但是邮件系统单封有内置上限
+        @param itemList: 元素支持字典{k:v, ...} 或列表 [itemID, itemCount, 可选是否拍品, 物品UserData]
+        '''
+        if not itemList:
+            return
+        self.__mailItemDict.pop(guid, None) # 防止重复添加邮件物品,先移除
+        
+        # 检查整合合并物品,并确保物品顺序
+        itemRankList = []
+        itemDict = {}
+        for itemInfo in itemList:
+            if isinstance(itemInfo, dict):
+                itemID = itemInfo.get("ItemID", 0)
+                itemCount = itemInfo.get("Count", 0)
+                isBind = itemInfo.get("IsBind", 0)
+                userData = itemInfo.get("UserData", "")
+            elif (isinstance(itemInfo, list) or isinstance(itemInfo, tuple)) and len(itemInfo) >= 2:
+                itemID, itemCount = itemInfo[:2]
+                isBind = itemInfo[2] if len(itemInfo) > 2 else 0
+                userData = itemInfo[3] if len(itemInfo) > 3 else ""
+            else:
+                continue
+            itemID = min(GameWorld.ToIntDef(itemID, 0), ShareDefine.Def_UpperLimit_DWord)
+            itemCount = min(GameWorld.ToIntDef(itemCount, 0), ShareDefine.Def_UpperLimit_DWord)
+            isBind = 1 if isBind else 0
+            if not itemID or not itemCount:
+                continue
+            
+            if userData:
+                itemRankList.append([itemID, itemCount, isBind, userData])
+            else:
+                key = (itemID, isBind)
+                if key not in itemDict:
+                    itemDict[key] = 0
+                    itemRankList.append(key)
+                itemDict[key] = itemDict[key] + itemCount
+                
+        mailItemCount = 0
+        for itemInfo in itemRankList:
+            if len(itemInfo) == 4:
+                itemID, itemCount, isBind, userData = itemInfo
+            else:
+                userData = ""
+                itemID, isBind = itemInfo
+                itemCount = itemDict.get(itemInfo, 0)
+                
+            dbData = DBStruct.tagDBMailItem()
+            dbData.GUID = guid
+            dbData.ItemID = itemID
+            dbData.Count = itemCount
+            dbData.IsBind = isBind
+            dbData.UserData = userData
+            dbData.UserDataLen = len(dbData.UserData)
+            self.__InitMailItemInstance(dbData)
+            
+            mailItemCount += 1
+            if mailItemCount >= 20:
+                #防范某些异常情况,内置单封邮件物品上限,做下限制,并做后台邮件警告
+                GameWorld.SendGameErrorEx("MailItemMultiError", "%s|%s" % (guid, itemList))
+                break
+            
+        return
+    
+    def AddPersonalMail(self, playerID, title, text, itemList, limitDays=7, mailType=0):
+        '''添加个人邮件
+        @param itemList: 元素支持字典{k:v, ...} 或列表 [itemID, itemCount, 可选是否拍品, 物品UserData]
+        '''
+        guid = GameWorld.GetGUID()
+        dbData = DBStruct.tagDBMailPersonal()
+        dbData.PlayerID = playerID
+        dbData.GUID = guid
+        dbData.Type = mailType
+        dbData.CreateTime = GameWorld.GetCurrentDataTimeStr()
+        dbData.LimitDays = limitDays
+        dbData.Title = title
+        dbData.TitleLen = len(dbData.Title)
+        dbData.Text = text
+        dbData.TextLen = len(dbData.Text)
+        dbData.MailState = ShareDefine.MailState_UnRead # 个人邮件默认未读
+        self.__AddMailItem(guid, itemList)
+        mailObj = self.__InitPersonalMailInstance(dbData)
+        return mailObj
+    
+    def GetAllPersonalMailDict(self): return self.__personalMailDict
+    
+    def GetPersonalMailCount(self, playerID):
+        ## 获取玩家个人邮件总数
+        if playerID not in self.__personalMailDict:
+            return 0
+        return len(self.__personalMailDict[playerID])
+    
+    def GetPersonalMailGuids(self, playerID):
+        ## 获取玩家所有个人邮件guid列表
+        if playerID not in self.__personalMailDict:
+            return []
+        mailDict = self.__personalMailDict[playerID]
+        return mailDict.keys()
+    
+    def GetPersonalMails(self, playerID):
+        ## 获取玩家所有个人邮件实例列表
+        if playerID not in self.__personalMailDict:
+            return []
+        mailDict = self.__personalMailDict[playerID]
+        return mailDict.values()
+    
+    def GetPersonalMail(self, playerID, guid):
+        ## 获取玩家个人邮件
+        mailObj = None
+        if playerID not in self.__personalMailDict:
+            self.__personalMailDict[playerID] = {}
+        mailDict = self.__personalMailDict[playerID]
+        if guid in mailDict:
+            mailObj = mailDict[guid]
+        elif False:
+            mailObj = PersonalMail()
+        return mailObj
+
+    def GetMailItemCount(self, guid):
+        ## 获取邮件物品个数,全服邮件、个人邮件通用
+        if guid not in self.__mailItemDict:
+            return 0
+        return len(self.__mailItemDict[guid])
+    
+    def GetMailItemAt(self, guid, index):
+        ## 获取邮件某个物品
+        itemObj = None
+        if guid in self.__mailItemDict:
+            itemObjList = self.__mailItemDict[guid]
+            if 0 <= index < len(itemObjList):
+                itemObj = itemObjList[index]
+        if not itemObj and False:
+            itemObj = MailItem()
+        return itemObj
+    
+    def AddServerMail(self, guid, title, text, itemList, limitDays=7, mailType=0):
+        '''添加个人邮件
+        @param guid: 指定的邮件guid,为空时自动生成新guid
+        @param itemList: 元素支持字典{k:v, ...} 或列表 [itemID, itemCount, 可选是否拍品, 物品UserData]
+        '''
+        if not guid:
+            guid = GameWorld.GetGUID()
+        dbData = DBStruct.tagDBMailServer()
+        dbData.GUID = guid
+        dbData.Type = mailType
+        dbData.CreateTime = GameWorld.GetCurrentDataTimeStr()
+        dbData.LimitDays = limitDays
+        dbData.Title = title
+        dbData.TitleLen = len(dbData.Title)
+        dbData.Text = text
+        dbData.TextLen = len(dbData.Text)
+        self.__AddMailItem(guid, itemList)
+        mailObj = self.__InitServerMailInstance(dbData)
+        return mailObj
+    
+    def GetServerMail(self, guid):
+        ## 获取全服邮件
+        mailObj = None
+        if guid in self.__serverMailDict:
+            mailObj = self.__serverMailDict[guid]
+        elif False:
+            mailObj = ServerMail()
+        return mailObj
+    
+    def GetServerMailGuids(self):
+        ## 获取所有全服邮件guid列表
+        return self.__serverMailDict.keys()
+    
+    def GetPlayerMailState(self, guid, playerID):
+        ## 获取玩家某个邮件状态,个人、全服邮件通用
+        personalMail = self.GetPersonalMail(playerID, guid)
+        if personalMail:
+            return personalMail.GetMailState()
+        if guid not in self.__serverMailPlayerStateDict:
+            return ShareDefine.MailState_Unknown
+        playerStateDict = self.__serverMailPlayerStateDict[guid]
+        if playerID not in playerStateDict:
+            return ShareDefine.MailState_Unknown
+        return playerStateDict[playerID]
+    
+    def SetPlayerMailState(self, guid, playerID, mailState):
+        ## 设置玩家某个邮件状态,个人、全服邮件通用
+        #  @return: 返回设置的邮件状态
+        personalMail = self.GetPersonalMail(playerID, guid)
+        if personalMail:
+            if mailState >= ShareDefine.MailState_Del:
+                self.DelPersonalMail(playerID, guid)
+                return ShareDefine.MailState_Del
+            personalMail.SetMailState(mailState)
+            return mailState
+        if guid not in self.__serverMailPlayerStateDict:
+            self.__serverMailPlayerStateDict[guid] = {}
+        playerStateDict = self.__serverMailPlayerStateDict[guid]
+        playerStateDict[playerID] = mailState
+        return mailState
+    
+    def DelPersonalMail(self, playerID, guid):
+        ## 删除个人邮件
+        self.__mailItemDict.pop(guid, None)
+        if playerID in self.__personalMailDict:
+            mailDict = self.__personalMailDict[playerID]
+            mailDict.pop(guid, None)
+        return
+    
+    def DelServerMail(self, guid):
+        ## 删除某个全服邮件
+        self.__mailItemDict.pop(guid, None)
+        self.__serverMailDict.pop(guid, None)
+        playerStateDict = self.__serverMailPlayerStateDict.pop(guid, {})
+        return playerStateDict
+    
+    # 保存数据 存数据库和realtimebackup
+    def GetSaveData(self):
+        saveData = ""
+        
+        # 全服邮件
+        serverMailCnt, serverMailSavaData = 0, ""
+        for mailObj in self.__serverMailDict.values():
+            serverMailCnt += 1
+            serverMailSavaData += mailObj.GetBuffer()
+        saveData += CommFunc.WriteDWORD("", serverMailCnt) + serverMailSavaData
+        GameWorld.Log("Save DBMailServer count :%s len=%s" % (serverMailCnt, len(serverMailSavaData)))
+        
+        # 全服邮件玩家状态
+        dbData = DBStruct.tagDBMailPlayerRec()
+        playerStateCnt, playerStateSaveData = 0, ""
+        for guid, playerStateDict in self.__serverMailPlayerStateDict.items():
+            for playerID, mailState in playerStateDict.items():
+                dbData.clear()
+                dbData.GUID = guid
+                dbData.PlayerID = playerID
+                dbData.MailState = mailState
+                playerStateCnt += 1
+                playerStateSaveData += dbData.getBuffer()
+        saveData += CommFunc.WriteDWORD("", playerStateCnt) + playerStateSaveData
+        GameWorld.Log("Save DBMailPlayerRec count :%s len=%s" % (playerStateCnt, len(playerStateSaveData)))
+        
+        # 个人邮件
+        personalMailCnt, personalMailSavaData = 0, ""
+        for mailDict in self.__personalMailDict.values():
+            for mailObj in mailDict.values():
+                personalMailCnt += 1
+                personalMailSavaData += mailObj.GetBuffer()
+        saveData += CommFunc.WriteDWORD("", personalMailCnt) + personalMailSavaData
+        GameWorld.Log("Save DBMailPersonal count :%s len=%s" % (personalMailCnt, len(personalMailSavaData)))
+        
+        # 邮件物品
+        mailItemCnt, mailItemSavaData = 0, ""
+        for itemList in self.__mailItemDict.values():
+            for itemObj in itemList:
+                mailItemCnt += 1
+                mailItemSavaData += itemObj.GetBuffer()
+        saveData += CommFunc.WriteDWORD("", mailItemCnt) + mailItemSavaData
+        GameWorld.Log("Save DBMailItem count :%s len=%s" % (mailItemCnt, len(mailItemSavaData)))
+        
+        return saveData
+    
+    # 从数据库载入数据
+    def LoadPyGameData(self, datas, pos, dataslen):
+        if pos >= dataslen:
+            return
+        
+        # 全服邮件
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load DBMailServer count :%s" % cnt)
+        for _ in xrange(cnt):
+            dbData = DBStruct.tagDBMailServer()
+            pos += dbData.readData(datas, pos, dataslen)
+            self.__InitServerMailInstance(dbData)
+            
+        # 全服邮件玩家状态
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load DBMailPlayerRec count :%s" % cnt)
+        dbData = DBStruct.tagDBMailPlayerRec()
+        for _ in xrange(cnt):
+            dbData.clear()
+            pos += dbData.readData(datas, pos, dataslen)
+            self.SetServerMailPlayerState(dbData.GUID, dbData.PlayerID, dbData.MailState)
+            
+        # 个人邮件
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load DBMailPersonal count :%s" % cnt)
+        for _ in xrange(cnt):
+            dbData = DBStruct.tagDBMailPersonal()
+            pos += dbData.readData(datas, pos, dataslen)
+            self.__InitPersonalMailInstance(dbData)
+            
+        # 邮件物品
+        cnt, pos = CommFunc.ReadDWORD(datas, pos)
+        GameWorld.Log("Load DBMailItem count :%s" % cnt)
+        for _ in xrange(cnt):
+            dbData = DBStruct.tagDBMailItem()
+            pos += dbData.readData(datas, pos, dataslen)
+            self.__InitMailItemInstance(dbData)
+            
+        return pos
+
+    
\ No newline at end of file
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DataRecordPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DataRecordPack.py
index 593dd42..9a24612 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DataRecordPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/DataRecordPack.py
@@ -1565,4 +1565,26 @@
                 'AccID':curPlayer.GetAccID(), 'dayIndex':dayIndex, 'point':point}
     #发送封包
     SendEventPack("FeastWeekPartyPoint", dataDict, curPlayer)
-    return
\ No newline at end of file
+    return
+
+def DR_MailSend(playerID, GUID, addDict={}):
+    ## 邮件发送流向
+    dataDict = {'PlayerID':playerID, 'GUID':GUID} 
+    dataDict.update(addDict)
+    #发送封包
+    SendEventPack("MailSend", dataDict)
+    return 
+
+def DR_MailGiveSuccess(playerID, GUID):
+    ## 邮件领取流向
+    dataDict = {'PlayerID':playerID, 'GUID':GUID} 
+    #发送封包
+    SendEventPack("MailGiveSuccess", dataDict)
+    return
+
+def DR_MailDel(playerID, GUID, eventName):
+    ## 邮件删除流向
+    dataDict = {'PlayerID':playerID, 'GUID':GUID, 'eventName':eventName}
+    #发送封包
+    SendEventPack("MailDel", dataDict)
+    return 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Mail.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Mail.py
new file mode 100644
index 0000000..ab5ab3f
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Mail.py
@@ -0,0 +1,158 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package GM.Commands.Mail
+#
+# @todo:邮件
+# @author hxp
+# @date 2025-05-14
+# @version 1.0
+#
+# 详细描述: 邮件
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-05-14 12:00"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import PlayerMail
+import ShareDefine
+import DataRecordPack
+import DBDataMgr
+import random
+
+## 执行逻辑
+#  @param curPlayer 当前玩家
+#  @param gmList []
+#  @return None
+def OnExec(curPlayer, gmList):
+    
+    if not gmList:
+        GameWorld.DebugAnswer(curPlayer, "清空邮件: Mail 0")
+        GameWorld.DebugAnswer(curPlayer, "发送邮件: Mail 几封 物品数 [模板key 参数1 ...]")
+        GameWorld.DebugAnswer(curPlayer, "输出邮件: Mail p")
+        return
+    
+    value = gmList[0]
+    
+    if value == 0:
+        ClearMail(curPlayer)
+        return
+    
+    if value == "p":
+        PrintPlayerMail(curPlayer)
+        return
+    
+    if value > 0:
+        SendPlayerMail(curPlayer, gmList)
+        return
+    
+    return
+
+def SendPlayerMail(curPlayer, gmList):
+    playerID = curPlayer.GetPlayerID()
+    sendCnt = gmList[0]
+    mailItemCnt = gmList[1] if len(gmList) > 1 else 5
+    mailTypeKey = gmList[2] if len(gmList) > 2 else ""
+    paramList = gmList[3:]
+    
+    itemIDList = range(3501, 3530 + 1)
+    moneyIDList = [20, 30]
+    
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailCntBef = mailMgr.GetPersonalMailCount(playerID)
+    
+    isBind = 0
+    for _ in range(sendCnt):
+        
+        itemList = []
+        if mailItemCnt:
+            for moneyID in moneyIDList:
+                itemCount = random.choice([100, 1000, 10000, 20000, 50000, 100000, 200000, 300000, 500000])
+                itemList.append([moneyID, itemCount, isBind])
+                if len(itemList) >= mailItemCnt:
+                    break
+                
+            random.shuffle(itemIDList)
+            for i in range(mailItemCnt):
+                itemID = itemIDList[i]
+                itemCount = random.randint(1, 100000)
+                itemList.append([itemID, itemCount, isBind])
+                if len(itemList) >= mailItemCnt:
+                    break
+                
+        PlayerMail.SendMailByKey(mailTypeKey, playerID, itemList, paramList)
+        
+    mailCntAft = mailMgr.GetPersonalMailCount(playerID)
+    GameWorld.DebugAnswer(curPlayer, "发送个人邮件OK:%s, %s~%s" % (sendCnt, mailCntBef, mailCntAft))
+    return
+
+def ClearMail(curPlayer):
+    notifyGUIDState = {}
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()    
+    guidList = mailMgr.GetPersonalMailGuids(playerID)
+    for guid in guidList:
+        mailMgr.DelPersonalMail(playerID, guid)
+        DataRecordPack.DR_MailDel(playerID, guid, "GMDel")
+        notifyGUIDState[guid] = ShareDefine.MailState_Del
+    GameWorld.DebugAnswer(curPlayer, "删除个人邮件:%s" % len(guidList))
+    
+    guidList = mailMgr.GetServerMailGuids()
+    for guid in guidList:
+        playerStateDict = mailMgr.DelServerMail(guid)
+        if playerID in playerStateDict and playerStateDict[playerID] < ShareDefine.MailState_Del:
+            notifyGUIDState[guid] = ShareDefine.MailState_Del
+    if len(guidList):
+        GameWorld.DebugAnswer(curPlayer, "删除全服邮件:%s" % len(guidList))
+    PlayerMail.Sync_PlayerMailState(curPlayer, notifyGUIDState)
+    return
+
+def PrintPlayerMail(curPlayer):
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()    
+    guidList = mailMgr.GetPersonalMailGuids(playerID)
+    GameWorld.DebugAnswer(curPlayer, "个人邮件数:%s" % len(guidList))
+    for num, guid in enumerate(guidList, 1):
+        mailObj = mailMgr.GetPersonalMail(playerID, guid)
+        __printMailLog(curPlayer, mailMgr, mailObj, num, False)
+            
+    guidList = mailMgr.GetServerMailGuids()
+    GameWorld.DebugAnswer(curPlayer, "全服邮件数:%s" % len(guidList))
+    for num, guid in enumerate(guidList, 1):
+        mailObj = mailMgr.GetServerMail(guid)
+        __printMailLog(curPlayer, mailMgr, mailObj, num, True)
+        
+    GameWorld.DebugAnswer(curPlayer, "邮件明细详见地图日志")
+    return
+
+def __printMailLog(curPlayer, mailMgr, mailObj, num, isServerMail):
+    playerID = curPlayer.GetPlayerID()
+    GUID = mailObj.GetGUID()
+    Title = mailObj.GetTitle()
+    Text = mailObj.GetText()
+    CreateTime = mailObj.GetCreateTime()
+    LimitDays = mailObj.GetLimitDays()
+    mailState = mailMgr.GetPlayerMailState(GUID, playerID)
+    mailItemCount = mailMgr.GetMailItemCount(GUID)
+    GameWorld.DebugLog("%s,%s,mailState=%s,mailItemCount=%s" % (num, GUID, mailState, mailItemCount), playerID)
+    GameWorld.DebugLog("    CreateTime=%s,LimitDays=%s,Title=%s,Text=%s" % (CreateTime, LimitDays, Title, Text), playerID)
+    if isServerMail:
+        LimitLV = mailObj.GetLimitLV()
+        LimitLVType = mailObj.GetLimitLVType()
+        CheckState = mailObj.GetCheckState()
+        GameWorld.DebugLog("    LimitLV=%s,升级可领=%s,需要审核=%s" % (LimitLV, LimitLVType, CheckState), playerID)
+        
+    itemList = []
+    for index in range(mailItemCount):
+        mailItem = mailMgr.GetMailItemAt(GUID, index)
+        itemID = mailItem.GetItemID()
+        itemCount = mailItem.GetCount()
+        isBind = mailItem.GetIsBind()
+        userData = mailItem.GetUserData()
+        itemList.append([itemID, itemCount, isBind, userData])
+    if itemList:
+        GameWorld.DebugLog("    %s,itemList=%s" % (mailItemCount, itemList), playerID)
+    return
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
index c71d18d..6092f9c 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorld.py
@@ -56,6 +56,7 @@
 import PyGameData
 import urllib
 import md5
+import uuid
 #---------------------------------------------------------------------
 #设置允许的最大迭代数目, 默认1000
 #在NPCAI中, 可能超过1000, 所以要设定为2000
@@ -1475,6 +1476,8 @@
     ChannelCodeDict = ReadChConfig.GetEvalChConfig("ChannelCode")
     return ChannelCodeDict.get(codeNum, "")
 
+def GetGUID(): return str(uuid.uuid1())
+
 #---------------------------------------------------------------------
 ##大额度金钱记录
 # @param tradeGold 物品售价金子
@@ -2365,7 +2368,7 @@
     #    return
     #===========================================================================
     
-    DebugLog(text)
+    DebugLog(text, curPlayer.GetPlayerID())
     text = text.decode(ShareDefine.Def_Game_Character_Encoding).encode(GetCharacterEncoding())
     curPlayer.DebugAnswer(text)
     return
@@ -2381,10 +2384,9 @@
 
 def SendGameErrorEx(errType, msgInfo="", playerID=0):
     ErrLog("SendGameErrorEx: %s -> %s" % (errType, msgInfo), playerID)
+    SendGameError(errType, msgInfo)
     if GetGameWorld().GetDebugLevel():
         raise Exception("%s -> %s" % (errType, msgInfo))
-    else:
-        SendGameError(errType, msgInfo)
     return
 
 def SendGameError(errType, msgInfo=""):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
index 77e539c..fa6d995 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GameWorldLogic/GameWorldProcess.py
@@ -782,7 +782,7 @@
     if curMinute == PyGameData.g_mapLastProcess_Minute:
         return
     if PyGameData.g_initGame:
-        DBDataMgr.OnMinute(curMinute)
+        DBDataMgr.OnMinute(curTime)
     PyGameData.g_mapLastProcess_Minute = curMinute
     PlayerTeam.OnCheckTeamPlayerDisconnectTimeout(tick)
     
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
index 1c4fbf6..d205783 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/ChPlayer.py
@@ -172,6 +172,7 @@
 import PlayerActFamilyGCZ
 import PlayerActYunshi
 import PlayerActTask
+import PlayerMail
 
 import datetime
 import time
@@ -1007,6 +1008,7 @@
         pass
     
     else:
+        PlayerMail.OnPlayerLogin(curPlayer)
         PlayerChatBox.OnPlayerLogin(curPlayer)
         PlayerFace.OnPlayerLogin(curPlayer)
         PlayerXiangong.OnPlayerLogin(curPlayer)
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
index 0e9b165..6846c02 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerEventCounter.py
@@ -142,6 +142,8 @@
 import PlayerXiangong
 import PlayerFuncTeam
 import PlayerMineArea
+import PlayerMail
+import DBDataMgr
 
 import datetime
 import time
@@ -222,6 +224,8 @@
 def __Func_GameServer_OnDayEx(tick):
     GameWorld.Log("MapServer -> OnDayEx!")
     
+    DBDataMgr.OnDayEx()
+    PlayerMail.OnDayEx()
     PlayerControl.RemoveTimeoutLeaveServerPlayerInfo(tick)
     
     playerManager = GameWorld.GetPlayerManager()
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerMail.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerMail.py
new file mode 100644
index 0000000..cdcf8db
--- /dev/null
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerMail.py
@@ -0,0 +1,515 @@
+#!/usr/bin/python
+# -*- coding: GBK -*-
+#-------------------------------------------------------------------------------
+#
+##@package Player.PlayerMail
+#
+# @todo:邮件
+# @author hxp
+# @date 2025-05-14
+# @version 1.0
+#
+# 详细描述: 邮件
+#
+#-------------------------------------------------------------------------------
+#"""Version = 2025-05-14 12:00"""
+#-------------------------------------------------------------------------------
+
+import GameWorld
+import ItemCommon
+import PlayerControl
+import IPY_GameWorld
+import DataRecordPack
+import ChPyNetSendPack
+import IpyGameDataPY
+import ItemControler
+import NetPackCommon
+import ShareDefine
+import DBDataMgr
+import ChConfig
+
+import json
+import time
+
+def OnPlayerLogin(curPlayer):
+    Sync_PlayerServerMail(curPlayer)
+    Sync_PersonalMail(curPlayer)
+    return
+
+def OnDayEx():
+    curTime = int(time.time())
+    DelTimeoutServerMail(curTime)
+    DelTimeoutPersonalMail(curTime)
+    return
+
+def DelTimeoutServerMail(curTime):
+    ## 清理超时全服邮件, 全服邮件分两部处理:1. 邮件接收的有效时间,后台设置,2. 邮件删除时间为接收有效时间再延长30天
+    mailMgr = DBDataMgr.GetMailMgr()
+    serverMailGuids = mailMgr.GetServerMailGuids()
+    for guid in serverMailGuids:
+        mailObj = mailMgr.GetServerMail(guid)
+        if not mailObj:
+            continue
+        createTime = GameWorld.ChangeTimeStrToNum(mailObj.GetCreateTime())
+        limitTime = createTime + (mailObj.GetLimitDays() + 7) * 3600 * 24
+        if curTime < limitTime:
+            #GameWorld.DebugLog("全服邮件未超时,不删除! %s,createTime=%s,limitTime=%s" % (guid, mailObj.GetCreateTime(), GameWorld.ChangeTimeNumToStr(limitTime)))
+            continue
+        
+        DelServerMail(guid)
+        
+    return
+
+def DelServerMail(guid):
+    mailMgr = DBDataMgr.GetMailMgr()
+    playerStateDict = mailMgr.DelServerMail(guid)
+    if not playerStateDict:
+        return
+    playerMgr = GameWorld.GetPlayerManager()
+    clientPack = __packMailStateChange({guid:ShareDefine.MailState_Del})
+    for playerID, mailState in playerStateDict.items():
+        if mailState >= ShareDefine.MailState_Del:
+            continue
+        curPlayer = playerMgr.FindPlayerByID(playerID)
+        if curPlayer:
+            NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+def DelTimeoutPersonalMail(curTime):
+    ## 清理超时个人邮件
+    mailMgr = DBDataMgr.GetMailMgr()
+    personalMailDict = mailMgr.GetAllPersonalMailDict()
+    for playerID, mailDict in personalMailDict.items():
+        notifyGUIDState = {}
+        for guid, mailObj in mailDict.items():
+            createTime = GameWorld.ChangeTimeStrToNum(mailObj.GetCreateTime())
+            limitTime = createTime + (mailObj.GetLimitDays() + 0) * 3600 * 24
+            if curTime < limitTime:
+                #GameWorld.DebugLog("个人邮件未超时,不删除! %s,createTime=%s,limitTime=%s" % (guid, mailObj.GetCreateTime(), GameWorld.ChangeTimeNumToStr(limitTime)), playerID)
+                continue
+            
+            mailState = mailObj.GetMailState()
+            mailItemCount = mailMgr.GetMailItemCount(guid)
+            if mailItemCount and mailState != ShareDefine.MailState_Got:
+                #GameWorld.DebugLog("个人邮件有物品邮件未领取不删除! %s" % guid, playerID)
+                continue
+            
+            mailMgr.DelPersonalMail(playerID, guid)
+            DataRecordPack.DR_MailDel(playerID, guid, "Timeout")
+            notifyGUIDState[guid] = ShareDefine.MailState_Del
+            
+        if not notifyGUIDState:
+            continue
+        
+        curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+        if curPlayer:
+            Sync_PlayerMailState(curPlayer, notifyGUIDState)
+    return
+
+def SendMailByKey(mailTypeKey, playerID, itemList, paramList=[], limitDays=7):
+    ## 发送个人邮件魔板
+    if not mailTypeKey:
+        mailTypeKey = ShareDefine.DefaultLackSpaceMailType
+    title = "<T>%s</T>" % mailTypeKey
+    text = "%s" % json.dumps(paramList, ensure_ascii=False)
+    SendMail(playerID, title, text, itemList, limitDays)
+    return
+
+def SendMail(playerID, title, text, itemList=None, limitDays=7, mailType=0):
+    ## 发送个人邮件
+    if itemList == None:
+        itemList = []
+        
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailMax = IpyGameDataPY.GetFuncCfg("PersonalMail", 1)
+    mailCnt = mailMgr.GetPersonalMailCount(playerID)
+    if mailCnt >= mailMax:
+        mailList = mailMgr.GetPersonalMails(playerID)
+        mailList.sort(key=lambda m: (-m.GetMailState(), m.GetCreateTime())) # 按邮件状态倒序、创建时间升序排
+        oneDelCnt = max(1, mailMax / 10) # 一次性删除最大数量的1/10封邮件
+        isStop = False
+        delCnt = 0
+        notifyGUIDState = {}
+        while delCnt < oneDelCnt and mailList and not isStop:
+            mailObj = mailList.pop(0) # 该列表是copy,可直接pop
+            if not mailObj:
+                continue
+            guid = mailObj.GetGUID()
+            mailState = mailObj.GetMailState()
+            # 已经删到未读或未领取的邮件,则强制停止,不再删除
+            if mailState == ShareDefine.MailState_UnRead or (mailState == ShareDefine.MailState_Read and mailMgr.GetMailItemCount(guid)):
+                isStop = True
+                if delCnt > 0:
+                    # 已经有删过了本封可不删
+                    break
+                
+            #至少要删1封
+            delCnt += 1
+            GameWorld.DebugLog("系统自动删除邮件: playerID=%s,guid=%s,mailState=%s,isStop=%s" % (playerID, guid, mailState, isStop))
+            mailMgr.DelPersonalMail(playerID, guid)
+            DataRecordPack.DR_MailDel(playerID, guid, "MaxCountLimiit")
+            notifyGUIDState[guid] = ShareDefine.MailState_Del
+            
+        if notifyGUIDState:
+            curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+            if curPlayer:
+                Sync_PlayerMailState(curPlayer, notifyGUIDState)
+                
+    mailObj = mailMgr.AddPersonalMail(playerID, title, text, itemList, limitDays, mailType)
+    CreateTime = mailObj.GetCreateTime()
+    GUID = mailObj.GetGUID()
+    
+    #添加流向
+    addDict = {"CreateTime":CreateTime, "Title":title, "Text":text, "LimitDays":limitDays, 
+               "ItemList":itemList, "ItemListLen":len(itemList)}
+    DataRecordPack.DR_MailSend(playerID, GUID, addDict)
+    
+    curPlayer = GameWorld.GetPlayerManager().FindPlayerByID(playerID)
+    if curPlayer:
+        Sync_PersonalMail(curPlayer, [mailObj.GetGUID()])
+    return
+
+def SendSeverMail(guid, title, text, itemList=None, limitDays=7, mailType=0, limitLV=0, limitLVType=0, checkState=0):
+    '''发送全服邮件
+    @param limitLV: 限制可领的最低等级
+    @param limitLVType: 等级达到后是否可领,默认不可
+    @param checkState: 是否需要审核,默认不需要
+    '''
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailObj = mailMgr.AddServerMail(guid, title, text, itemList, limitDays, mailType)
+    mailObj.SetLimitLV(limitLV)
+    mailObj.SetLimitLVType(limitLVType)
+    mailObj.SetCheckState(checkState)
+    if not checkState:
+        Sync_ServerMail(mailObj.GetGUID())
+    return
+
+def CheckServerMailResult(guid, isOK):
+    ## 审核全服邮件结果
+    # @param isOK: 是否通过审核
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailObj = mailMgr.GetServerMail(guid)
+    if not mailObj:
+        return
+    if not isOK:
+        return
+    mailObj.SetCheckState(0) # 设置为0,不需要审核了,即通过
+    Sync_ServerMail(guid)
+    return
+
+#// A5 37 请求邮件操作 #tagCMRequestMail
+#
+#struct    tagCMRequestMail
+#{
+#    tagHead        Head;
+#    char        GUID[36];    //邮件GUID,可传空,默认针对个人邮件的批量处理,如批量领取、批量删除已领邮件等;全服邮件暂时限制只能单封邮件处理
+#    BYTE        ReqType;        //0-设置已读,1-领取邮件,2-删除邮件
+#};
+def OnRequestMail(index, curPackData, tick):
+    curPlayer = GameWorld.GetPlayerManager().GetPlayerByIndex(index)
+    guid = curPackData.GUID
+    reqType = curPackData.ReqType
+    
+    if reqType == 2:
+        doMailDel(curPlayer, guid)
+    elif reqType == 1:
+        doMailGetAward(curPlayer, guid)
+    else:
+        doMailRead(curPlayer, guid)
+        
+    return
+
+def doMailRead(curPlayer, guid):
+    ## 执行邮件已读
+    if not guid:
+        return
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailState = mailMgr.GetPlayerMailState(guid, playerID)
+    if mailState == ShareDefine.MailState_UnRead:
+        mailState = mailMgr.SetPlayerMailState(guid, playerID, ShareDefine.MailState_Read)
+    Sync_PlayerMailState(curPlayer, {guid:mailState})
+    return
+
+def doMailGetAward(curPlayer, guid):
+    ## 执行邮件领取,支持批量
+    
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()
+    
+    # 批量处理,仅针对个人邮件
+    if not guid:
+        guidList = mailMgr.GetPersonalMailGuids(playerID)
+    else:
+        guidList = [guid]
+        
+    statItemList = []
+    statItemDict = {} # 统计累计
+    isPackSpaceEnough = True
+    notifyGUIDState = {}
+    for guid in guidList:
+        mailState = mailMgr.GetPlayerMailState(guid, playerID)
+        if mailState == ShareDefine.MailState_Unknown or mailState >= ShareDefine.MailState_Got:
+            GameWorld.DebugLog("邮件状态已领取或不能领取! mailState=%s,%s" % (mailState, guid), playerID)
+            continue
+        
+        mailItemCount = mailMgr.GetMailItemCount(guid)
+        if not mailItemCount:
+            GameWorld.DebugLog("邮件没有物品可领取! %s" % (guid), playerID)
+            continue
+        
+        needPackSpaceDict = {}
+        for index in range(mailItemCount):
+            mailItem = mailMgr.GetMailItemAt(guid, index)
+            itemID = mailItem.GetItemID()
+            itemCount = mailItem.GetCount()
+            isBind = mailItem.GetIsBind()
+            userData = mailItem.GetUserData()
+            
+            curItemData = GameWorld.GetGameData().GetItemByTypeID(itemID)
+            if not curItemData:
+                continue
+            
+            packType = ChConfig.GetItemPackType(curItemData)
+            if userData:
+                statItemList.append([itemID, itemCount, isBind, userData])
+                needSpace = 1
+            else:
+                key = (itemID, isBind)
+                if key not in statItemDict:
+                    statItemDict[key] = 0
+                    statItemList.append(key)
+                statItemDict[key] = statItemDict[key] + itemCount
+                
+                needSpace = ItemControler.GetItemNeedPackCount(packType, curItemData, itemCount, isBind)
+            needPackSpaceDict[packType] = needPackSpaceDict.get(packType, 0) + needSpace
+            
+        GameWorld.DebugLog("    guid=%s,needPackSpaceDict=%s" % (guid, needPackSpaceDict))
+        
+        for packType, needSpace in needPackSpaceDict.items():
+            if needSpace > ItemCommon.GetItemPackSpace(curPlayer, packType, needSpace):
+                PlayerControl.NotifyCode(curPlayer, "GeRen_chenxin_676165", [packType])
+                isPackSpaceEnough = False
+                break
+            
+        if not isPackSpaceEnough:
+            break
+        
+        notifyGUIDState[guid] = mailMgr.SetPlayerMailState(guid, playerID, ShareDefine.MailState_Got)
+        
+        for index in range(mailItemCount):
+            mailItem = mailMgr.GetMailItemAt(guid, index)
+            itemID = mailItem.GetItemID()
+            itemCount = mailItem.GetCount()
+            isBind = mailItem.GetIsBind()
+            userData = mailItem.GetUserData()
+            #setAttrDict = {} if not userData else eval(userData) 之后扩展有指定属性的,可参考砍树版本
+            if not ItemControler.GivePlayerItem(curPlayer, itemID, itemCount, isBind, [IPY_GameWorld.rptItem], 
+                                                event=[ChConfig.ItemGive_Mail, False, {"MailGUID":guid}]):
+                continue
+            
+        DataRecordPack.DR_MailGiveSuccess(playerID, guid)
+        
+    giveItemInfo = []
+    for statItem in statItemList:
+        if len(statItem) == 2:
+            itemID, isBind = statItem
+            itemCount = statItemDict.get(statItem, 0)
+            giveItemInfo.append([itemID, itemCount, isBind])
+        else:
+            giveItemInfo.append(statItem)
+    if giveItemInfo:
+        ItemControler.NotifyGiveAwardInfo(curPlayer, giveItemInfo, "Mail")
+        
+    Sync_PlayerMailState(curPlayer, notifyGUIDState)
+    return
+
+def doMailDel(curPlayer, guid, isGM=False):
+    ## 执行邮件删除
+    # @param isGM: 是否GM删除,无视物品是否已领取,强制删除
+    
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()
+    
+    # 批量处理,仅针对个人邮件
+    if not guid:
+        guidList = mailMgr.GetPersonalMailGuids(playerID)
+    else:
+        guidList = [guid]
+        
+    notifyGUIDState = {}
+    for guid in guidList:
+        mailState = mailMgr.GetPlayerMailState(guid, playerID)
+        if mailState >= ShareDefine.MailState_Del:
+            continue
+        if not isGM:
+            mailItemCount = mailMgr.GetMailItemCount(guid)
+            if mailItemCount and mailState != ShareDefine.MailState_Got:
+                GameWorld.DebugLog("有物品邮件未领取不能删除! %s" % guid, playerID)
+                continue
+            if not mailItemCount and mailState != ShareDefine.MailState_Read:
+                GameWorld.DebugLog("无物品邮件未读不能删除! %s" % guid, playerID)
+                continue
+            
+        notifyGUIDState[guid] = mailMgr.SetPlayerMailState(guid, playerID, ShareDefine.MailState_Del)
+        if isGM:
+            DataRecordPack.DR_MailDel(playerID, guid, "GMDel")
+        # 这里玩家主动删除的可不记录流向,因为未读未领取不允许主动删除,已领取的已有领取记录,所以可不记录
+        
+    Sync_PlayerMailState(curPlayer, notifyGUIDState)
+    return
+
+def Sync_ServerMail(guid):
+    ## 通知全服邮件
+    mailMgr = DBDataMgr.GetMailMgr()
+    serverMail = mailMgr.GetServerMail(guid)
+    if not serverMail:
+        return
+    packMail = __packMailObj(serverMail)
+    
+    clientPack = ChPyNetSendPack.tagMCMailList()
+    clientPack.IsServerMail = 1
+    clientPack.MailList = [packMail]
+    clientPack.Count = len(clientPack.MailList)
+    
+    playerManager = GameWorld.GetPlayerManager()
+    for i in range(playerManager.OnlineCount()):
+        curPlayer = playerManager.OnlineAt(i)
+        if not GameWorld.IsNormalPlayer(curPlayer):
+            continue
+        
+        mailState = CheckPlayerServerMailState(curPlayer, serverMail)
+        if mailState == ShareDefine.MailState_Unknown or mailState >= ShareDefine.MailState_Del:
+            continue
+        
+        packMail.MailState = mailState
+        NetPackCommon.SendFakePack(curPlayer, clientPack)
+        
+    return
+
+def Sync_PlayerServerMail(curPlayer):
+    ## 通知玩家相关的全服邮件
+    mailMgr = DBDataMgr.GetMailMgr()
+    guidList = mailMgr.GetServerMailGuids()
+    if not guidList:
+        return
+    
+    mailList = []
+    for guid in guidList:
+        mailObj = mailMgr.GetServerMail(guid)
+        if not mailObj:
+            continue
+        
+        mailState = CheckPlayerServerMailState(curPlayer, mailObj)
+        if mailState == ShareDefine.MailState_Unknown or mailState >= ShareDefine.MailState_Del:
+            continue
+        
+        mail = __packMailObj(mailObj)
+        mail.MailState = mailState
+        mailList.append(mail)
+        
+    if not mailList:
+        return
+    
+    clientPack = ChPyNetSendPack.tagMCMailList()
+    clientPack.MailList = mailList
+    clientPack.Count = len(clientPack.MailList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+def CheckPlayerServerMailState(curPlayer, serverMail):
+    ## 检查处理玩家全服邮件状态
+    # @return: 玩家该全服邮件的状态
+    
+    if not serverMail or serverMail.GetCheckState():
+        # 需要审核的,直接返回
+        return ShareDefine.MailState_Unknown
+    
+    playerID = curPlayer.GetPlayerID()
+    guid = serverMail.GetGUID()
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailState = mailMgr.GetPlayerMailState(guid, playerID)
+    if mailState != ShareDefine.MailState_Unknown:
+        return mailState
+    
+    mailState = ShareDefine.MailState_Unknown
+    limitLV = serverMail.GetLimitLV()
+    limitLVType = serverMail.GetLimitLVType()
+    playerLV = curPlayer.GetLV()
+    if playerLV < limitLV:
+        if not limitLVType:
+            return mailMgr.SetPlayerMailState(guid, playerID, ShareDefine.MailState_Limit)
+        return ShareDefine.MailState_Unknown
+    
+    return mailMgr.SetPlayerMailState(guid, playerID, ShareDefine.MailState_UnRead)
+
+def Sync_PersonalMail(curPlayer, guidList=None):
+    ## 通知玩家个人邮件
+    
+    playerID = curPlayer.GetPlayerID()
+    mailMgr = DBDataMgr.GetMailMgr()
+    if not guidList:
+        guidList = mailMgr.GetPersonalMailGuids(playerID)
+        
+    if not guidList:
+        return
+    
+    clientPack = ChPyNetSendPack.tagMCMailList()
+    clientPack.MailList = []
+    for guid in guidList:
+        mailObj = mailMgr.GetPersonalMail(playerID, guid)
+        if not mailObj:
+            continue
+        mail = __packMailObj(mailObj)
+        mail.MailState = mailObj.GetMailState()
+        clientPack.MailList.append(mail)
+    clientPack.Count = len(clientPack.MailList)
+    NetPackCommon.SendFakePack(curPlayer, clientPack)
+    return
+
+def __packMailObj(mailObj):
+    ## 打包通知的邮件
+    guid = mailObj.GetGUID()
+    mail = ChPyNetSendPack.tagMCMail()
+    mail.GUID = guid
+    mail.Type = mailObj.GetType()
+    mail.CreateTime = mailObj.GetCreateTime()
+    mail.LimitDays = mailObj.GetLimitDays()
+    mail.Title = mailObj.GetTitle()
+    mail.TitleLen = len(mail.Title)
+    mail.Text = mailObj.GetText()
+    mail.TextLen = len(mail.Text)
+    
+    mailMgr = DBDataMgr.GetMailMgr()
+    mailItemCount = mailMgr.GetMailItemCount(guid)
+    for index in range(mailItemCount):
+        mailItem = mailMgr.GetMailItemAt(guid, index)
+        if not mailItem:
+            continue
+        item = ChPyNetSendPack.tagMCMailItem()
+        item.ItemID = mailItem.GetItemID()
+        item.Count = mailItem.GetCount()
+        item.IsBind = mailItem.GetIsBind()
+        item.UserData = mailItem.GetUserData()
+        item.UserDataLen = len(item.UserData)
+        mail.Items.append(item)
+    mail.Count = len(mail.Items)
+    return mail
+
+def __packMailStateChange(notifyGUIDState):
+    clientPack = ChPyNetSendPack.tagMCMailStateChange()
+    clientPack.MailList = []
+    for guid, mailState in notifyGUIDState.items():
+        mail = ChPyNetSendPack.tagMCMailState()
+        mail.GUID = guid
+        mail.MailState = mailState
+        clientPack.MailList.append(mail)
+    clientPack.Count = len(clientPack.MailList)
+    return clientPack
+
+def Sync_PlayerMailState(curPlayer, notifyGUIDState):
+    ## 通知邮件状态变更
+    if not notifyGUIDState:
+        return
+    NetPackCommon.SendFakePack(curPlayer, __packMailStateChange(notifyGUIDState))
+    return
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
index c6b2c9a..e0d4613 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ShareDefine.py
@@ -2261,6 +2261,17 @@
 DefaultLackSpaceMailType = "DefaultLackSpace" # 背包空间不足时发放物品的默认邮件模板
 Def_Space = "<Space=1>" # <Space=空格数>
 
+# 邮件状态定义,每封邮件只能有一种状态,状态只升不降
+MailStateList = (
+MailState_Unknown, # 未知 0
+MailState_UnRead, # 未读 1
+MailState_Read, # 已读 2
+MailState_Got, # 已领取 3
+MailState_Del, # 已删除,全服邮件会用到 4
+MailState_Limit, # 限制接收邮件,全服邮件会用到 5
+) = range(6)
+
+
 # 手游不使用C++定义 enum            RoleEquipType
 # 装备位定义
 RoleEquipType = (

--
Gitblit v1.8.0