From a1f9aded73d7c162d51f87a5a6659799f2f08c21 Mon Sep 17 00:00:00 2001
From: hxp <ale99527@vip.qq.com>
Date: 星期一, 17 十月 2022 17:09:34 +0800
Subject: [PATCH] 9684 【越南】坐骑幻化支持时效性

---
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py            |  108 ++++++++++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Horse.py          |    7 +
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py         |  107 ++++++++++++++---
 ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py                                 |  108 ++++++++++++++++++
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py                   |    1 
 ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py |    9 +
 6 files changed, 322 insertions(+), 18 deletions(-)

diff --git a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
index 0919513..82feceb 100644
--- a/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
+++ b/ServerPython/CoreServerGroup/GameServer/Script/ChPyNetSendPack.py
@@ -20805,6 +20805,114 @@
 
 
 #------------------------------------------------------
+# A3 10 通知坐骑幻化时效信息 #tagMCHorseSkinTimeInfoList
+
+class  tagMCHorseSkinTimeInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("ID", c_int),    # 对应坐骑幻化表ID
+                  ("InvalidTime", c_int),    # 失效时间戳
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.ID = 0
+        self.InvalidTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCHorseSkinTimeInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A3 10 通知坐骑幻化时效信息 //tagMCHorseSkinTimeInfoList:
+                                ID:%d,
+                                InvalidTime:%d
+                                '''\
+                                %(
+                                self.ID,
+                                self.InvalidTime
+                                )
+        return DumpString
+
+
+class  tagMCHorseSkinTimeInfoList(Structure):
+    Head = tagHead()
+    TimeCnt = 0    #(BYTE TimeCnt)//个数
+    TimeInfoList = list()    #(vector<tagMCHorseSkinTimeInfo> TimeInfoList)// 数据列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x10
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.TimeCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.TimeCnt):
+            temTimeInfoList = tagMCHorseSkinTimeInfo()
+            _pos = temTimeInfoList.ReadData(_lpData, _pos)
+            self.TimeInfoList.append(temTimeInfoList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x10
+        self.TimeCnt = 0
+        self.TimeInfoList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.TimeCnt):
+            length += self.TimeInfoList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.TimeCnt)
+        for i in range(self.TimeCnt):
+            data = CommFunc.WriteString(data, self.TimeInfoList[i].GetLength(), self.TimeInfoList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                TimeCnt:%d,
+                                TimeInfoList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.TimeCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCHorseSkinTimeInfoList=tagMCHorseSkinTimeInfoList()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCHorseSkinTimeInfoList.Head.Cmd,m_NAtagMCHorseSkinTimeInfoList.Head.SubCmd))] = m_NAtagMCHorseSkinTimeInfoList
+
+
+#------------------------------------------------------
 # A3 38 投资理财信息 #tagMCInvestInfo
 
 class  tagMCInvestInfo(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
index 4a8be8a..1e48d1b 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChConfig.py
@@ -3996,6 +3996,7 @@
 Def_PDict_HorserLV = "HorserLV"  # 坐骑等级
 Def_PDict_HorserEatItemCount = "HorserEatItemCount"  # 当前阶已吃培养丹个数
 Def_PDict_HorserSkinPlusState = "HorserSkinPlusState"  # 坐骑幻化皮肤激活状态,按二进制位存储,每位代表幻化ID是否已激活
+Def_PDict_HorserSkinEndTime = "HorserSkinEndTime_%s"  # 坐骑幻化皮肤激活结束时间戳,仅针对非永久的幻化皮肤
 Def_PDict_HorserTrainLV = "HorserTrainLV_%s"  # 坐骑培养等阶,参数(培养类型)
 Def_PDict_HorserTrainItemCount = "HorserTrainItemCount_%s"  # 坐骑培养当前阶已吃培养丹个数,参数(培养类型)
 
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
index 0919513..82feceb 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/ChPyNetSendPack.py
@@ -20805,6 +20805,114 @@
 
 
 #------------------------------------------------------
+# A3 10 通知坐骑幻化时效信息 #tagMCHorseSkinTimeInfoList
+
+class  tagMCHorseSkinTimeInfo(Structure):
+    _pack_ = 1
+    _fields_ = [
+                  ("ID", c_int),    # 对应坐骑幻化表ID
+                  ("InvalidTime", c_int),    # 失效时间戳
+                  ]
+
+    def __init__(self):
+        self.Clear()
+        return
+
+    def ReadData(self, stringData, _pos=0, _len=0):
+        self.Clear()
+        memmove(addressof(self), stringData[_pos:], self.GetLength())
+        return _pos + self.GetLength()
+
+    def Clear(self):
+        self.ID = 0
+        self.InvalidTime = 0
+        return
+
+    def GetLength(self):
+        return sizeof(tagMCHorseSkinTimeInfo)
+
+    def GetBuffer(self):
+        return string_at(addressof(self), self.GetLength())
+
+    def OutputString(self):
+        DumpString = '''// A3 10 通知坐骑幻化时效信息 //tagMCHorseSkinTimeInfoList:
+                                ID:%d,
+                                InvalidTime:%d
+                                '''\
+                                %(
+                                self.ID,
+                                self.InvalidTime
+                                )
+        return DumpString
+
+
+class  tagMCHorseSkinTimeInfoList(Structure):
+    Head = tagHead()
+    TimeCnt = 0    #(BYTE TimeCnt)//个数
+    TimeInfoList = list()    #(vector<tagMCHorseSkinTimeInfo> TimeInfoList)// 数据列表
+    data = None
+
+    def __init__(self):
+        self.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x10
+        return
+
+    def ReadData(self, _lpData, _pos=0, _Len=0):
+        self.Clear()
+        _pos = self.Head.ReadData(_lpData, _pos)
+        self.TimeCnt,_pos = CommFunc.ReadBYTE(_lpData, _pos)
+        for i in range(self.TimeCnt):
+            temTimeInfoList = tagMCHorseSkinTimeInfo()
+            _pos = temTimeInfoList.ReadData(_lpData, _pos)
+            self.TimeInfoList.append(temTimeInfoList)
+        return _pos
+
+    def Clear(self):
+        self.Head = tagHead()
+        self.Head.Clear()
+        self.Head.Cmd = 0xA3
+        self.Head.SubCmd = 0x10
+        self.TimeCnt = 0
+        self.TimeInfoList = list()
+        return
+
+    def GetLength(self):
+        length = 0
+        length += self.Head.GetLength()
+        length += 1
+        for i in range(self.TimeCnt):
+            length += self.TimeInfoList[i].GetLength()
+
+        return length
+
+    def GetBuffer(self):
+        data = ''
+        data = CommFunc.WriteString(data, self.Head.GetLength(), self.Head.GetBuffer())
+        data = CommFunc.WriteBYTE(data, self.TimeCnt)
+        for i in range(self.TimeCnt):
+            data = CommFunc.WriteString(data, self.TimeInfoList[i].GetLength(), self.TimeInfoList[i].GetBuffer())
+        return data
+
+    def OutputString(self):
+        DumpString = '''
+                                Head:%s,
+                                TimeCnt:%d,
+                                TimeInfoList:%s
+                                '''\
+                                %(
+                                self.Head.OutputString(),
+                                self.TimeCnt,
+                                "..."
+                                )
+        return DumpString
+
+
+m_NAtagMCHorseSkinTimeInfoList=tagMCHorseSkinTimeInfoList()
+ChNetPackDict[eval("0x%02x%02x"%(m_NAtagMCHorseSkinTimeInfoList.Head.Cmd,m_NAtagMCHorseSkinTimeInfoList.Head.SubCmd))] = m_NAtagMCHorseSkinTimeInfoList
+
+
+#------------------------------------------------------
 # A3 38 投资理财信息 #tagMCInvestInfo
 
 class  tagMCInvestInfo(Structure):
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Horse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Horse.py
index 1f2b8e6..8d297b0 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Horse.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/GM/Commands/Horse.py
@@ -19,6 +19,7 @@
 import ChConfig
 import PlayerControl
 import PlayerHorse
+import IpyGameDataPY
 
 #---------------------------------------------------------------------
 #逻辑实现
@@ -45,6 +46,12 @@
                 PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainLV % trainType, 1)
                 PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainItemCount % trainType, 0)
                 
+            ipyDataMgr = IpyGameDataPY.IPY_Data()
+            for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
+                skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
+                skinID = skinPlusIpyData.GetID()
+                PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % skinID, 0)
+                
     elif len(msgList) == 2:
         lv, eatItemCount = msgList
         if lv < 1:
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
index 32586b7..faeee70 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerHorse.py
@@ -38,6 +38,8 @@
 import PlayerSuccess
 import PlayerPet
 
+import time
+
 
 Def_HorseEquipIndex = 5
 
@@ -172,15 +174,25 @@
     if not GameFuncComm.GetFuncCanUse(curPlayer, ShareDefine.GameFuncID_Horse):
         return
     
-    activateState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
-    if activateState & pow(2, activateID):
-        GameWorld.DebugLog("该坐骑已幻化!activateState=%s,activateID=%s" % (activateState, activateID))
-        return
-    
     ipyData = IpyGameDataPY.GetIpyGameData("HorseSkinPlus", activateID)
     if not ipyData:
         return
     
+    updSkinEndTime = 0
+    validTime = ipyData.GetSkinValidTime()
+    if validTime:
+        curTime = int(time.time())
+        skinEndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % activateID)
+        if curTime >= skinEndTime:
+            updSkinEndTime = curTime + validTime
+        else:
+            updSkinEndTime = skinEndTime + validTime
+    else:
+        activateState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
+        if activateState & pow(2, activateID):
+            GameWorld.DebugLog("该坐骑已幻化!activateState=%s,activateID=%s" % (activateState, activateID))
+            return
+        
     needItemID = ipyData.GetUnlockItemID()
     needItemCnt = ipyData.GetUnlockItemCnt()
     itemPack = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptItem)
@@ -190,16 +202,23 @@
         return
     ItemCommon.ReduceItem(curPlayer, itemPack, itemList, needItemCnt, False, ChConfig.ItemDel_Horse)
     
-    PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, activateState|pow(2, activateID))
-    
+    if validTime:
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % activateID, updSkinEndTime)
+        GameWorld.DebugLog("坐骑幻化时效 activateID=%s,updSkinEndTime=%s(%s)" % (activateID, updSkinEndTime, GameWorld.ChangeTimeNumToStr(updSkinEndTime)), playerID)
+    else:
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, activateState|pow(2, activateID))
+        
     playerEquip = curPlayer.GetItemManager().GetPack(IPY_GameWorld.rptEquip)
     itemRideHorse = playerEquip.GetAt(Def_HorseEquipIndex)
     #无指定道具
     if itemRideHorse.IsEmpty():
         DoChangeHorse(curPlayer, 2, activateID, tick)
         
-    Sync_HorseClassData(curPlayer)
-    GameWorld.Log("坐骑激活成功!activateID=%s" % (activateID), playerID)
+    if validTime:
+        SyncHorseSkinTimeInfo(curPlayer, activateID)
+    else:
+        Sync_HorseClassData(curPlayer)
+    GameWorld.Log("坐骑激活成功!activateID=%s,updSkinEndTime=%s" % (activateID, updSkinEndTime), playerID)
     
     # 刷属性,更新排行榜
     RefreshHorseAttr(curPlayer)
@@ -265,13 +284,10 @@
         
     # 按幻化
     elif chooseType == 2:
-        activateState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
-        if not activateState & pow(2, lvID):
-            GameWorld.DebugLog("坐骑幻化未激活,无法使用该坐骑皮肤!  activateState=%s,lvID=%s" % (activateState, lvID))
-            return
-        
         skinPlusIpyData = IpyGameDataPY.GetIpyGameData("HorseSkinPlus", lvID)
         if not skinPlusIpyData:
+            return
+        if not CheckHorseSkinState(curPlayer, skinPlusIpyData):
             return
         horseItemID = skinPlusIpyData.GetHorseSkinPlusID()
         
@@ -306,6 +322,31 @@
             
     return True
 
+def CheckHorseSkinState(curPlayer, skinPlusIpyData):
+    skinID = skinPlusIpyData.GetID()
+    if skinPlusIpyData.GetSkinValidTime():
+        skinEndTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
+        if not skinEndTime:
+            return False
+        curTime = int(time.time())
+        if curTime >= skinEndTime:
+            playerID = curPlayer.GetPlayerID()
+            GameWorld.Log("坐骑幻化已过期! skinID=%s,skinEndTime=%s(%s)" % (skinID, skinEndTime, GameWorld.ChangeTimeNumToStr(skinEndTime)), playerID)
+            # 发送过期通知邮件,重置为0
+            PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % skinID, 0)
+            
+            skinItemID = skinPlusIpyData.GetUnlockItemID()
+            addItemList = []
+            paramList = [skinItemID]
+            PlayerControl.SendMailByKey("HorseSkinInvalidNotify", [playerID], addItemList, paramList)
+            return False
+    else:
+        activateState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
+        if not activateState & pow(2, skinID):
+            #GameWorld.DebugLog("坐骑幻化未激活! skinID=%s,activateState=%s" % (skinID, activateState), curPlayer.GetPlayerID())
+            return False
+    return True
+
 def DoHorseOpen(curPlayer):
     ## 马匹功能开启
     horseLV = 1
@@ -318,6 +359,12 @@
     PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinPlusState, 0)
     for trainType in xrange(1, GetHorseTrainTypes() + 1):
         PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserTrainLV % trainType, 1)
+        
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
+        skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
+        skinID = skinPlusIpyData.GetID()
+        PlayerControl.NomalDictSetProperty(curPlayer, ChConfig.Def_PDict_HorserSkinEndTime % skinID, 0)
         
     horseID = ipyData.GetHorseSkinID()
     if not ItemCommon.FindItemInPackByItemID(curPlayer, horseID, IPY_GameWorld.rptEquip):
@@ -386,11 +433,9 @@
         
     # 幻化属性
     initFPAdd = 0 #初始战力
-    activateState = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState)
     for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
         skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
-        skinPlusID = skinPlusIpyData.GetID()
-        if not activateState & pow(2, skinPlusID):
+        if not CheckHorseSkinState(curPlayer, skinPlusIpyData):
             continue
         initFPAdd += skinPlusIpyData.GetInitFightPower()
         attrTypeList = skinPlusIpyData.GetAttrType()
@@ -643,6 +688,7 @@
             
     Sync_HorseClassData(curPlayer)
     SyncHorsePetSkinData(curPlayer)
+    SyncHorseSkinTimeInfo(curPlayer)
     return
 
 def Sync_HorseClassData(curPlayer):
@@ -837,4 +883,29 @@
     NetPackCommon.SendFakePack(curPlayer, packData)
     return
 
-        
+def SyncHorseSkinTimeInfo(curPlayer, skinID=None):
+    skinList = []
+    if skinID > 0:
+        timeInfo = ChPyNetSendPack.tagMCHorseSkinTimeInfo()
+        timeInfo.ID = skinID
+        timeInfo.InvalidTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
+        skinList.append(timeInfo)
+    else:
+        ipyDataMgr = IpyGameDataPY.IPY_Data()
+        for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
+            skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
+            skinID = skinPlusIpyData.GetID()
+            if not skinPlusIpyData.GetSkinValidTime():
+                continue
+            timeInfo = ChPyNetSendPack.tagMCHorseSkinTimeInfo()
+            timeInfo.ID = skinID
+            timeInfo.InvalidTime = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
+            skinList.append(timeInfo)
+            
+    packData = ChPyNetSendPack.tagMCHorseSkinTimeInfoList()
+    packData.Clear()
+    packData.TimeInfoList = skinList
+    packData.TimeCnt = len(packData.TimeInfoList)
+    NetPackCommon.SendFakePack(curPlayer, packData)
+    return
+
diff --git a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
index 40ca12b..12eaefc 100644
--- a/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
+++ b/ServerPython/ZoneServerGroup/map1_8G/MapServer/MapServerData/Script/Player/PlayerViewCacheTube.py
@@ -311,9 +311,18 @@
 
 ## 坐骑信息
 def __GetHorseInfo(curPlayer):
+    skinEndTimeInfo = {}
+    ipyDataMgr = IpyGameDataPY.IPY_Data()
+    for index in xrange(ipyDataMgr.GetHorseSkinPlusCount()):
+        skinPlusIpyData = ipyDataMgr.GetHorseSkinPlusByIndex(index)
+        skinID = skinPlusIpyData.GetID()
+        if not skinPlusIpyData.GetSkinValidTime():
+            continue
+        skinEndTimeInfo[skinID] = curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinEndTime % skinID)
     horseInfo = {"LV":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserLV),
                  "EatItemCount":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserEatItemCount),
                  "SkinPlusState":curPlayer.NomalDictGetProperty(ChConfig.Def_PDict_HorserSkinPlusState),
+                 "SkinEndTimeInfo":skinEndTimeInfo,
                  }
     return horseInfo
 

--
Gitblit v1.8.0