You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1079 lines
38 KiB
1079 lines
38 KiB
/*
|
|
Sog 游戏基础库
|
|
2016 by zouwei
|
|
*/
|
|
|
|
using System;
|
|
using System.IO;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
|
|
using Sog;
|
|
using ProtoCSStruct;
|
|
|
|
namespace Version
|
|
{
|
|
public class FileVersionList
|
|
{
|
|
public Dictionary<string, string> m_fileVerDict = new Dictionary<string, string>();
|
|
|
|
private string ReadFromFileThenAdd(string filename)
|
|
{
|
|
TraceLog.Trace("FileVersionList.ReadFromFile file {0}", filename);
|
|
//已经有了
|
|
if (m_fileVerDict.TryGetValue(filename,out string oldVersion))
|
|
{
|
|
TraceLog.Trace("FileVersionList.ReadFromFileThenAdd file {0} had read value {1}", filename, oldVersion);
|
|
return null;
|
|
}
|
|
if (File.Exists(filename))
|
|
{
|
|
string[] alllines = File.ReadAllLines(filename);
|
|
string version = null;
|
|
if (alllines.Length > 0)
|
|
{
|
|
version = alllines[0];
|
|
}
|
|
if (version != null)
|
|
{
|
|
version.Replace("\n", "");
|
|
m_fileVerDict.Add(filename, version);
|
|
|
|
TraceLog.Trace("FileVersionList.ReadFromFile file {0} version {1}", filename, version);
|
|
|
|
return version;
|
|
}
|
|
}
|
|
|
|
//文件不存在,返回空
|
|
TraceLog.Trace("FileVersionList.ReadFromFileThenAdd file {0} not exists", filename);
|
|
m_fileVerDict.Add(filename, string.Empty);
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
private string GetVersionByFileName(string filename)
|
|
{
|
|
TraceLog.Trace("FileVersionList.GetVersionByFileName file {0}", filename);
|
|
if (!m_fileVerDict.TryGetValue(filename, out string oldVersion))
|
|
{
|
|
TraceLog.Trace("FileVersionList.GetVersionByFileName file {0} not in dictionary", filename);
|
|
return null;
|
|
}
|
|
return oldVersion;
|
|
}
|
|
|
|
public string GetFileVersion(string filename)
|
|
{
|
|
string versionFile = filename.Substring(0,filename.LastIndexOf('/')) + "/fileversion";
|
|
TraceLog.Trace("FileVersionList.GetVersion file {0} version file {1}", filename, versionFile);
|
|
string version = GetVersionByFileName(versionFile);
|
|
if(version == null)
|
|
{
|
|
version = ReadFromFileThenAdd(versionFile);
|
|
}
|
|
return version;
|
|
}
|
|
|
|
public void ClearAll()
|
|
{
|
|
m_fileVerDict.Clear();
|
|
}
|
|
|
|
}
|
|
|
|
public class PatchFileMd5Info
|
|
{
|
|
public string FileFullPath;
|
|
public string Md5;
|
|
public int FileSize;
|
|
public long CreateTimeMs;
|
|
public long ModifyTimeMs;
|
|
}
|
|
|
|
public class VersionSvc
|
|
{
|
|
private VersionServerConfig m_serverConfig;
|
|
|
|
private ServerApp m_app;
|
|
private const string VERSION_FILE_NAME = "version";
|
|
private const string VERSION_FILE_NAME_DEBUG = "version_debug";
|
|
|
|
private const string AndroidStr = "android";
|
|
|
|
private const string md5_cache_path = "../cfg/version_md5_cache";
|
|
private const string md5_cache_file = "md5info";
|
|
private const string md5_cache_file_path = md5_cache_path + "/" + md5_cache_file;
|
|
|
|
|
|
public void InitVersionServerConfig()
|
|
{
|
|
m_serverConfig = VersionServerUtils.GetServerConfig();
|
|
|
|
TraceLog.Debug("read serverConfig from {0}", m_app.AppParam.ServerConfig.configfile);
|
|
TraceLog.Debug("downloadUrl:{0}", m_serverConfig.downloadUrl);
|
|
TraceLog.Debug("versionFileBasePath:{0}", m_serverConfig.versionFileBasePath);
|
|
TraceLog.Debug("lobbyGateUrl:{0}", m_serverConfig.accountGateUrl[0]);
|
|
TraceLog.Debug("packageName:{0}", m_serverConfig.packageName);
|
|
|
|
ReadMd5CacheFile();
|
|
}
|
|
|
|
//最新版本的cache,以os+language为key
|
|
private Dictionary<string, long> m_curVersionTable = new Dictionary<string, long>();
|
|
|
|
//补丁文件MD5 cache
|
|
private Dictionary<string, PatchFileMd5Info> m_patchFileMd5Table = new Dictionary<string, PatchFileMd5Info>();
|
|
|
|
// 热更包MD5
|
|
private Dictionary<string, PatchFileMd5Info> m_hotPatchFileMd5 = new Dictionary<string, PatchFileMd5Info>();
|
|
|
|
// aab MD5
|
|
private Dictionary<string, PatchFileMd5Info> m_aabPatchFileMd5 = new Dictionary<string, PatchFileMd5Info>();
|
|
|
|
|
|
private Dictionary<string, PatchFileMd5Info> m_allPatchFileMd5 = new Dictionary<string, PatchFileMd5Info>();
|
|
|
|
|
|
private FileVersionList m_fileVersionList = new FileVersionList();
|
|
|
|
public VersionSvc(ServerApp app)
|
|
{
|
|
m_app = app;
|
|
}
|
|
|
|
public void ClearAllCache()
|
|
{
|
|
m_curVersionTable.Clear();
|
|
m_patchFileMd5Table.Clear();
|
|
m_hotPatchFileMd5.Clear();
|
|
m_aabPatchFileMd5.Clear();
|
|
|
|
m_fileVersionList.ClearAll();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
m_app = null;
|
|
m_serverConfig = null;
|
|
}
|
|
|
|
public bool IsSkipCheckVersion()
|
|
{
|
|
return m_serverConfig.skipCheckVersion;
|
|
}
|
|
|
|
public bool IsValidOs(ref VersionInfo versionInfo)
|
|
{
|
|
if (versionInfo.Os.Equals("android")
|
|
|| versionInfo.Os.Equals("ios")
|
|
|| versionInfo.Os.Equals("win"))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public string GetMd5CachePath()
|
|
{
|
|
return md5_cache_path;
|
|
}
|
|
|
|
public string GetMd5CacheFilePath()
|
|
{
|
|
return md5_cache_file_path;
|
|
}
|
|
|
|
public void ReadMd5CacheFile()
|
|
{
|
|
string dir = GetMd5CachePath();
|
|
if (! Directory.Exists(dir))
|
|
{
|
|
Directory.CreateDirectory(dir);
|
|
TraceLog.Trace("VersionSvc.ReadMd5CacheFile create dir {0}", dir);
|
|
return;
|
|
}
|
|
|
|
string filePath = GetMd5CacheFilePath();
|
|
if (! File.Exists(filePath))
|
|
{
|
|
TraceLog.Trace("VersionSvc.ReadMd5CacheFile {0} no cache file", filePath);
|
|
return;
|
|
}
|
|
|
|
|
|
try
|
|
{
|
|
string[] lines = File.ReadAllLines(filePath);
|
|
if (lines == null || lines.Length == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_allPatchFileMd5.Clear();
|
|
|
|
// filename md5 size createtime modifytime
|
|
foreach (string str in lines)
|
|
{
|
|
var split = str.Split(" ");
|
|
if (split.Length >= 5)
|
|
{
|
|
var fileInfo = new PatchFileMd5Info
|
|
{
|
|
FileFullPath = split[0],
|
|
Md5 = split[1],
|
|
FileSize = int.Parse(split[2]),
|
|
CreateTimeMs = long.Parse(split[3]),
|
|
ModifyTimeMs = long.Parse(split[4]),
|
|
};
|
|
|
|
m_allPatchFileMd5.Add(split[0], fileInfo);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Exception(ex);
|
|
}
|
|
}
|
|
|
|
|
|
public void WriteMd5CacheFile()
|
|
{
|
|
string dir = GetMd5CachePath();
|
|
if (! Directory.Exists(dir))
|
|
{
|
|
Directory.CreateDirectory(dir);
|
|
TraceLog.Trace("VersionSvc.WriteMd5CacheFile create dir {0}", dir);
|
|
return;
|
|
}
|
|
|
|
string file = GetMd5CacheFilePath();
|
|
|
|
try
|
|
{
|
|
using (StreamWriter ws = File.CreateText(file))
|
|
{
|
|
foreach (PatchFileMd5Info info in m_allPatchFileMd5.Values)
|
|
{
|
|
string line = $"{info.FileFullPath} {info.Md5} {info.FileSize} {info.CreateTimeMs} {info.ModifyTimeMs}";
|
|
ws.WriteLine(line);
|
|
}
|
|
|
|
TraceLog.Trace("VersionSvc.WriteMd5CacheFile write file {0} succ", file);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Exception(ex);
|
|
}
|
|
}
|
|
|
|
// 不管是否配置了系统维护时间, 该函数一定会返回一个非负数的剩余时间, 非系统维护状态时不要调用此函数
|
|
public int GetMaintenanceRemainTime()
|
|
{
|
|
int nowSec = (int)VersionServerUtils.GetTimeSecond();
|
|
|
|
if (! string.IsNullOrEmpty(m_serverConfig.maintenanceStartTime) && m_serverConfig.maintenanceStartTimeInt == 0)
|
|
{
|
|
m_serverConfig.maintenanceStartTimeInt = (int) ConfigStringTimeParse.ParseConfigTime(m_serverConfig.maintenanceStartTime);
|
|
}
|
|
|
|
if (! string.IsNullOrEmpty(m_serverConfig.maintenanceEndTime) && m_serverConfig.maintenanceEndTimeInt == 0)
|
|
{
|
|
m_serverConfig.maintenanceEndTimeInt = (int)ConfigStringTimeParse.ParseConfigTime(m_serverConfig.maintenanceEndTime);
|
|
}
|
|
|
|
if (m_serverConfig.maintenanceStartTimeInt > 0 && m_serverConfig.maintenanceEndTimeInt > 0
|
|
&& m_serverConfig.maintenanceStartTimeInt <= nowSec && nowSec <= m_serverConfig.maintenanceEndTimeInt)
|
|
{
|
|
return m_serverConfig.maintenanceEndTimeInt - nowSec;
|
|
}
|
|
|
|
return 3600;
|
|
}
|
|
|
|
public bool CheckValidVersionInfo(ref VersionInfo versionInfo)
|
|
{
|
|
//os 非法
|
|
if (! IsValidOs(ref versionInfo))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string ver = versionInfo.AppVersion.GetString();
|
|
if (string.IsNullOrEmpty(ver))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (AppVersion.ToIntVersion(ver) == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
string[] validChannels = m_serverConfig.validChannel.Split('|');
|
|
|
|
for(int i=0; i < validChannels.Length; i++)
|
|
{
|
|
if(versionInfo.ApkChannel.Equals(validChannels[i]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
*/
|
|
return true;
|
|
}
|
|
|
|
public string GetVersionFileFullPath(string Os, string channel, bool bDebug)
|
|
{
|
|
string strFullPath = string.Format("{0}/{1}/{2}/{3}"
|
|
,m_serverConfig.versionFileBasePath
|
|
,Os
|
|
, channel //渠道包标识
|
|
, bDebug ? VERSION_FILE_NAME_DEBUG : VERSION_FILE_NAME
|
|
);
|
|
|
|
TraceLog.Trace("VersionSvc.GetVersionFileFullPath {0}", strFullPath);
|
|
return strFullPath;
|
|
}
|
|
|
|
//是否需要更新整包
|
|
public bool NeedUpdateFullApk(long curVersion, long reqVersion)
|
|
{
|
|
ushort vMax = AppVersion.GetMaxFromVersion(curVersion);
|
|
ushort vMid = AppVersion.GetMidFromVersion(curVersion);
|
|
//ushort vMin = AppVersion.GetMinFromVersion(curVersion);
|
|
|
|
ushort vMaxReq = AppVersion.GetMaxFromVersion(reqVersion);
|
|
ushort vMidReq = AppVersion.GetMidFromVersion(reqVersion);
|
|
//ushort vMinReq = AppVersion.GetMinFromVersion(reqVersion);
|
|
|
|
//大版本号不同需要升级整包
|
|
if (vMax > vMaxReq)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (vMid > vMidReq)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//只升级小版本号不需要更新整包
|
|
return false;
|
|
}
|
|
|
|
private string GetApkRelativePath(ref VersionInfo versionInfo, long curVersion)
|
|
{
|
|
string strVersion = AppVersion.ToStringVersion(curVersion);
|
|
|
|
// android/en/1.0.0.25/xgame_en_1.0.0.25.apk
|
|
string strFullPath = string.Format("{0}/{1}/{2}/{3}_{4}_{5}"
|
|
, versionInfo.Os
|
|
, versionInfo.ApkChannel
|
|
, strVersion
|
|
, m_serverConfig.packageName
|
|
, "en"
|
|
, strVersion);
|
|
|
|
if(versionInfo.Os.Equals("android"))
|
|
{
|
|
strFullPath += ".apk";
|
|
}
|
|
else
|
|
{
|
|
strFullPath += ".ipa";
|
|
}
|
|
|
|
return strFullPath;
|
|
}
|
|
|
|
private string GetAabRelativePath(ref VersionInfo versionInfo, long apkVersion)
|
|
{
|
|
string strVersion = AppVersion.ToStringVersion(apkVersion);
|
|
TraceLog.Trace("GetAabRelativePath apkVersion {0}", strVersion);
|
|
// android/en/1.0.0.25/xgame_en_1.0.0.25.aab
|
|
string strFullPath = string.Format("{0}/{1}/{2}/{3}_{4}_{5}"
|
|
, versionInfo.Os
|
|
, versionInfo.ApkChannel
|
|
, strVersion
|
|
, m_serverConfig.packageName
|
|
, "en"
|
|
, strVersion);
|
|
|
|
if (versionInfo.Os.Equals("android"))
|
|
{
|
|
strFullPath += ".aab";
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return strFullPath;
|
|
}
|
|
|
|
private string GetPatchFileRelativePath(ref VersionInfo versionInfo, long curVersion, long reqVersion)
|
|
{
|
|
//直接完整包
|
|
if (NeedUpdateFullApk(curVersion, reqVersion))
|
|
{
|
|
return GetApkRelativePath(ref versionInfo, curVersion);
|
|
}
|
|
|
|
//补丁
|
|
string strFromVersion = AppVersion.ToStringVersion(reqVersion);
|
|
string strToVersion = AppVersion.ToStringVersion(curVersion);
|
|
|
|
// android/en/1.0.25/xgame_en_1.0.25_1.0.26.upk
|
|
string strPatch = string.Format("{0}/{1}/{2}/{3}_{4}_{5}_{6}.upk"
|
|
, versionInfo.Os
|
|
, versionInfo.ApkChannel
|
|
, strToVersion
|
|
, m_serverConfig.packageName
|
|
, "en"
|
|
, strFromVersion
|
|
, strToVersion);
|
|
|
|
return strPatch;
|
|
}
|
|
|
|
public HotPatchCfg GetHotPatchCfg(string version)
|
|
{
|
|
if (m_serverConfig.hotPatches != null)
|
|
{
|
|
return m_serverConfig.hotPatches.FirstOrDefault(v => v.version == version);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public HotPatchCfg GetDebugHotPatchCfg(string version)
|
|
{
|
|
if (m_serverConfig.debugHotPatches != null)
|
|
{
|
|
return m_serverConfig.debugHotPatches.FirstOrDefault(v => v.version == version);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
// 热更patch
|
|
private string GetHotPatchFileRelativePath(ref VersionInfo versionInfo, string hotPatchVer, bool bDebug)
|
|
{
|
|
// android/en/hotfix/1.0.12_hotpatch.zip
|
|
return string.Format("{0}/{1}/{2}/{3}_hotpatch.zip"
|
|
, versionInfo.Os
|
|
, versionInfo.ApkChannel
|
|
, bDebug ? "hotfix_debug" : "hotfix"
|
|
, hotPatchVer);
|
|
}
|
|
|
|
|
|
//获取补丁文件下载地址(有可能是完整的apk文件,ios需要去应用商店下载)
|
|
public string GetPatchFileUrl(ref VersionInfo versionInfo, long curVersion, long reqVersion, bool bDebugUrl)
|
|
{
|
|
string strPatchFileRelativePath = GetPatchFileRelativePath(ref versionInfo, curVersion, reqVersion);
|
|
|
|
string strPatchUrl = null;
|
|
if (bDebugUrl && m_serverConfig.useDebugDownloadUrl > 0)
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.debugDownloadUrl, strPatchFileRelativePath);
|
|
}
|
|
else
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.downloadUrl, strPatchFileRelativePath);
|
|
}
|
|
string strPatchLocalFullPatch = GetPatchFileLocalFullPath(ref versionInfo, curVersion, reqVersion);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
return strPatchUrl;
|
|
}
|
|
|
|
//获取补丁文件下载地址(有可能是完整的apk文件,ios需要去应用商店下载)
|
|
public string GetAabPatchFileUrl(ref VersionInfo versionInfo, long apkVersion,bool bDebugUrl)
|
|
{
|
|
TraceLog.Trace("VersionSvc.GetAabPatchFileUrl apkVersion {0}", apkVersion);
|
|
string strPatchFileRelativePath = GetAabRelativePath(ref versionInfo, apkVersion);
|
|
if (strPatchFileRelativePath == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string strPatchUrl = null;
|
|
if (bDebugUrl && m_serverConfig.useDebugDownloadUrl > 0)
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.debugDownloadUrl, strPatchFileRelativePath);
|
|
}
|
|
else
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.downloadUrl, strPatchFileRelativePath);
|
|
}
|
|
|
|
string strPatchLocalFullPatch = GetAabFileLocalFullPath(ref versionInfo, apkVersion);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
|
|
TraceLog.Trace("VersionSvc.GetAabPatchFileUrl url {0}", strPatchUrl);
|
|
return strPatchUrl;
|
|
}
|
|
|
|
public string GetHotPatchFileUrl(ref VersionInfo versionInfo, string hotPatchVer, bool bDebug,bool bDebugUrl)
|
|
{
|
|
string hotPath = GetHotPatchFileRelativePath(ref versionInfo, hotPatchVer, bDebug);
|
|
if (string.IsNullOrEmpty(hotPath))
|
|
{
|
|
return null;
|
|
}
|
|
string strPatchUrl = null;
|
|
if (bDebugUrl && m_serverConfig.useDebugDownloadUrl > 0)
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.debugDownloadUrl, hotPath);
|
|
}
|
|
else
|
|
{
|
|
strPatchUrl = string.Format("{0}/{1}", m_serverConfig.downloadUrl, hotPath);
|
|
}
|
|
|
|
|
|
string strPatchLocalFullPatch = GetHotPatchFileLocalFullPath(ref versionInfo, hotPatchVer, bDebug);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
return strPatchUrl;
|
|
}
|
|
|
|
//获取补丁(upk或者apk的本地完整路径)
|
|
public string GetPatchFileLocalFullPath(ref VersionInfo versionInfo, long curVersion, long reqVersion)
|
|
{
|
|
string strPatchFileRelativePath = GetPatchFileRelativePath(ref versionInfo, curVersion, reqVersion);
|
|
|
|
string strPatchLocalFullPatch = string.Format("{0}/{1}", m_serverConfig.versionFileBasePath, strPatchFileRelativePath);
|
|
|
|
return strPatchLocalFullPatch;
|
|
}
|
|
|
|
|
|
public string GetAabFileLocalFullPath(ref VersionInfo versionInfo, long apkVersion)
|
|
{
|
|
string strPatchFileRelativePath = GetAabRelativePath(ref versionInfo, apkVersion);
|
|
|
|
string strPatchLocalFullPatch = string.Format("{0}/{1}", m_serverConfig.versionFileBasePath, strPatchFileRelativePath);
|
|
|
|
return strPatchLocalFullPatch;
|
|
}
|
|
|
|
// 获取热更包本地完整路径
|
|
public string GetHotPatchFileLocalFullPath(ref VersionInfo versionInfo, string hotPatchVer, bool bDebug)
|
|
{
|
|
string relPath = GetHotPatchFileRelativePath(ref versionInfo, hotPatchVer, bDebug);
|
|
if (string.IsNullOrEmpty(relPath))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return string.Format("{0}/{1}", m_serverConfig.versionFileBasePath, relPath);
|
|
}
|
|
|
|
private PatchFileMd5Info CalcPatchFileMd5(string fullPath)
|
|
{
|
|
try
|
|
{
|
|
//这里有io,后面请求量大了有可能有点性能问题,可以优化成每几秒只读一次
|
|
if (! File.Exists(fullPath))
|
|
{
|
|
TraceLog.Debug("VersionSvc.CalcPatchFileMd5 file {0} not exist", fullPath);
|
|
return null;
|
|
}
|
|
|
|
byte[] filecontent = File.ReadAllBytes(fullPath);
|
|
|
|
MD5 md5 = MD5.Create();
|
|
byte[] result = md5.ComputeHash(filecontent);
|
|
md5.Dispose();
|
|
|
|
string strmd5 = string.Empty;
|
|
for (int i = 0; i < result.Length; i++)
|
|
{
|
|
strmd5 += result[i].ToString("x2");
|
|
}
|
|
|
|
PatchFileMd5Info info = new PatchFileMd5Info();
|
|
info.FileFullPath = fullPath;
|
|
info.FileSize = filecontent.Length;
|
|
info.Md5 = strmd5;
|
|
|
|
FileInfo fileinfo = new FileInfo(fullPath);
|
|
info.CreateTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.CreationTime);
|
|
info.ModifyTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.LastWriteTime);
|
|
|
|
TraceLog.Debug("VersionSvc.CalcPatchFileMd5 fullPath {0} size {1} md5 {2}"
|
|
, info.FileFullPath, info.FileSize, info.Md5);
|
|
|
|
return info;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Exception(ex);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
public PatchFileMd5Info GetPatchFileMd5Info(string fileFullPath)
|
|
{
|
|
if (! File.Exists(fileFullPath))
|
|
{
|
|
TraceLog.Error("VersionSvc.GetPatchFileMd5Info {0} not exist", fileFullPath);
|
|
return null;
|
|
}
|
|
|
|
long fileSize = 0, createTimeMs = 0, modifyTimeMs = 0;
|
|
|
|
try
|
|
{
|
|
FileInfo fileinfo = new FileInfo(fileFullPath);
|
|
fileSize = fileinfo.Length;
|
|
createTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.CreationTime);
|
|
modifyTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.LastWriteTime);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Exception(ex);
|
|
return null;
|
|
}
|
|
|
|
if (m_allPatchFileMd5.TryGetValue(fileFullPath, out PatchFileMd5Info info))
|
|
{
|
|
// 文件大小, 创建时间, 修改时间完全一致
|
|
if (info.FileSize == fileSize
|
|
&& info.CreateTimeMs == createTimeMs
|
|
&& info.ModifyTimeMs == modifyTimeMs)
|
|
{
|
|
return info;
|
|
}
|
|
|
|
TraceLog.Trace("VersionSvc.GetPatchFileMd5Info file {0} size {1} {2} create time {3} {4} modify time {5} {6} not same"
|
|
, fileFullPath, info.FileSize, fileSize, info.CreateTimeMs, createTimeMs
|
|
, info.ModifyTimeMs, modifyTimeMs);
|
|
|
|
// 任何一项不同时删除缓存重新计算
|
|
m_allPatchFileMd5.Remove(fileFullPath);
|
|
}
|
|
|
|
info = CalcPatchFileMd5(fileFullPath);
|
|
if (info != null)
|
|
{
|
|
m_allPatchFileMd5.Add(fileFullPath, info);
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
public int GetPatchFileSizeAndMd5(ref VersionInfo versionInfo, long curVersion, long reqVersion, out string strmd5)
|
|
{
|
|
strmd5 = string.Empty;
|
|
string strPatchLocalFullPatch = GetPatchFileLocalFullPath(ref versionInfo, curVersion, reqVersion);
|
|
|
|
if (! File.Exists(strPatchLocalFullPatch))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
long fileSize = 0, createTimeMs = 0, modifyTimeMs = 0;
|
|
|
|
try
|
|
{
|
|
FileInfo fileinfo = new FileInfo(strPatchLocalFullPatch);
|
|
fileSize = fileinfo.Length;
|
|
createTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.CreationTime);
|
|
modifyTimeMs = AppTime.ConvertDateTimeToUnixTime(fileinfo.LastWriteTime);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TraceLog.Exception(ex);
|
|
return -1;
|
|
}
|
|
|
|
if (m_patchFileMd5Table.TryGetValue(strPatchLocalFullPatch, out PatchFileMd5Info info))
|
|
{
|
|
// 文件大小, 创建时间, 修改时间完全一致
|
|
if (info.FileSize == fileSize && info.CreateTimeMs == createTimeMs
|
|
&& info.ModifyTimeMs == modifyTimeMs)
|
|
{
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
// 任何一项不同时, 直接删除缓存, 重新计算
|
|
m_patchFileMd5Table.Remove(strPatchLocalFullPatch);
|
|
}
|
|
|
|
info = CalcPatchFileMd5(strPatchLocalFullPatch);
|
|
if (info == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
m_patchFileMd5Table.Add(strPatchLocalFullPatch, info);
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
|
|
|
|
public int GetAabFileSizeAndMd5(ref VersionInfo versionInfo, long reqVersion, out string strmd5)
|
|
{
|
|
TraceLog.Trace("GetAabFileSizeAndMd5 reqVersion {0}", reqVersion);
|
|
strmd5 = string.Empty;
|
|
string strPatchLocalFullPatch = GetAabFileLocalFullPath(ref versionInfo, reqVersion);
|
|
TraceLog.Trace("GetAabFileSizeAndMd5 strPatchLocalFullPatch {0}", strPatchLocalFullPatch);
|
|
PatchFileMd5Info info;
|
|
if (m_aabPatchFileMd5.ContainsKey(strPatchLocalFullPatch))
|
|
{
|
|
info = m_aabPatchFileMd5[strPatchLocalFullPatch];
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
info = CalcPatchFileMd5(strPatchLocalFullPatch);
|
|
if (info == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
m_aabPatchFileMd5.Add(strPatchLocalFullPatch, info);
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
public int GetHotPatchFileSizeAndMd5(ref VersionInfo versionInfo, string curVersion, out string strmd5, bool bDebug)
|
|
{
|
|
strmd5 = string.Empty;
|
|
|
|
string hotPath = GetHotPatchFileLocalFullPath(ref versionInfo, curVersion, bDebug);
|
|
if (string.IsNullOrEmpty(hotPath))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
PatchFileMd5Info info;
|
|
if (m_hotPatchFileMd5.ContainsKey(hotPath))
|
|
{
|
|
info = m_hotPatchFileMd5[hotPath];
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
info = CalcPatchFileMd5(hotPath);
|
|
if (info == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
m_hotPatchFileMd5.Add(hotPath, info);
|
|
strmd5 = info.Md5;
|
|
return info.FileSize;
|
|
}
|
|
|
|
//获得对应 os_channel 的最新版本, ios其实只会有一个缺省channel,Android有可能有多个
|
|
public long GetCurrentVersion(ref VersionInfo versionInfo, bool bDebug)
|
|
{
|
|
if (! IsValidOs(ref versionInfo))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
string os = versionInfo.Os.GetString();
|
|
string channel = versionInfo.ApkChannel.GetString();
|
|
// windows通知android的版本号, 方便在unity测试, 客户端会在unity下屏蔽掉自动更新功能
|
|
if (os.Equals("win"))
|
|
{
|
|
os = AndroidStr;
|
|
}
|
|
|
|
string strKey = os + "_" + channel;
|
|
if(bDebug)
|
|
{
|
|
strKey = strKey + "_debug";
|
|
}
|
|
bool bInTableAlready = m_curVersionTable.ContainsKey(strKey);
|
|
|
|
if (bInTableAlready == false)
|
|
{
|
|
try
|
|
{
|
|
string versionFile = GetVersionFileFullPath(os, channel, bDebug);
|
|
string strVersion = File.ReadAllText(versionFile);
|
|
|
|
if (strVersion == null)
|
|
{
|
|
TraceLog.Trace("VersionSvc.GetCurrentVersion file {0} no data", versionFile);
|
|
return 0;
|
|
}
|
|
|
|
long iVersion = AppVersion.ToIntVersion(strVersion);
|
|
if (bInTableAlready == false)
|
|
{
|
|
m_curVersionTable.Add(strKey, iVersion);
|
|
}
|
|
else
|
|
{
|
|
m_curVersionTable[strKey] = iVersion;
|
|
}
|
|
}
|
|
catch(Exception)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return m_curVersionTable[strKey];
|
|
|
|
}
|
|
|
|
public bool SelectGateUrlCheckIpMatch(string ip, SpecialVersionCfg cfg)
|
|
{
|
|
//没配置全通过
|
|
if(string.IsNullOrEmpty(cfg.ipRule))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//客户端的ip非法,则算不同过
|
|
if(string.IsNullOrEmpty(ip))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string[] ipsub = ip.Split('.');
|
|
if (ipsub.Length != 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string[] ipInListSub = cfg.ipRule.Split('.');
|
|
if (ipInListSub.Length != 4)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool isSame = true;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (ipInListSub[i] == ipsub[i] || ipInListSub[i] == "*")
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
isSame = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return isSame;
|
|
}
|
|
|
|
|
|
//选择一个lobbyGate的Url
|
|
public string SelectGateUrl(string ip, ref CSVersionCheckReq req, out int forReview)
|
|
{
|
|
//缺省是正式版本,审核字段设置成0
|
|
forReview = 0;
|
|
|
|
foreach (var specialversion in m_serverConfig.specialVersions)
|
|
{
|
|
if (specialversion.disable !=0)
|
|
continue;
|
|
//特殊版本返回特殊配置,不配置specialOs表示所有specialVersion
|
|
//注意只有app版本和apk版本一致才行(新包),不一致的表示是审核期间通过自动更新上来的玩家
|
|
if (req.Version.AppVersion.Equals(specialversion.version)
|
|
&& (string.IsNullOrEmpty(specialversion.apkversion) || req.ApkVersion.Equals(specialversion.apkversion))
|
|
&& (string.IsNullOrEmpty(specialversion.os) || req.Version.Os.Equals(specialversion.os))
|
|
&& (string.IsNullOrEmpty(specialversion.channel) || req.Version.ApkChannel.Equals(specialversion.channel))
|
|
&& (specialversion.packageType == 0 || req.Version.PackageType == specialversion.packageType)
|
|
&& SelectGateUrlCheckIpMatch(ip, specialversion)
|
|
)
|
|
{
|
|
forReview = specialversion.review;
|
|
return specialversion.gateUrl;
|
|
}
|
|
}
|
|
|
|
|
|
//正常版本,轮着来,选择下一个就可以了,客户端和accountgate的连接是短连接,无需复杂的负载均衡
|
|
//注意:如果物理机器的网关支持一个对外端口负载均衡到内网多个端口的话(TGW似乎就支持),这个功能尽量不用,
|
|
//因为对外端口越多,客户端连不上概率越大
|
|
|
|
//int selectIndex = (int)(m_accountGateSelectSeq % m_serverConfig.accountGateUrl.Length);
|
|
//m_accountGateSelectSeq++;
|
|
//return m_serverConfig.accountGateUrl[selectIndex];
|
|
|
|
// account改成了主备两套, 不再支持一套内部署多个account进程
|
|
return m_serverConfig.accountGateUrl[0];
|
|
}
|
|
|
|
|
|
public void AddClientConnectData(uint gateServerID, long sessionID, string ip)
|
|
{
|
|
var clientConnData = VersionServerUtils.GetVersionServerData().m_ClientConnectDict;
|
|
if (clientConnData.ContainsKey(sessionID) == false)
|
|
{
|
|
ClientConnectInfo data = new ClientConnectInfo();
|
|
data.iConnectSessionID = sessionID;
|
|
data.uGateServerID = gateServerID;
|
|
data.iConnectTime = AppTime.GetNowSysMs();
|
|
data.ip = ip;
|
|
clientConnData.Add(sessionID, data);
|
|
}
|
|
}
|
|
|
|
public void RemoveClientConnectData(uint gateServerID, long sessionID)
|
|
{
|
|
var clientConnData = VersionServerUtils.GetVersionServerData().m_ClientConnectDict;
|
|
clientConnData.Remove(sessionID);
|
|
}
|
|
|
|
public ClientConnectInfo GetClientConnData(long sessionID)
|
|
{
|
|
var clientConnData = VersionServerUtils.GetVersionServerData().m_ClientConnectDict;
|
|
|
|
ClientConnectInfo info = null;
|
|
clientConnData.TryGetValue(sessionID, out info);
|
|
|
|
return info;
|
|
}
|
|
|
|
public bool IsMaintenanceMode()
|
|
{
|
|
return m_serverConfig.maintenanceMode == 1;
|
|
}
|
|
|
|
public string GetMaintenanceNotice()
|
|
{
|
|
return m_serverConfig.maintenanceNotice;
|
|
}
|
|
|
|
public string GetLatestVersion(ref VersionInfo versionInfo)
|
|
{
|
|
foreach(var c in m_serverConfig.versions)
|
|
{
|
|
//if(c.os == versionInfo.Os.GetString() && c.productId == versionInfo.ProductId.GetString())
|
|
//{
|
|
// return c.version;
|
|
//}
|
|
if (c.productId == versionInfo.ProductId.GetString())
|
|
{
|
|
return c.version;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
public string GetApkMarketUrl(ref VersionInfo versionInfo, string packageName)
|
|
{
|
|
|
|
if(0 <= versionInfo.PackageType - 1 && versionInfo.PackageType - 1 < m_serverConfig.versions.Length)
|
|
{
|
|
return m_serverConfig.versions[versionInfo.PackageType - 1].url;
|
|
}
|
|
return "";
|
|
|
|
//if(versionInfo.ApkChannel.Equals("huawei"))
|
|
//{
|
|
// return m_serverConfig.apkMarketUrlHuawei;
|
|
//}
|
|
//else if(versionInfo.Os.Equals("ios"))
|
|
//{
|
|
// //1表示美国ios包
|
|
// if (packageName == "com.herogame.ios.eternalevolution" || versionInfo.PackageType == 1)
|
|
// {
|
|
// return m_serverConfig.apkMarketUrlIosUS;
|
|
// }
|
|
// else
|
|
// {
|
|
// return m_serverConfig.apkMarketUrlIos;
|
|
// }
|
|
//}
|
|
//return m_serverConfig.apkMarketUrl;
|
|
}
|
|
|
|
//获取补丁文件下载地址(有可能是完整的apk文件,ios需要去应用商店下载)
|
|
public string GetPatchFileBackupUrl(ref VersionInfo versionInfo, long curVersion, long reqVersion, bool bDebugUrl)
|
|
{
|
|
if (bDebugUrl || string.IsNullOrEmpty(m_serverConfig.backupDownloadUrl))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
string strPatchFileRelativePath = GetPatchFileRelativePath(ref versionInfo, curVersion, reqVersion);
|
|
string strPatchUrl = string.Format("{0}/{1}", m_serverConfig.backupDownloadUrl, strPatchFileRelativePath);
|
|
string strPatchLocalFullPatch = GetPatchFileLocalFullPath(ref versionInfo, curVersion, reqVersion);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
return strPatchUrl;
|
|
}
|
|
|
|
//获取补丁文件下载地址(有可能是完整的apk文件,ios需要去应用商店下载)
|
|
public string GetAabPatchFileBackupUrl(ref VersionInfo versionInfo, long apkVersion, bool bDebugUrl)
|
|
{
|
|
TraceLog.Trace("GetAabPatchFileUrl apkVersion {0}", apkVersion);
|
|
if (bDebugUrl || string.IsNullOrEmpty(m_serverConfig.backupDownloadUrl))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
string strPatchFileRelativePath = GetAabRelativePath(ref versionInfo, apkVersion);
|
|
if (strPatchFileRelativePath == null)
|
|
{
|
|
return null;
|
|
}
|
|
string strPatchUrl = string.Format("{0}/{1}", m_serverConfig.backupDownloadUrl, strPatchFileRelativePath);
|
|
string strPatchLocalFullPatch = GetAabFileLocalFullPath(ref versionInfo, apkVersion);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
return strPatchUrl;
|
|
}
|
|
public string GetHotPatchFileBackupUrl(ref VersionInfo versionInfo, string hotPatchVer, bool bDebug, bool bDebugUrl)
|
|
{
|
|
if (bDebugUrl || string.IsNullOrEmpty(m_serverConfig.backupDownloadUrl))
|
|
{
|
|
return string.Empty;
|
|
}
|
|
string hotPath = GetHotPatchFileRelativePath(ref versionInfo, hotPatchVer, bDebug);
|
|
if (string.IsNullOrEmpty(hotPath))
|
|
{
|
|
return null;
|
|
}
|
|
string strPatchUrl = string.Format("{0}/{1}", m_serverConfig.backupDownloadUrl, hotPath);
|
|
|
|
string strPatchLocalFullPatch = GetHotPatchFileLocalFullPath(ref versionInfo, hotPatchVer, bDebug);
|
|
string version = m_fileVersionList.GetFileVersion(strPatchLocalFullPatch);
|
|
if (string.IsNullOrEmpty(version) == false)
|
|
{
|
|
strPatchUrl = string.Format("{0}?v={1}", strPatchUrl, version);
|
|
}
|
|
return strPatchUrl;
|
|
}
|
|
}
|
|
}
|
|
|