using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEditor;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
using Beebyte.Obfuscator;
public class ClientPackage
{
public static readonly string versionsFilePath = Application.dataPath + Path.DirectorySeparatorChar + "Editor/VersionConfigs/Versions.txt";
public static readonly string[] baseLevels = new string[] { "Assets/Resources/Scenes/Launch.unity", "Assets/Resources/Scenes/Empty.unity" };
public static string auditOutTime = string.Empty;
public static string SDK_PLUGIN_PROJECT {
get { return LocalSave.GetString("SDK_PROJECT_PATH"); }
set { LocalSave.SetString("SDK_PROJECT_PATH", value); }
}
public static bool obfuscatorEnabled {
get { return LocalSave.GetBool("obfuscatorEnabled", false); }
set { LocalSave.SetBool("obfuscatorEnabled", value); }
}
public static bool includeConfig {
get { return LocalSave.GetBool("client_pg_includeConfig", false); }
set { LocalSave.SetBool("client_pg_includeConfig", value); }
}
public static bool includeUI {
get { return LocalSave.GetBool("client_pg_includeUI", false); }
set { LocalSave.SetBool("client_pg_includeUI", value); }
}
public static int AssetPrior {
get { return LocalSave.GetInt("HalfAssetPrior", 1); }
set { LocalSave.SetInt("HalfAssetPrior", value); }
}
static string THE_2TH_WORLD_SDK_PATH = Application.dataPath + "/Plugins/Android/bin";
static string MANIFEST_PATH = Application.dataPath + "/Plugins/Android/AndroidManifest.xml";
static string iOS_PLUGIN_PATH = Application.dataPath + "/Plugins/iOS/Channel";
///
/// 打包
///
/// 整合sdk路径
/// _assetBundle的资源路径,默认在项目目录下的 AssetBundles/android
/// 打包完成后的输出路径
/// 渠道id,可打多个包,用|分割
/// 打包序序号,用于标识名称
/// 是否为开发版
/// ios打包导出xcode工程模式,Append和Replace
public static void BuildPublishers(string _sdkPath, string _assetBundlePath, string _output, string _publisherString, int _buildIndex, bool _development, bool _replace)
{
var publisherIds = new List();
var tempStrings = _publisherString.Split(StringUtility.splitSeparator, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < tempStrings.Length; i++)
{
var temp = tempStrings[i];
var matches = Regex.Matches(temp, "\\d+");
switch (matches.Count)
{
case 1:
var id = int.Parse(matches[0].Value);
publisherIds.Add(id.ToString());
break;
case 2:
var min = int.Parse(matches[0].Value);
var max = int.Parse(matches[1].Value);
for (int j = min; j <= max; j++)
{
publisherIds.Add(j.ToString());
}
break;
}
}
var smallPackages = new List();
var halfPackages = new List();
var bigPackages = new List();
for (int i = 0; i < publisherIds.Count; i++)
{
try
{
var publisher = publisherIds[i];
var versionName = string.Empty;
var versionConfig = GetVersionConfig(publisher, out versionName);
switch (versionConfig.assetAccess)
{
case InstalledAsset.NullAsset:
smallPackages.Add(publisher);
break;
case InstalledAsset.HalfAsset:
halfPackages.Add(publisher);
break;
case InstalledAsset.FullAsset:
case InstalledAsset.IngoreDownLoad:
bigPackages.Add(publisher);
break;
}
}
catch (Exception ex)
{
Debug.Log(ex);
}
}
if (Directory.Exists(ResourcesPath.Instance.StreamingAssetPath))
{
Directory.Delete(ResourcesPath.Instance.StreamingAssetPath, true);
}
if (smallPackages.Count > 0)
{
var files = new List();
var builtInFiles = new List();
FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, files);
foreach (var file in files)
{
var fileName = Path.GetFileName(file.FullName);
if (file.FullName.Contains("builtin"))
{
builtInFiles.Add(file);
continue;
}
if (ConfigInitiator.builtinConfig.Contains(fileName))
{
builtInFiles.Add(file);
continue;
}
// File.Delete(file.FullName);
}
var totalCount = builtInFiles.Count;
var completeCount = 0;
foreach (var item in builtInFiles)
{
var extension = Path.GetExtension(item.FullName);
if (extension == ".meta")
{
continue;
}
var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, item.FullName);
var to = StringUtility.Contact(ResourcesPath.Instance.StreamingAssetPath, relativePath);
var directory = Path.GetDirectoryName(to);
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.Copy(item.FullName, to, true);
completeCount++;
}
EditorUtility.ClearProgressBar();
for (int i = 0; i < smallPackages.Count; i++)
{
#if UNITY_ANDROID
BuildApk(_sdkPath, _output, smallPackages[i], _buildIndex, _development);
#elif UNITY_IOS
BuildIpa(_sdkPath, smallPackages[i], _buildIndex, _replace);
#endif
}
}
if (halfPackages.Count > 0)
{
PriorBundleConfig.Init(true);
var fromFiles = new List();
FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, fromFiles);
var excludeFileFullNames = new List();
var tempFiles = new List();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/gmodels"), tempFiles);
foreach (var file in tempFiles)
{
var extersion = Path.GetExtension(file.FullName);
var fileName = Path.GetFileNameWithoutExtension(file.FullName);
var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Mob, fileName);
if (prior > AssetPrior)
{
excludeFileFullNames.Add(file.FullName);
}
}
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/maps"), tempFiles);
foreach (var file in tempFiles)
{
var extersion = Path.GetExtension(file.FullName);
var fileName = Path.GetFileNameWithoutExtension(file.FullName);
var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Scene, fileName);
if (prior > AssetPrior)
{
excludeFileFullNames.Add(file.FullName);
}
}
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/audio"), tempFiles);
foreach (var file in tempFiles)
{
var extersion = Path.GetExtension(file.FullName);
var fileName = Path.GetFileNameWithoutExtension(file.FullName);
var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Audio, fileName);
if (prior > AssetPrior)
{
excludeFileFullNames.Add(file.FullName);
}
}
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/effect"), tempFiles);
foreach (var file in tempFiles)
{
var extersion = Path.GetExtension(file.FullName);
var fileName = Path.GetFileNameWithoutExtension(file.FullName);
var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Effect, fileName);
if (prior > AssetPrior)
{
excludeFileFullNames.Add(file.FullName);
}
}
//剔除视频资源
tempFiles.Clear();
var videoPath = StringUtility.Contact(_assetBundlePath, "/video");
if (File.Exists(videoPath))
{
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/video"), tempFiles);
foreach (var file in tempFiles)
{
var extersion = Path.GetExtension(file.FullName);
var fileName = Path.GetFileNameWithoutExtension(file.FullName);
var prior = PriorBundleConfig.GetAssetPrior(AssetVersion.AssetCategory.Video, fileName);
if (prior > AssetPrior)
{
excludeFileFullNames.Add(file.FullName);
}
}
}
//剔除表资源
if (!includeConfig)
{
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/config"), tempFiles);
foreach (var file in tempFiles)
{
if (!ConfigInitiator.builtinConfig.Contains(file.Name))
excludeFileFullNames.Add(file.FullName);
}
}
//剔除UI资源
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/ui/sprite"), tempFiles);
foreach (var file in tempFiles)
{
var fileName = Path.GetFileName(file.FullName);
if (fileName == "loadingbg")
{
excludeFileFullNames.Add(file.FullName);
}
}
if (!includeUI)
{
tempFiles.Clear();
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(_assetBundlePath, "/ui"), tempFiles);
foreach (var file in tempFiles)
{
excludeFileFullNames.Add(file.FullName);
}
}
for (int i = fromFiles.Count - 1; i >= 0; i--)
{
var item = fromFiles[i];
if (excludeFileFullNames.Contains(item.FullName))
{
fromFiles.RemoveAt(i);
}
}
var totalCount = fromFiles.Count;
var completeCount = 0;
foreach (var item in fromFiles)
{
var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, item.FullName);
var to = StringUtility.Contact(ResourcesPath.Instance.StreamingAssetPath, relativePath);
if (relativePath.StartsWith("patch"))//不包含meta文件和补丁
continue;
var directory = Path.GetDirectoryName(to);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var extension = Path.GetExtension(item.FullName);
if (extension == ".meta")
continue;
File.Copy(item.FullName, to, true);
completeCount++;
}
EditorUtility.ClearProgressBar();
for (int i = 0; i < halfPackages.Count; i++)
{
#if UNITY_ANDROID
BuildApk(_sdkPath, _output, halfPackages[i], _buildIndex, _development);
#elif UNITY_IOS
BuildIpa(_sdkPath, halfPackages[i], _buildIndex, _replace);
#endif
}
}
if (bigPackages.Count > 0)
{
var fromFiles = new List();
FileExtersion.GetAllDirectoryFileInfos(_assetBundlePath, fromFiles);
var totalCount = fromFiles.Count;
var completeCount = 0;
foreach (var item in fromFiles)
{
var relativePath = FileExtersion.GetFileRelativePath(_assetBundlePath, item.FullName);
var to = StringUtility.Contact(ResourcesPath.Instance.StreamingAssetPath, relativePath);
if (relativePath.StartsWith("patch"))//不包含meta文件和补丁
continue;
var directory = Path.GetDirectoryName(to);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
var extension = Path.GetExtension(item.FullName);
if (extension == ".meta")
continue;
File.Copy(item.FullName, to, true);
completeCount++;
}
for (int i = 0; i < bigPackages.Count; i++)
{
#if UNITY_ANDROID
BuildApk(_sdkPath, _output, bigPackages[i], _buildIndex, _development);
#elif UNITY_IOS
BuildIpa(_sdkPath, bigPackages[i], _buildIndex, _replace);
#endif
}
}
}
public static void BuildApk(string _sdkPath, string _output, string _publisher, int _buildIndex, bool _development)
{
PreBuild(_publisher, _buildIndex);
var auditTimeFile = StringUtility.Contact(Application.dataPath, Path.DirectorySeparatorChar, "Resources/Config/AuditTime.txt");
if (File.Exists(auditTimeFile))
{
File.Delete(auditTimeFile);
}
var versionName = string.Empty;
var versionConfig = GetVersionConfig(_publisher, out versionName);
var versionConfigCSpath = Application.dataPath + "/Scripts/System/ClientVersion/VersionConfig.cs";
var text = File.ReadAllText(versionConfigCSpath);
if (text.Contains("VERSION_ALTERNATIVE"))
{
var pattern = "VERSION_ALTERNATIVE = \".*\"";
text = Regex.Replace(text, pattern, StringUtility.Contact("VERSION_ALTERNATIVE = ", "\"", versionConfig.m_Version, "\""));
bool encoderShouldEmitUTF8Identifier = true;
bool throwOnInvalidBytes = false;
UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier, throwOnInvalidBytes);
bool append = false;
StreamWriter streamWriter = new StreamWriter(versionConfigCSpath, append, encoding);
streamWriter.Write(text);
streamWriter.Close();
AssetDatabase.ImportAsset(versionConfigCSpath);
}
// 针对某一些打包的特殊性, 这里特指依赖的第三方库含有资源的问题
// 这里对有这种特型的渠道的sdk在打包的时候差异化gradle文件
// 更名原有文件
// string _gradleTemplateUrl = EditorApplication.applicationContentsPath + "/PlaybackEngines/AndroidPlayer/Tools/GradleTemplates/";
// string _gradleFileUrl = _gradleTemplateUrl + "mainTemplate.gradle";
// string _diffGradleFileUrl = StringUtility.Contact(_sdkPath, "/GradleDiff/", versionConfig.appId, "/mainTemplate.gradle");
// FileInfo _fileInfo = new FileInfo(_diffGradleFileUrl);
// if (!_fileInfo.Exists)
// {
// EditorUtility.DisplayDialog("警告", "Gradle模式需要配置差异文件,但是没有找到该文件: " + _diffGradleFileUrl, "@Hale");
// return;
// }
// _fileInfo.CopyTo(_gradleFileUrl, true);
// 移动配置文件至打包工作目录中
var keystorePath = Application.dataPath + "/Editor/Keystore/" + versionConfig.keystoreFileName + ".keystore";
if (!File.Exists(keystorePath))
{
Debug.LogError("签名文件不存在");
return;
}
PlayerSettings.Android.keystoreName = keystorePath;
PlayerSettings.Android.keystorePass = versionConfig.keystorePassword;
PlayerSettings.Android.keyaliasName = versionConfig.keystoreAlias;
PlayerSettings.Android.keyaliasPass = versionConfig.keystoreAliasPassword;
// PlayerSettings.Android.targetSdkVersion = AndroidSdkVersions.AndroidApiLevel22;
PlayerSettings.Android.bundleVersionCode = VersionConfig.GetVersionNumber(versionConfig.version);
PlayerSettings.enableInternalProfiler = _development;
//删除原sdk文件
if (Directory.Exists(THE_2TH_WORLD_SDK_PATH))
Directory.Delete(THE_2TH_WORLD_SDK_PATH, true);
//删除原清单文件
// if (File.Exists(MANIFEST_PATH))
// File.Delete(MANIFEST_PATH);
var variant = _development ? "debug" : "release";
// var manifestpath = StringUtility.Contact(_sdkPath, "/Channel/Android/", versionConfig.appId, "/AndroidManifest.xml");
var channelSdkPath = StringUtility.Contact(_sdkPath, "/Channel/Android/", versionConfig.appId, "/", variant);
var manifeastPath = StringUtility.Contact(_sdkPath, "/Channel/Android/", versionConfig.appId, "/AndroidManifest.xml");
// if (File.Exists(manifestpath))
// {
// File.Copy(manifestpath, MANIFEST_PATH);
// DebugEx.LogFormat("执行Android 清单文件拷贝,from:{0};to:{1}", manifestpath, MANIFEST_PATH);
// }
// else
// {
// DebugEx.LogFormat("Android 清单文件不存在,{0}", manifestpath);
// // return;
// }
if (Directory.Exists(channelSdkPath))
{
FileExtersion.DirectoryCopy(channelSdkPath, THE_2TH_WORLD_SDK_PATH);
DebugEx.LogFormat("执行Android 渠道SDK 文件拷贝,from:{0};to:{1}", channelSdkPath, THE_2TH_WORLD_SDK_PATH);
}
else
{
DebugEx.LogFormat("Android 渠道SDK文件不存在,{0}", channelSdkPath);
return;
}
if (File.Exists(manifeastPath))
{
if (File.Exists(MANIFEST_PATH))
File.Delete(MANIFEST_PATH);
File.Copy(manifeastPath, MANIFEST_PATH);
DebugEx.LogFormat("Android 清单文件拷贝成功,{0}", manifeastPath);
}
var option = AssetDatabase.LoadAssetAtPath("Assets/Editor/Beebyte/Obfuscator/ObfuscatorOptions.asset");
if (option != null)
{
option.enabled = obfuscatorEnabled;
EditorUtility.SetDirty(option);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
IFix.Editor.IFixEditor.InjectAllAssemblys();//对injectAssemblys里的程序集进行注入,然后备份
if (_development)
{
var apkName = StringUtility.Contact(_output, "/", versionName, "_", versionConfig.clientPackageFlag, "_v", versionConfig.m_Version, "_", _buildIndex, "_development.apk");
BuildPipeline.BuildPlayer(baseLevels, apkName, BuildTarget.Android, BuildOptions.Development | BuildOptions.ConnectWithProfiler | BuildOptions.AllowDebugging);
}
else
{
var apkName = StringUtility.Contact(_output, "/", versionName, "_", versionConfig.clientPackageFlag, "_v", versionConfig.m_Version, "_", _buildIndex, ".apk");
BuildPipeline.BuildPlayer(baseLevels, apkName, BuildTarget.Android, BuildOptions.None);
}
}
public static void BuildIpa(string _sdkPath, string _publisher, int _buildIndex, bool _replace)
{
PreBuild(_publisher, _buildIndex);
var versionName = string.Empty;
var versionConfig = GetVersionConfig(_publisher, out versionName);
if (Directory.Exists(iOS_PLUGIN_PATH))
{
Directory.Delete(iOS_PLUGIN_PATH, true);
}
// if (!versionConfig.appId.Equals("test"))
// {
FileExtersion.DirectoryCopy(StringUtility.Contact(_sdkPath, "/Channel/iOS/", versionConfig.appId, "/Plugins"), iOS_PLUGIN_PATH);
// }
DateTime outTime;
var correct = DateTime.TryParse(auditOutTime, out outTime);
var directory = Application.dataPath + Path.DirectorySeparatorChar + "Resources/Config";
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
File.WriteAllText(Application.dataPath + Path.DirectorySeparatorChar + "Resources/Config/AuditTime.txt", correct ? auditOutTime : "");
var option = AssetDatabase.LoadAssetAtPath("Assets/Editor/Beebyte/Obfuscator/ObfuscatorOptions.asset");
if (option != null)
{
option.enabled = obfuscatorEnabled;
EditorUtility.SetDirty(option);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
PlayerSettings.iOS.buildNumber = VersionConfig.Get().buildIndex.ToString();
PlayerSettings.iOS.appleDeveloperTeamID = VersionConfig.Get().appleDeveloperTeamID;
IFix.Editor.IFixEditor.InjectAllAssemblys();//对injectAssemblys里的程序集进行注入,然后备份
#if UNITY_IOS
if (_replace)
{
XCodeProjectMod.BuildIOSProject_Replace();
}
else
{
XCodeProjectMod.BuildIOSProject_Append();
}
#endif
}
private static void PreBuild(string _publisher, int _buildIndex)
{
var newVersionConfigPath = StringUtility.Contact("Assets/Resources/VersionConfig", ".asset");
var versionName = string.Empty;
var fromVersionConfig = GetVersionConfig(_publisher, out versionName);
var newVersionConfig = ScriptableObject.CreateInstance();
if (File.Exists(newVersionConfigPath))
{
AssetDatabase.DeleteAsset(newVersionConfigPath);
}
VersionConfig.Copy(fromVersionConfig, newVersionConfig);
newVersionConfig.buildTime = DateTime.Now.ToString("yy/MM/dd--HH:mm");
newVersionConfig.buildIndex = _buildIndex;
if (File.Exists(Application.dataPath + "/Resources/Game.txt"))
{
File.Delete(Application.dataPath + "/Resources/Game.txt");
}
File.WriteAllText(Application.dataPath + "/Resources/Game.txt", GetGameId(_publisher));
AssetDatabase.CreateAsset(newVersionConfig, newVersionConfigPath);
EditorUtility.SetDirty(newVersionConfig);
SetIconAndSplashImage(versionName);
//SetCreateRoleAnimation();
PlayerSettings.companyName = "TheSecondWorld";
PlayerSettings.productName = newVersionConfig.productName;
PlayerSettings.applicationIdentifier = newVersionConfig.bundleIdentifier;
PlayerSettings.defaultInterfaceOrientation = UIOrientation.AutoRotation;
PlayerSettings.allowedAutorotateToLandscapeLeft = true;
PlayerSettings.allowedAutorotateToLandscapeRight = true;
PlayerSettings.allowedAutorotateToPortrait = false;
PlayerSettings.allowedAutorotateToPortraitUpsideDown = false;
PlayerSettings.bundleVersion = VersionConfig.GetVersionNumber(newVersionConfig.version).ToString();
//生成ILruntiemd CLR 绑定代码
ILRuntimeCLRBinding.GenerateCLRBinding();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
static VersionConfig GetVersionConfig(string _publisher, out string _versionName)
{
var lines = File.ReadAllLines(versionsFilePath);
for (int i = 2; i < lines.Length; i++)
{
var line = lines[i];
var lineStrings = line.Split('\t');
if (lineStrings[0] == _publisher)
{
var config = new VersionConfig();
config.Read(line);
_versionName = lineStrings[1];
return config;
}
}
_versionName = string.Empty;
return null;
}
static string GetGameId(string _publisher)
{
var lines = File.ReadAllLines(versionsFilePath);
for (int i = 2; i < lines.Length; i++)
{
var line = lines[i];
var lineStrings = line.Split('\t');
if (lineStrings[0] == _publisher)
{
return lineStrings[7];
}
}
return "xbqy";
}
static void SetIconAndSplashImage(string _versionName)
{
var buildTarget = GetBuildTarget();
var texture = AssetDatabase.LoadAssetAtPath(StringUtility.Contact("Assets/Editor/Logo/", _versionName, "/Icon.png"));
var iconSizes = PlayerSettings.GetIconSizesForTargetGroup(buildTarget);
var icons = new Texture2D[iconSizes.Length];
for (int i = 0; i < iconSizes.Length; i++)
{
icons[i] = texture;
}
PlayerSettings.SetIconsForTargetGroup(buildTarget, icons);
PlayerSettings.SplashScreen.show = false;
PlayerSettings.SplashScreen.showUnityLogo = false;
PlayerSettings.SplashScreen.unityLogoStyle = PlayerSettings.SplashScreen.UnityLogoStyle.LightOnDark;
var splashImage = AssetDatabase.LoadAssetAtPath(StringUtility.Contact("Assets/Editor/Logo/", _versionName, "/SplashImage.png"));
var splashScreenLogo = PlayerSettings.SplashScreenLogo.Create(3, splashImage);
PlayerSettings.SplashScreen.logos = new PlayerSettings.SplashScreenLogo[] { splashScreenLogo };
if (buildTarget == BuildTargetGroup.Android)
{
var projectSettings = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/ProjectSettings.asset")[0]);
var splashImageSetting = projectSettings.FindProperty("androidSplashScreen");
splashImageSetting.objectReferenceValue = splashImage.texture;
projectSettings.ApplyModifiedProperties();
PlayerSettings.Android.splashScreenScale = AndroidSplashScreenScale.ScaleToFill;
}
}
public static void SetCreateRoleAnimation()
{
var from = "Assets/Editor/Video/CreateRoleMovie.mp4";
var to = "Assets/StreamingAssets/CreateRoleMovie.mp4";
if (!Directory.Exists(Application.dataPath + "StreamingAssets"))
{
Directory.CreateDirectory(Application.dataPath + "StreamingAssets");
}
if (AssetDatabase.LoadAssetAtPath(to) == null)
{
AssetDatabase.CopyAsset(from, to);
}
}
static BuildTargetGroup GetBuildTarget()
{
#if UNITY_ANDROID
return BuildTargetGroup.Android;
#elif UNITY_IOS
return BuildTargetGroup.iOS;
#else
return BuildTargetGroup.Standalone;
#endif
}
}