From 328648da94a07437fc46024f3e9b7e48c2e2ae38 Mon Sep 17 00:00:00 2001 From: hxp <ale99527@vip.qq.com> Date: 星期二, 29 十月 2024 17:15:29 +0800 Subject: [PATCH] 10275 【越南】【英语】【砍树】【tqxbqy】仙盟珍宝阁-后端 --- ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py | 437 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 381 insertions(+), 56 deletions(-) diff --git a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py b/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py index 23b3ac8..c287f47 100644 --- a/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py +++ b/ServerPython/CoreServerGroup/GameServer/Script/GameWorld.py @@ -15,12 +15,10 @@ # @change: "2013-03-20 18:00" Alee 添加DEBUG输出函数DebugLog # @change: "2013-09-10 20:10" Alee 函数DebugLog改用GetDebugLevel才有效 # @change: "2014-05-16 10:30" xmnathan 增加交易所管理器接口 -# @change: "2014-08-01 15:30" xmnathan 增加天梯竞技场管理器接口 # @change: "2014-09-22 10:00" xmnathan 增加GM工具补偿管理器接口 # @change: "2015-01-14 00:30" hxp 增加服务器平台区服ID获取 # @change: "2015-01-14 20:30" hxp 增加CanHappen函数 # @change: "2015-06-08 20:30" hxp 增加获取channelCode -# @change: "2015-09-24 16:00" hxp 增加IsMergeOpen # @change: "2015-10-22 23:00" hxp 增加获取子服serverID # @change: "2016-03-28 17:00" hxp 增加创角改名GetPlayerFullName # @change: "2016-07-18 19:00" hxp 增加获取平台账号 @@ -33,6 +31,7 @@ import IpyGameDataPY import PyGameData import ReadChConfig +import PlayerDBGSEvent import ConfigParser import GameConfig import ShareDefine @@ -44,6 +43,7 @@ import time import random import copy +import urllib #import psyco #--------------------------------------------------------------------- GameWorldData = IPY_GameServer.IPY_GameWorld() @@ -115,6 +115,14 @@ result = int(input) return result + except ValueError: + return defValue + +def ToFloat(input, defValue = 0): + try: + result = float(input) + return result + except ValueError: return defValue @@ -303,14 +311,6 @@ global GameWorldData return GameWorldData.GetBourseManager() -## 竞技场管理器 -# @param -# @return HightLadderMgr -# @remarks 函数详细说明. -def GetHightLadderMgr(): - global GameWorldData - return GameWorldData.GetHighLadderManager() - ## GM工具补偿管理器 # @param # @return HightLadderMgr @@ -418,18 +418,33 @@ return GetGameWorld().GetServerVersion() def GetServerGroupID(): - # 服务器组ID: 代表一台服务器中包含的合服、混服的各平台区服组成的一个独立游戏区服服务器环境, 用某个ID来代表此服务器 - # 在跨服环境中,此ID就是代表来自同一台服务器子服的玩家 - # 服务器组ID生成规则: DB平台标识对应编号 * 1000000 + DB配置的ServerID - # ServerID支持范围: 1~999999 - # 平台编号支持范围:1~2000 - platform = GetPlatform() - serverNum = GetPlatformServerNum(platform) - if not serverNum: - return 0 - serverNum = max(1, min(2000, serverNum)) - serverID = max(1, min(999999, GetServerID())) - return serverNum * 1000000 + serverID + ## 服务器组ID,必须唯一,代表这台物理服务器 + return ToIntDef(ReadChConfig.GetPyMongoConfig("platform", "GroupID"), 0) + +def GetMainServerID(serverID): + ## 获取服务器ID所属主服ID + ServerIDMainServerDict = IpyGameDataPY.GetConfigEx("ServerIDMainServerDict") + if ServerIDMainServerDict == None: + filePath = ChConfig.GetDBPath() + ("\\MixServerMap_%s.json" % GetPlatform()) + if not os.path.isfile(filePath): + SendGameErrorEx("GetMainServerIDError", "file can not found. %s" % filePath) + else: + fileObj = open(filePath, 'rb') + content = fileObj.read() + fileObj.close() + MixServerMapDict = eval(content) + + ServerIDMainServerDict = {} + for mainServerIDStr, serverIDList in MixServerMapDict.items(): + mainServerID = int(mainServerIDStr) + for sID in serverIDList: + ServerIDMainServerDict[sID] = mainServerID + IpyGameDataPY.SetConfigEx("ServerIDMainServerDict", ServerIDMainServerDict) + Log("加载 ServerIDMainServerDict=%s" % ServerIDMainServerDict) + + if not ServerIDMainServerDict: + return serverID + return ServerIDMainServerDict.get(serverID, serverID) def GetPlatformServerNum(platform): # 获取服务器的平台编号 @@ -447,15 +462,19 @@ def GetPlatform(): return ReadChConfig.GetPyMongoConfig("platform", "PlatformName") -##获得当前服务器ID -# @param 无 -# @return -def GetServerID(): - return ToIntDef(GetServerSID()[1:], 0) +#=============================================================================== +# ##获得当前服务器ID +# # @param 无 +# # @return +# def GetServerID(): +# return ToIntDef(GetServerSID()[1:], 0) +#=============================================================================== -def GetServerSID(): - ##获得当前服务器ID, 带s的 - return ReadChConfig.GetPyMongoConfig("platform", "ServerID") +#=============================================================================== +# def GetServerSID(): +# ##获得当前服务器ID, 带s的 +# return ReadChConfig.GetPyMongoConfig("platform", "ServerID") +#=============================================================================== Def_AccID_Split_Sign = "@" @@ -463,10 +482,10 @@ ##获取玩家所属区服ID # @param curPlayer # @return -def GetPlayerServerID(curPlayer): - accID = curPlayer.GetAccID() +def GetPlayerServerID(curPlayer): return GetAccIDServerID(curPlayer.GetAccID()) +def GetAccIDServerID(accID): infoList = accID.split(Def_AccID_Split_Sign) - return 0 if len(infoList) < 3 else int(infoList[-1][1:]) + return 0 if len(infoList) < 3 else ToIntDef(infoList[-1][1:]) def GetPlayerServerSID(curPlayer): # 返回含s的serverID @@ -480,12 +499,39 @@ mainServerID = ToIntDef(ReadChConfig.GetPyMongoConfig("platform", "%sMainServerID" % accIDPlatform), None) if mainServerID != None: return mainServerID - return GetServerID() + return 0 + +def CheckServerIDInList(serverID, serverIDList): + if serverIDList == None: + return False + if not serverIDList: + return True + for serverIDInfo in serverIDList: + if (isinstance(serverIDInfo, tuple) and serverIDInfo[0] <= serverID <= serverIDInfo[1]) \ + or (isinstance(serverIDInfo, list) and serverIDInfo[0] <= serverID <= serverIDInfo[1]) \ + or (isinstance(serverIDInfo, int) and serverIDInfo == serverID): + return True + return False + +#=============================================================================== +# 平台ID = appid +# 运营ID = spid +# 服务器账号更改 +# 原先账号组合为 +# 运营提供的玩家账号ID + 平台ID + 区服组成 +# +# 因为IOS和安卓在支持混服的情况下,还要能支持同一个角色号,故引入一个新的运营ID(客户端打包配置),以保证玩家账号为同一个 +# 账号组合变更 +# 运营提供的玩家账号ID + 运营ID + 区服组成 +# +# 原游戏中获取平台ID接口变更 +# 1. db 从数据库中的账号表获取AppID +# 2. GameServer和MapServer 接口改为GetOperator 参考 GetPlayerPlatform +#=============================================================================== ##获取玩家所属平台 -def GetPlayerPlatform(gameAccID): - infoList = gameAccID.split(Def_AccID_Split_Sign) - return "" if len(infoList) < 3 else infoList[-2] +def GetPlayerPlatform(curPlayer): + return curPlayer.GetAccountInfo().GetOperator() ##获取平台账号 def GetPlatformAccID(gameAccID): @@ -535,12 +581,103 @@ return tagPlayer.GetRealMapID() #--------------------------------------------------------------------- + +def GetTemplateID(ipyData, cfgID, dayIndex): + if cfgID == None or dayIndex == None or not ipyData: + return 0 + templateIDList = ipyData.GetTemplateIDList() + if not templateIDList: + return 0 + templateID = templateIDList[-1] if dayIndex >= len(templateIDList) else templateIDList[dayIndex] + return templateID + +def GetTemplateIDByList(templateIDList, dayIndex): + if dayIndex == None: + return 0 + if not templateIDList: + return 0 + templateID = templateIDList[-1] if dayIndex >= len(templateIDList) else templateIDList[dayIndex] + return templateID + +def GetOperationActionDateStr(ipyData): + ## 获取运营活动对应日期,存数字代表开服天配置,需要转化为对应的日期 + curDateTime = datetime.datetime.today() + startDateStr = ipyData.GetStartDate() + endDateStr = ipyData.GetEndDate() + if not startDateStr: + startDateStr = "%d-%d-%d" % (curDateTime.year, curDateTime.month, curDateTime.day) + if not endDateStr: + endDateStr = "%d-%d-%d" % (curDateTime.year, curDateTime.month, curDateTime.day) + + # 日期直接返回 + if startDateStr.count("-") == 2 and "W" not in startDateStr: + return startDateStr, endDateStr + + # 开服天 + if startDateStr.isdigit(): + diffDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_ServerDay) + 1 + startDateTime = curDateTime + datetime.timedelta(days=(int(startDateStr)-diffDay)) + endDateTime = curDateTime + datetime.timedelta(days=(int(endDateStr)-diffDay)) + + # 合服天 + elif startDateStr.startswith("Mix"): + diffDay = PlayerDBGSEvent.GetDBGSTrig_ByKey(PlayerDBGSEvent.Def_MixServerDay) + 1 + startDateTime = curDateTime + datetime.timedelta(days=(int(startDateStr[3:])-diffDay)) + endDateTime = curDateTime + datetime.timedelta(days=(int(endDateStr[3:])-diffDay)) + + # 周循环, 直接配置 Wx 或 Wx|有效开始日期 或 Wx|有效结束日期 + elif startDateStr.startswith("W"): + curWeekday = curDateTime.weekday() + 1 + + startWeekDateInfo = startDateStr.split("|") + startWeekInfo = startWeekDateInfo[0] + startWeekday = int(startWeekInfo[1:]) + startDateTime = curDateTime + datetime.timedelta(days=(startWeekday-curWeekday)) + + # 限制开启循环日期 + if len(startWeekDateInfo) > 1: + startLoopDateStr = startWeekDateInfo[1] + startLoopDateTime = ChangeStrToDatetime(startLoopDateStr, ChConfig.TYPE_Time_YmdFormat) + if startLoopDateTime > startDateTime: + startDateTime = startLoopDateTime + + startWeekDay = startDateTime.weekday() + 1 # 实际开启活动是周几,不一定和配置的周几一样,可能从中间被截断开始 + + # 处理周循环的结束日期 + endWeekDateInfo = endDateStr.split("|") + endWeekInfo = endWeekDateInfo[0] + endWeekday = int(endWeekInfo[1:]) + endDateTime = startDateTime + datetime.timedelta(days=(endWeekday-startWeekDay)) + if len(endWeekDateInfo) > 1: + endLoopDateStr = endWeekDateInfo[1] + endLoopDateTime = ChangeStrToDatetime(endLoopDateStr, ChConfig.TYPE_Time_YmdFormat) + if endDateTime > endLoopDateTime: + endDateTime = endLoopDateTime + + # 只配置结束日期的时候可能导致开始日期计算出来比结束日期还大,即当前时间超过结束日期,且 配置还存在的情况 + if startDateTime > endDateTime: + startDateTime = endDateTime # 反正都无法开启,随便给个日期,不超过结束日期即可 + + # 默认 + else: + startDateTime = curDateTime + endDateTime = curDateTime + + startDateStr = "%d-%d-%d" % (startDateTime.year, startDateTime.month, startDateTime.day) + endDateStr = "%d-%d-%d" % (endDateTime.year, endDateTime.month, endDateTime.day) + return startDateStr, endDateStr + ##获得系统时间 # @param 无意义 # @return 系统时间 # @remarks 获得系统时间 def GetServerTime(): return datetime.datetime.today() + +def GetWeekOfYear(): + ## 一年中的第几周, 1代表第1周 + return datetime.datetime.isocalendar(datetime.datetime.today())[1] + #--------------------------------------------------------------------- ##获取2个时间之间差异的TimeDelta类实例 # @param compareTimeStr 比较的时间字符, 如"2010-05-26 11:21:25" @@ -599,6 +736,15 @@ return pastTimeDelta.days * 24 * 60 * 60 + pastTimeDelta.seconds #--------------------------------------------------------------------- + +def GetDiff_Day(timeA , timeB): + ## 获取 timeA - timeB 相差的日期天数 + dateTimeA = ChangeTimeNumToDatetime(timeA) + dateTimeA = datetime.datetime(dateTimeA.year, dateTimeA.month, dateTimeA.day, 0, 0, 0) + dateTimeB = ChangeTimeNumToDatetime(timeB) + dateTimeB = datetime.datetime(dateTimeB.year, dateTimeB.month, dateTimeB.day, 0, 0, 0) + return (dateTimeA - dateTimeB).days + ##设置世界服务器字典 # @param key 字典值 # @param tick 时间戳 @@ -810,6 +956,8 @@ timeStr = datetime.datetime.strptime(timeStr, timeFormat).timetuple() return int(time.mktime(timeStr)) +def ChangeStrToDatetime(timeStr, timeFormat=ChConfig.TYPE_Time_Format): + return datetime.datetime.strptime(timeStr, timeFormat) ##获取与当前时间相差天数的datetime格式数据 # @param diffDays 差距天数 @@ -818,23 +966,38 @@ def GetDatetimeBySubDays(diffDays): return datetime.datetime.today() - datetime.timedelta(days = diffDays) +def GetDailyDateStr(): + ## 获取日常日期 yyyy-MM-dd + curDate = GetServerTime() + if curDate.hour >= ShareDefine.Def_OnEventHour: + return "%d-%s-%s" % (curDate.year, curDate.month, curDate.day) + preDate = curDate - datetime.timedelta(days = -1) # 属于前一天 + return "%s-%s-%s" % (preDate.year, preDate.month, preDate.day) -## 是否跨服服务器 -# @param None None -# @return -def IsMergeServer(): - config = GameConfig.GetConfig() - - return config.get('IsMergeWarServe', 0) +def IsCrossServer(): + ## 是否跨服服务器 + return ToIntDef(ReadChConfig.GetPyMongoConfig("Merge", "IsMergeServer"), 0) -## 是否开启跨服活动 -# @param None None -# @return -def IsMergeOpen(): - config = GameConfig.GetConfig() - # 如果跨服服务器IP有配置, 或本身就是跨服服务器, 则代表开启 - return True if (config.get('MergeServerIP') or config.get('IsMergeWarServe')) else False +def IsCrossRealmOpen(): + ## 是否开启跨服活动 + return ReadChConfig.GetPyMongoConfig("Merge", "MergeServerIP") or IsCrossServer() +def GetCrossZoneName(): + ## 跨服服务器分区名,标记一起跨服分区的名字,配置在跨服服务器,子服不用配置,由跨服服务器同步 + if IsCrossServer(): + return ReadChConfig.GetPyMongoConfig("Merge", "CrossZoneName") + return PyGameData.g_crossZoneName + +def GetCrossServerTimeStr(): + ## 跨服服务器时间 + if IsCrossServer(): + return GetCurrentDataTimeStr() + lastCrossServerTime, lastServerTime, _ = PyGameData.g_crossServerTimeInfo + if not lastCrossServerTime: + return GetCurrentDataTimeStr() + curTime = int(time.time()) + crossServerTime = lastCrossServerTime + (curTime - lastServerTime) + return ChangeTimeNumToStr(crossServerTime) ## 获取玩家的区服名,仅在跨服有效 # @param curPlayer 玩家实例 @@ -877,13 +1040,36 @@ return fullName +def MergeItemList(itemList): + ## 合并物品列表,将相同物品数量合并 + itemDict = {} + for itemInfo in itemList: + if len(itemInfo) == 3: + itemID, itemCount, isAuctionItem = itemInfo + elif len(itemInfo) == 2: + itemID, itemCount = itemInfo + isAuctionItem = None + else: + continue + key = (itemID, isAuctionItem) + itemDict[key] = itemDict.get(key, 0) + itemCount + + mItemList = [] + for key, itemCount in itemDict.items(): + itemID, isAuctionItem = key + if isAuctionItem == None: + mItemList.append([itemID, itemCount]) + else: + mItemList.append([itemID, itemCount, isAuctionItem]) + return mItemList ## 从列表中产生物品,[[几率,object], ....],万分率 # @param itemList 待选列表 # @return object def GetResultByRandomList(randList, defValue=None, maxRateValue=ChConfig.Def_MaxRateValue): - rate = random.randint(0, maxRateValue) - + if not randList: + return defValue + rate = random.randint(0, randList[-1][0]) return GetResultByRiseList(randList, rate, defValue) ## 增长列表(类似饼图)从中获得指定的信息 @@ -899,6 +1085,62 @@ return smallList[1] return defValue + +## 从列表中产生物品,[[权重, object], ....] +# @param weightList 待选列表 +def GetResultByWeightList(weightList, defValue=None): + randList = [] + weight = 0 + for info in weightList: + weight += info[0] + randList.append([weight, info[1] if len(info) == 2 else info[1:]]) + if not randList: + return defValue + rate = random.randint(1, randList[-1][0]) + return GetResultByRiseList(randList, rate, defValue) + +## 获得对应数位的值 +# @param numValue 数值 +# @param dataIndex 数位索引 +# @return 获得的值 +def GetDataByDigitPlace(numValue, dataIndex): + return (numValue/pow(10, dataIndex))%10 + + +## 设置对应数位的值 +# @param numValue 数值 +# @param dataIndex 数位索引 +# @param dataValue 当前修改数值 +# @return 获得的值 +def ChangeDataByDigitPlace(numValue, dataIndex, dataValue): + + if dataValue < 0 or dataValue > 9 or dataIndex > ShareDefine.Def_PDictDigitCnt: + return numValue + + # 获得对应数位的值 + lastTagLV = GetDataByDigitPlace(numValue, dataIndex) + + numValue += pow(10, dataIndex)*(dataValue - lastTagLV) + return numValue + +def GetBitValue(dataValue, index): + """ 得到某个字节值中某一位(Bit)的值 + @param dataValue: 待取值的字节值 + @param index: 待读取位的序号,从右向左0开始,0-7为一个完整字节的8个位 + @return: 返回读取该位的值,0或1 + """ + return 1 if dataValue & (1 << index) else 0 + +def SetBitValue(dataValue, index, val): + """ 更改某个字节值中某一位(Bit)的值 + @param dataValue: 准备更改的字节原值 + @param index: 待更改位的序号,从右向左0开始,0-7为一个完整字节的8个位 + @param val: 目标位预更改的值,0或1 + @return: 返回更改后字节的值 + """ + if val: + return dataValue | (1 << index) + return dataValue & ~(1 << index) ## 根据字典key获取value值 # @return @@ -936,6 +1178,24 @@ adict.update(bdict) return adict +## 根据排行获取名次对应值 +# @param orderDict {名次:obj, ...} 名次支持段跳跃 +# @param order 名次,从1开始 +# @param isDefaultLast 找不到的名次是否默认取最后一名的 +# @return obj or None +def GetOrderValueByDict(orderDict, order, isDefaultLast=True, defaultValue=None): + if order in orderDict: + return orderDict[order] + + orderList = sorted(orderDict.keys()) + if order > 0: + for dOrder in orderList: + if order <= dOrder: + return orderDict[dOrder] + + # 找不到的默认取最后一名 + return orderDict[orderList[-1]] if isDefaultLast else defaultValue + ##概率相关, 这个事件是否能够出现 # @param rate 基础几率 # @param maxRate 最大几率 @@ -947,15 +1207,34 @@ return 0 -def DebugAnswer(curPlayer, text): +def DebugAnswer(curPlayer, text, isLog=True): '''转码后再发DebugAnswer''' #=========================================================================== # if not GetGameWorld().GetDebugLevel(): # return #=========================================================================== - DebugLog(text) + if isLog: + DebugLog(text) text = text.decode(ShareDefine.Def_Game_Character_Encoding).encode(GetCharacterEncoding()) curPlayer.DebugAnswer(text) + return + +def CrossServerMsg_DebugAnswer(msgData): + playerID, text = msgData + curPlayer = GetPlayerManager().FindPlayerByID(playerID) + if not curPlayer: + return + curPlayer.DebugAnswer(text) + return + +def DebugAnswerCross(playerID, serverGroupID, text): + DebugLog(text) + text = text.decode(ShareDefine.Def_Game_Character_Encoding).encode(GetCharacterEncoding()) + + import CrossRealmMsg + dataMsg = [playerID, text] + serverGroupIDList = [serverGroupID] + CrossRealmMsg.SendMsgToClientServer(ShareDefine.CrossServerMsg_DebugAnswer, dataMsg, serverGroupIDList) return def GetMap(mapID): return IpyGameDataPY.GetIpyGameData("ChinMap", mapID) @@ -985,4 +1264,50 @@ except: return inputText - return inputText \ No newline at end of file + return inputText + +# 服务器默认GBK,转配置编码如UTF8,一般用于与显示层交互,不判断是否base64 +def CodeToGBK(inputText): + encodingList = ReadChConfig.GetEvalChConfig("EncodingTex") + + if len(encodingList) != 2: + ErrLog("EncodingTex.txt Error len != 2") + return inputText + + try: + return inputText.decode(GetCharacterEncoding()).encode(ShareDefine.Def_Game_Character_Encoding) + except: + return inputText + + return inputText + +def RaiseException(errorMsg, playerID=0): + ## 处理抛出异常信息,debug下直接抛出异常报错信息,否则发送运维邮件提醒 + ErrLog(errorMsg, playerID) + if GetGameWorld().GetDebugLevel(): + raise Exception(errorMsg) + else: + SendGameError("GameServerRaiseException", errorMsg) + return + +def SendGameErrorEx(errType, msgInfo="", playerID=0): + ErrLog("SendGameErrorEx: %s -> %s" % (errType, msgInfo), playerID) + if GetGameWorld().GetDebugLevel(): + raise Exception("%s -> %s" % (errType, msgInfo)) + else: + SendGameError(errType, msgInfo) + return + +def SendGameError(errType, msgInfo=""): + ''' 向运维发送邮件,用于需要紧急处理的信息 + @param errType: 错误类型,自定义即可 + @param msgInfo: 错误信息,可选 + ''' + getUrl = ReadChConfig.GetPyMongoConfig("EventReport", "OpenStateUrl") + groupID = ReadChConfig.GetPyMongoConfig("platform", "GroupID") + userDBName = ReadChConfig.GetPyMongoConfig("connect", "USER_DB_NAME") + getUrl = getUrl + "?Type=%s&groupID=%s&userDBName=%s"%(errType, groupID, userDBName) + if msgInfo: + getUrl = getUrl + "&MsgInfo=%s" % urllib.quote_plus(msgInfo) + GetGameWorld().EventReport_EventReport("", "", "", "", 0, getUrl) + return -- Gitblit v1.8.0