using UnityEditor;
|
using UnityEditor.IMGUI.Controls;
|
using System.Collections.Generic;
|
using System.IO;
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using UnityEngine.AssetBundles.AssetBundleDataSource;
|
using Assets.Editor.Tool;
|
using HybridCLR.Editor.Commands;
|
using HybridCLR.Editor.Settings;
|
//using Mono.Cecil;
|
using DG.Tweening.Plugins.Core.PathCore;
|
using System;
|
using Path = System.IO.Path;
|
using HybridCLR.Editor.HotUpdate;
|
using HybridCLR.Editor;
|
using System.Text;
|
|
namespace UnityEngine.AssetBundles
|
{
|
[System.Serializable]
|
public class AssetBundleBuildTab
|
{
|
const string k_BuildPrefPrefix = "ABBBuild:";
|
// gui vars
|
//[SerializeField]
|
//private ValidBuildTarget m_BuildTarget = ValidBuildTarget.StandaloneWindows;
|
//[SerializeField]
|
//private CompressOptions m_Compression = CompressOptions.StandardCompression;
|
//private string m_OutputPath = string.Empty;
|
//[SerializeField]
|
//private bool m_UseDefaultPath = true;
|
private string m_streamingPath
|
{
|
get
|
{
|
switch (m_UserData.m_BuildTarget)
|
{
|
case ValidBuildTarget.Android:
|
return StringUtility.Contact("Assets/StreamingAssets", "/android");
|
case ValidBuildTarget.iOS:
|
return StringUtility.Contact("Assets/StreamingAssets", "/ios");
|
case ValidBuildTarget.StandaloneWindows:
|
case ValidBuildTarget.StandaloneWindows64:
|
return StringUtility.Contact("Assets/StreamingAssets", "/standalone");
|
default:
|
return "Assets/StreamingAssets";
|
}
|
}
|
}
|
|
[SerializeField]
|
private bool m_AdvancedSettings;
|
|
[SerializeField]
|
private Vector2 m_ScrollPosition;
|
|
|
class ToggleData
|
{
|
public ToggleData(bool s,
|
string title,
|
string tooltip,
|
List<string> onToggles,
|
BuildAssetBundleOptions opt = BuildAssetBundleOptions.None)
|
{
|
if (onToggles.Contains(title))
|
state = true;
|
else
|
state = s;
|
content = new GUIContent(title, tooltip);
|
option = opt;
|
}
|
//public string prefsKey
|
//{ get { return k_BuildPrefPrefix + content.text; } }
|
public bool state;
|
public GUIContent content;
|
public BuildAssetBundleOptions option;
|
}
|
|
[SerializeField]
|
private BuildTabData m_UserData;
|
|
[SerializeField] int m_Version;
|
|
public string ApkOutputPath
|
{
|
get { return LocalSave.GetString("APKOutPutPath"); }
|
set { LocalSave.SetString("APKOutPutPath", value); }
|
}
|
|
public string publishers
|
{
|
get { return LocalSave.GetString("APKPublishers"); }
|
set { LocalSave.SetString("APKPublishers", value); }
|
}
|
|
List<ToggleData> m_ToggleData;
|
ToggleData m_ForceRebuild;
|
ToggleData m_CopyToStreaming;
|
GUIContent m_TargetContent;
|
GUIContent m_CompressionContent;
|
public enum CompressOptions
|
{
|
Uncompressed = 0,
|
StandardCompression,
|
ChunkBasedCompression,
|
}
|
GUIContent[] m_CompressionOptions =
|
{
|
new GUIContent("No Compression"),
|
new GUIContent("Standard Compression (LZMA)"),
|
new GUIContent("Chunk Based Compression (LZ4)")
|
};
|
int[] m_CompressionValues = { 0, 1, 2 };
|
|
int rechargeSkin = 1;
|
string createRoleLevel = "001";
|
|
public AssetBundleBuildTab()
|
{
|
m_AdvancedSettings = false;
|
m_UserData = new BuildTabData();
|
m_UserData.m_OnToggles = new List<string>();
|
m_UserData.m_UseDefaultPath = true;
|
}
|
|
public void OnDisable()
|
{
|
var dataPath = System.IO.Path.GetFullPath(".");
|
dataPath = dataPath.Replace("\\", "/");
|
dataPath += "/Library/AssetBundleBrowserBuild.dat";
|
|
BinaryFormatter bf = new BinaryFormatter();
|
FileStream file = File.Create(dataPath);
|
|
bf.Serialize(file, m_UserData);
|
file.Close();
|
|
}
|
public void OnEnable(Rect pos, EditorWindow parent)
|
{
|
|
//LoadData...
|
var dataPath = System.IO.Path.GetFullPath(".");
|
dataPath = dataPath.Replace("\\", "/");
|
dataPath += "/Library/AssetBundleBrowserBuild.dat";
|
|
if (File.Exists(dataPath))
|
{
|
BinaryFormatter bf = new BinaryFormatter();
|
FileStream file = File.Open(dataPath, FileMode.Open);
|
var data = bf.Deserialize(file) as BuildTabData;
|
if (data != null)
|
m_UserData = data;
|
file.Close();
|
}
|
|
m_ToggleData = new List<ToggleData>();
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Exclude Type Information",
|
"不包含类型信息。发布web平台时,不能使用该选项。",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.DisableWriteTypeTree));
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Force Rebuild",
|
"强制重新build所有ab包。",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.ForceRebuildAssetBundle));
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Ignore Type Tree Changes",
|
"忽略typetree的变化,不能与DisableWriteTypeTree同时使用。",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.IgnoreTypeTreeChanges));
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Append Hash",
|
"附加hash到assetbundle名字中。",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.AppendHashToAssetBundleName));
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Strict Mode",
|
"使用严格模式build ab, 有任何非致命的error都不会build成功。",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.StrictMode));
|
m_ToggleData.Add(new ToggleData(
|
false,
|
"Dry Run Build",
|
"Do a dry run build.",
|
m_UserData.m_OnToggles,
|
BuildAssetBundleOptions.DryRunBuild));
|
|
|
m_ForceRebuild = new ToggleData(
|
false,
|
"Rebuild",
|
"选中后会清空所有旧的资源,全部重新打包(建议测试时不勾选可加快打包速度)",
|
m_UserData.m_OnToggles);
|
m_CopyToStreaming = new ToggleData(
|
false,
|
"Copy to StreamingAssets",
|
"资源打包完成后,会将所有资源拷贝至:" + m_streamingPath + " for use in stand-alone player.",
|
m_UserData.m_OnToggles);
|
|
m_TargetContent = new GUIContent("Build Target", "Choose target platform to build for.");
|
m_CompressionContent = new GUIContent("Compression", "Choose no compress, standard (LZMA), or chunk based (LZ4)");
|
|
if (m_UserData.m_UseDefaultPath)
|
{
|
ResetPathToDefault();
|
}
|
}
|
|
public void OnGUI(Rect pos)
|
{
|
m_ScrollPosition = EditorGUILayout.BeginScrollView(m_ScrollPosition);
|
bool newState = false;
|
var centeredStyle = GUI.skin.GetStyle("Label");
|
centeredStyle.alignment = TextAnchor.UpperCenter;
|
GUILayout.Label(new GUIContent("Example build setup"), centeredStyle);
|
//basic options
|
EditorGUILayout.Space();
|
GUILayout.BeginVertical();
|
|
// build target
|
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildTarget))
|
{
|
ValidBuildTarget tgt = (ValidBuildTarget)EditorGUILayout.EnumPopup(m_TargetContent, m_UserData.m_BuildTarget);
|
if (tgt != m_UserData.m_BuildTarget)
|
{
|
m_UserData.m_BuildTarget = tgt;
|
if (m_UserData.m_UseDefaultPath)
|
{
|
m_UserData.m_OutputPath = "AssetBundles/";
|
m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
|
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
}
|
}
|
}
|
|
|
////output path
|
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory))
|
{
|
EditorGUILayout.Space();
|
GUILayout.BeginHorizontal();
|
var newPath = EditorGUILayout.TextField("Output Path", m_UserData.m_OutputPath);
|
if ((newPath != m_UserData.m_OutputPath) &&
|
(newPath != string.Empty))
|
{
|
m_UserData.m_UseDefaultPath = false;
|
m_UserData.m_OutputPath = newPath;
|
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
}
|
GUILayout.EndHorizontal();
|
GUILayout.BeginHorizontal();
|
GUILayout.FlexibleSpace();
|
if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
|
BrowseForFolder();
|
if (GUILayout.Button("Reset", GUILayout.MaxWidth(75f)))
|
ResetPathToDefault();
|
//if (string.IsNullOrEmpty(m_OutputPath))
|
// m_OutputPath = EditorUserBuildSettings.GetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath");
|
GUILayout.EndHorizontal();
|
EditorGUILayout.Space();
|
|
newState = GUILayout.Toggle(
|
m_ForceRebuild.state,
|
m_ForceRebuild.content);
|
if (newState != m_ForceRebuild.state)
|
{
|
if (newState)
|
m_UserData.m_OnToggles.Add(m_ForceRebuild.content.text);
|
else
|
m_UserData.m_OnToggles.Remove(m_ForceRebuild.content.text);
|
m_ForceRebuild.state = newState;
|
}
|
newState = GUILayout.Toggle(
|
m_CopyToStreaming.state,
|
m_CopyToStreaming.content);
|
if (newState != m_CopyToStreaming.state)
|
{
|
if (newState)
|
m_UserData.m_OnToggles.Add(m_CopyToStreaming.content.text);
|
else
|
m_UserData.m_OnToggles.Remove(m_CopyToStreaming.content.text);
|
m_CopyToStreaming.state = newState;
|
}
|
}
|
|
// advanced options
|
using (new EditorGUI.DisabledScope(!AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions))
|
{
|
EditorGUILayout.Space();
|
m_AdvancedSettings = EditorGUILayout.Foldout(m_AdvancedSettings, "Advanced Settings");
|
if (m_AdvancedSettings)
|
{
|
var indent = EditorGUI.indentLevel;
|
EditorGUI.indentLevel = 1;
|
CompressOptions cmp = (CompressOptions)EditorGUILayout.IntPopup(
|
m_CompressionContent,
|
(int)m_UserData.m_Compression,
|
m_CompressionOptions,
|
m_CompressionValues);
|
|
if (cmp != m_UserData.m_Compression)
|
{
|
m_UserData.m_Compression = cmp;
|
}
|
foreach (var tog in m_ToggleData)
|
{
|
newState = EditorGUILayout.ToggleLeft(
|
tog.content,
|
tog.state);
|
if (newState != tog.state)
|
{
|
|
if (newState)
|
m_UserData.m_OnToggles.Add(tog.content.text);
|
else
|
m_UserData.m_OnToggles.Remove(tog.content.text);
|
tog.state = newState;
|
}
|
}
|
EditorGUILayout.Space();
|
EditorGUI.indentLevel = indent;
|
}
|
}
|
if (GUILayout.Button("HybridclrBuild(不能热更)"))
|
{
|
EditorApplication.delayCall += ExcuteBuildHybridclrBuild;
|
}
|
// build.
|
EditorGUILayout.Space();
|
if (GUILayout.Button("Build All Assets"))
|
{
|
EditorApplication.delayCall += ExecuteBuildAll;
|
}
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Config"))
|
{
|
EditorApplication.delayCall += ExcuteBuildConfig;
|
}
|
|
if (GUILayout.Button("UI"))
|
{
|
EditorApplication.delayCall += ExcuteBuildUI;
|
}
|
|
if (GUILayout.Button("BuiltIn"))
|
{
|
EditorApplication.delayCall += ExcuteBuildBuiltIn;
|
}
|
|
if (GUILayout.Button("Audio"))
|
{
|
EditorApplication.delayCall += ExcuteBuildAudio;
|
}
|
|
if (GUILayout.Button("Video"))
|
{
|
EditorApplication.delayCall += ExcuteBuildVideo;
|
}
|
|
if (GUILayout.Button("Maps"))
|
{
|
EditorApplication.delayCall += ExcuteBuildLevels;
|
}
|
|
if (GUILayout.Button("MobEffectShader"))
|
{
|
EditorApplication.delayCall += ExcuteBuildMobEffectShader;
|
}
|
|
if (GUILayout.Button("HybridclrUpdate"))
|
{
|
EditorApplication.delayCall += ExcuteBuildHybridclrUpdate;
|
}
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
|
|
EditorGUILayout.Space();
|
|
EditorGUILayout.BeginHorizontal();
|
|
if (GUILayout.Button("Make VersionFile"))
|
{
|
EditorApplication.delayCall += MakeAssetsVersionFile;
|
}
|
|
if (GUILayout.Button("Copy to StreamingAssets"))
|
{
|
EditorApplication.delayCall += CopyToStreamingAssets;
|
}
|
|
EditorGUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
EditorGUILayout.Space();
|
#if UNITY_ANDROID
|
GUILayout.BeginHorizontal();
|
EditorGUILayout.TextField("Apk Output Path", ApkOutputPath);
|
if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
|
{
|
BrowseForApkOutput();
|
}
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
#endif
|
|
#if UNITY_STANDALONE
|
GUILayout.BeginHorizontal();
|
EditorGUILayout.TextField("EXE Output Path", ApkOutputPath);
|
if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
|
{
|
BrowseForApkOutput();
|
}
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
#endif
|
|
GUILayout.BeginHorizontal();
|
EditorGUILayout.TextField("SDK Project Path", ClientPackage.SDK_PLUGIN_PROJECT);
|
if (GUILayout.Button("Browse", GUILayout.MaxWidth(75f)))
|
{
|
BrowseForSDKProject();
|
}
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
|
#if UNITY_IOS
|
ClientPackage.auditOutTime = EditorGUILayout.TextField("AppStore OutTime", ClientPackage.auditOutTime, GUILayout.Height(20));
|
EditorGUILayout.Space();
|
#endif
|
GUILayout.BeginHorizontal();
|
|
ClientPackage.AssetPrior = EditorGUILayout.IntField("AssetPrior", ClientPackage.AssetPrior, GUILayout.Height(50), GUILayout.Width(250));
|
publishers = EditorGUILayout.TextField("Publishers", publishers, GUILayout.Height(50));
|
GUILayout.EndHorizontal();
|
|
EditorGUILayout.Space();
|
GUILayout.BeginHorizontal();
|
|
ClientPackage.includeConfig = EditorGUILayout.Toggle("Include Config ", ClientPackage.includeConfig, GUILayout.Width(250));
|
ClientPackage.includeUI = EditorGUILayout.Toggle("Include UI ", ClientPackage.includeUI, GUILayout.Width(250));
|
|
GUILayout.EndHorizontal();
|
EditorGUILayout.Space();
|
GUILayout.BeginHorizontal();
|
|
//ClientPackage.obfuscatorEnabled = EditorGUILayout.Toggle("Obfuscator Enable ", ClientPackage.obfuscatorEnabled, GUILayout.Width(250));
|
|
#if UNITY_STANDALONE
|
if (GUILayout.Button("EXE"))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageStandalone;
|
}
|
#elif UNITY_ANDROID
|
if (GUILayout.Button("APK"))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageApk;
|
}
|
#elif UNITY_IOS
|
if (GUILayout.Button("IPA_Append "))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageIpaAppend;
|
}
|
#endif
|
|
#if UNITY_STANDALONE
|
if (GUILayout.Button("EXE Development "))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageStandaloneDevelopment;
|
}
|
#elif UNITY_ANDROID
|
if (GUILayout.Button("APK Development"))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageDevelopApk;
|
}
|
#elif UNITY_IOS
|
if (GUILayout.Button("IPA_Replace"))
|
{
|
EditorApplication.delayCall += ExecuteBuildClientPackageIpaReplace;
|
}
|
#endif
|
|
GUILayout.EndHorizontal();
|
|
GUILayout.Space(20);
|
if (GUILayout.Button("SwitchVersionConfig"))
|
{
|
EditorApplication.delayCall += ExecuteSwitchVersionConfig;
|
}
|
|
GUILayout.Space(20);
|
GUILayout.BeginHorizontal();
|
rechargeSkin = EditorGUILayout.IntField("Recharge Skin", rechargeSkin, GUILayout.Width(250));
|
if (GUILayout.Button("Switch"))
|
{
|
UpdateSpriteSetting.SetRechargeSkin(rechargeSkin);
|
AssetDatabase.Refresh();
|
UpdateSpritePackingSetting.UpdateAllSpritePackingSetting();
|
}
|
|
createRoleLevel = EditorGUILayout.TextField("CreateRole Level", createRoleLevel, GUILayout.Width(250));
|
if (GUILayout.Button("Switch"))
|
{
|
UpdateLevelSetting.SetCreateRoleLevel(createRoleLevel);
|
AssetDatabase.Refresh();
|
}
|
GUILayout.EndHorizontal();
|
|
GUILayout.EndVertical();
|
EditorGUILayout.EndScrollView();
|
}
|
|
private void CopyToStreamingAssets()
|
{
|
if (Directory.Exists(m_streamingPath))
|
Directory.Delete(m_streamingPath, true);
|
DirectoryCopy(m_UserData.m_OutputPath, m_streamingPath);
|
}
|
|
private void ExecuteBuildAll()
|
{
|
|
if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOutputDirectory)
|
{
|
if (string.IsNullOrEmpty(m_UserData.m_OutputPath))
|
{
|
BrowseForFolder();
|
}
|
|
if (string.IsNullOrEmpty(m_UserData.m_OutputPath)) //in case they hit "cancel" on the open browser
|
{
|
Debug.LogError("AssetBundle Build: No valid output path for build.");
|
return;
|
}
|
|
if (m_ForceRebuild.state)
|
{
|
try
|
{
|
if (Directory.Exists(m_UserData.m_OutputPath))
|
{
|
Directory.Delete(m_UserData.m_OutputPath, true);
|
}
|
|
if (m_CopyToStreaming.state)
|
{
|
if (Directory.Exists(m_streamingPath))
|
{
|
Directory.Delete(m_streamingPath, true);
|
}
|
}
|
}
|
catch (System.Exception e)
|
{
|
Debug.LogException(e);
|
}
|
}
|
|
if (!Directory.Exists(m_UserData.m_OutputPath))
|
{
|
Directory.CreateDirectory(m_UserData.m_OutputPath);
|
}
|
}
|
|
ExcuteBuildAudio();
|
ExcuteBuildVideo();
|
ExcuteBuildMobEffectShader();
|
ExcuteBuildConfig();
|
ExcuteBuildLevels();
|
ExcuteBuildUI();
|
ExcuteBuildBuiltIn();
|
ExcuteBuildHybridclrUpdate();
|
|
AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
|
|
MakeAssetsVersionFile();
|
|
if (m_CopyToStreaming.state)
|
{
|
if (Directory.Exists(m_streamingPath))
|
Directory.Delete(m_streamingPath, true);
|
DirectoryCopy(m_UserData.m_OutputPath, m_streamingPath);
|
}
|
}
|
|
private void ExcuteBuildAsset(string _category, bool uncompressed = false)
|
{
|
BuildAssetBundleOptions opt = BuildAssetBundleOptions.None;
|
if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions)
|
{
|
if (m_UserData.m_Compression == CompressOptions.Uncompressed || uncompressed)
|
{
|
opt |= BuildAssetBundleOptions.UncompressedAssetBundle;
|
}
|
else if (m_UserData.m_Compression == CompressOptions.ChunkBasedCompression)
|
{
|
opt |= BuildAssetBundleOptions.ChunkBasedCompression;
|
}
|
|
//opt |= BuildAssetBundleOptions.DeterministicAssetBundle;
|
|
foreach (var tog in m_ToggleData)
|
{
|
if (tog.state)
|
{
|
opt |= tog.option;
|
}
|
}
|
}
|
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
AssetBundleBuildExtersion.Build(outputPath, _category, opt, (BuildTarget)m_UserData.m_BuildTarget, m_ForceRebuild.state);
|
}
|
|
private void ExcuteBuildBuiltIn()
|
{
|
BuiltInResourceSetting.SetLaunchBackGround(publishers.Split('|')[0], (BuildTarget)m_UserData.m_BuildTarget);
|
BuiltInResourceSetting.SetLoginBackGround(publishers.Split('|')[0], (BuildTarget)m_UserData.m_BuildTarget);
|
BuiltInResourceSetting.SetLoginLogo(publishers.Split('|')[0], (BuildTarget)m_UserData.m_BuildTarget);
|
UpdateBuiltInSetting.SetBuiltinAssetBundleName();
|
ExcuteBuildAsset("builtin");
|
}
|
|
private void ExcuteBuildAudio()
|
{
|
UpdateAudioSetting.SetAllAudioAssetBundleName();
|
ExcuteBuildAsset("audio");
|
}
|
|
private void ExcuteBuildVideo()
|
{
|
UpdateVideoSetting.SetAllVideoAssetBundleName();
|
ExcuteBuildAsset("video");
|
}
|
|
private void ExcuteBuildMobEffectShader()
|
{
|
UpdateEffectPrefabSetting.SetAllEffectPrefabAssetBundleName();
|
UpdateMobSetting.SetAllMobAssetBundleName();
|
UpdateShaderSetting.SetAllShaderAssetBundleName();
|
ExcuteBuildAsset("mobeffectshader");
|
}
|
|
private void ExcuteBuildConfig()
|
{
|
UpdateScriptableObjectsSetting.SetAllScriptableObjectAssetBundleName();
|
ExcuteBuildAsset("config");
|
TableTool.CopyConfigsToOutPutPath(StringUtility.Contact(m_UserData.m_OutputPath, "/config"));
|
}
|
//发包时获取热更dll和裁剪AOT
|
private void ExcuteBuildHybridclrBuild()
|
{
|
//重新生成热更dll和裁剪AOT
|
PrebuildCommand.GenerateAll();
|
// 输出到AssetBundles目录
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
outputPath = StringUtility.Contact(outputPath, "/logicbytes");
|
if (Directory.Exists(outputPath))
|
Directory.Delete(outputPath, true);
|
//复制新生成的AOT dll到指定路径
|
CopyAOTDataDll(outputPath);
|
//复制热更dll到指定路径
|
CopyHotUpdateDll(outputPath);
|
AssetDatabase.Refresh();
|
}
|
//热更新时获取热更dll
|
private void ExcuteBuildHybridclrUpdate()
|
{
|
//生成热更dll
|
CompileDllCommand.CompileDll(EditorUserBuildSettings.activeBuildTarget);
|
//判断当前的dll有没有使用了被裁剪的aot dll
|
if (IsAccessMissingMetadata())
|
{
|
DebugEx.LogError("当前热更代码使用了被裁剪的aot dll,请使用HybridclrBuild重新生成,重新出包");
|
return;
|
}
|
// 输出到AssetBundles目录
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
outputPath = StringUtility.Contact(outputPath, "/logicbytes");
|
CopyHotUpdateDll(outputPath);
|
MakeBytesVersionFile();
|
AssetDatabase.Refresh();
|
}
|
|
private void MakeBytesVersionFile()
|
{
|
ChangeBytes();
|
var fileInfos = new List<FileInfo>();
|
FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(m_UserData.m_OutputPath, "/logicbytes"), fileInfos);
|
BytesVersionMaker.WriteAssetsVersionFile(Path.Combine(Directory.GetParent(Application.dataPath).FullName, m_UserData.m_OutputPath), fileInfos);
|
Debug.Log("热更新代码更新完毕,生成md5文件");
|
}
|
|
//复制裁剪AOT到指定目录
|
void CopyAOTDataDll(string outputPath)
|
{
|
//项目根目录
|
string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/");
|
//当前构建的平台名称
|
string platformName = "/" + EditorUserBuildSettings.activeBuildTarget + "/";
|
//上次出包时使用的AOT所在路径
|
string checkAotDir = rootDir + "/HybridCLRData/CheckAssembliesPostIl2CppStrip" + platformName;
|
//HybridCLR Settings面板上设置的裁减后AOT dll输出根目录
|
string strippedAOTDllOutputRootDir = "/" + HybridCLRSettings.Instance.strippedAOTDllOutputRootDir;
|
//重新生成的AOT所在路径
|
string aotDir = StringUtility.Contact(rootDir, strippedAOTDllOutputRootDir, platformName);
|
|
if (Directory.Exists(checkAotDir))
|
{
|
Directory.Delete(checkAotDir, true);
|
Directory.CreateDirectory(checkAotDir);
|
}
|
//复制一份出包时使用的AOT
|
CopyDirectoryToPath(aotDir, checkAotDir);
|
|
for (int i = 0; i < AOTGenericReferences.PatchedAOTAssemblyList.Count; i++)
|
{
|
string dllFile = StringUtility.Contact(aotDir, AOTGenericReferences.PatchedAOTAssemblyList[i]);
|
var outDllFile = outputPath + "/" + AOTGenericReferences.PatchedAOTAssemblyList[i] + ".bytes";
|
if (!File.Exists(dllFile))
|
{
|
DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile);
|
return;
|
}
|
|
FileExtersion.MakeSureDirectory(outDllFile);
|
File.Copy(dllFile, outDllFile, true);
|
}
|
}
|
|
//复制热更dll到指定目录
|
void CopyHotUpdateDll(string outputPath)
|
{
|
//项目根目录
|
string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/");
|
//当前构建的平台名称
|
string platformName = "/" + EditorUserBuildSettings.activeBuildTarget + "/";
|
//HybridCLR Settings面板上设置的热更新dll编译输出根目录
|
string hotUpdateDllCompileOutputRootDir = "/" + HybridCLRSettings.Instance.hotUpdateDllCompileOutputRootDir;
|
//热更DLL生成目录
|
string hotDllDir = StringUtility.Contact(rootDir, hotUpdateDllCompileOutputRootDir, platformName);
|
//HybridCLR的Settings面板上设置的热更新Assembly Definitions 资源
|
var assemblyDefinitionAsset = HybridCLRSettings.Instance.hotUpdateAssemblyDefinitions;
|
for (int i = 0; i < assemblyDefinitionAsset.Length; i++)
|
{
|
string assemblyPath = AssetDatabase.GetAssetPath(assemblyDefinitionAsset[i]);
|
string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath);
|
|
string dllFile = StringUtility.Contact(hotDllDir, assemblyName, ".dll");
|
var outDllFile = outputPath + "/" + assemblyName + ".dll.bytes";
|
if (!File.Exists(dllFile))
|
{
|
DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile);
|
return;
|
}
|
FileExtersion.MakeSureDirectory(outDllFile);
|
File.Copy(dllFile, outDllFile, true);
|
}
|
|
//HybridCLR的Settings面板上设置的要热更的DLL资源
|
string[] hotUpdateAssemblies = HybridCLRSettings.Instance.hotUpdateAssemblies;
|
for (int i = 0; i < hotUpdateAssemblies.Length; i++)
|
{
|
string dllFile = StringUtility.Contact(hotDllDir, hotUpdateAssemblies[i], ".dll");
|
var outDllFile = outputPath + "/" + hotUpdateAssemblies[i] + ".dll.bytes";
|
if (!File.Exists(dllFile))
|
{
|
DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile);
|
return;
|
}
|
FileExtersion.MakeSureDirectory(outDllFile);
|
File.Copy(dllFile, outDllFile, true);
|
}
|
|
//HybridCLR的Settings面板上设置的预留的热更新dlls
|
string[] preserveHotUpdateAssemblies = HybridCLRSettings.Instance.preserveHotUpdateAssemblies;
|
for (int i = 0; i < preserveHotUpdateAssemblies.Length; i++)
|
{
|
string dllFile = StringUtility.Contact(hotDllDir, preserveHotUpdateAssemblies[i], ".dll");
|
var outDllFile = outputPath + "/" + preserveHotUpdateAssemblies[i] + ".dll.bytes";
|
if (!File.Exists(dllFile))
|
{
|
DebugEx.LogWarningFormat("警告:预留的热更新dll: {0} 不存在", dllFile);
|
continue;
|
}
|
FileExtersion.MakeSureDirectory(outDllFile);
|
File.Copy(dllFile, outDllFile, true);
|
}
|
}
|
|
//检查目标文件夹下是否有文件
|
bool IsFolderNotEmpty(string dir)
|
{
|
if (Directory.Exists(dir))
|
{
|
string[] files = Directory.GetFiles(dir);
|
if (files.Length > 0)
|
return true;
|
}
|
return false;
|
}
|
|
//检查热更新代码中是否引用了被裁剪的类型或函数
|
bool IsAccessMissingMetadata()
|
{
|
//项目根目录
|
string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/");
|
//当前构建的平台名称
|
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
|
string platformName = "/" + target + "/";
|
//上次出包时使用的AOT所在路径
|
string checkAotDir = rootDir + "/HybridCLRData/CheckAssembliesPostIl2CppStrip" + platformName;
|
|
//上次打包时保存的AOT所在文件夹不存在或没有dll文件
|
if (!IsFolderNotEmpty(checkAotDir))
|
return true;
|
var checker = new MissingMetadataChecker(checkAotDir, new List<string>());
|
string hotUpdateDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target);
|
for (int i = 0; i < SettingsUtil.HotUpdateAssemblyFilesExcludePreserved.Count; i++)
|
{
|
string dllPath = hotUpdateDir + "/" + SettingsUtil.HotUpdateAssemblyFilesExcludePreserved[i];
|
bool notAnyMissing = checker.Check(dllPath);
|
if (!notAnyMissing)
|
{
|
return true;
|
}
|
}
|
return false;
|
}
|
private void CopyDirectoryToPath(string sourceDir, string destinationDir)
|
{
|
// 如果目标路径不存在,则创建它
|
if (!Directory.Exists(destinationDir))
|
Directory.CreateDirectory(destinationDir);
|
// 获取所有文件并复制
|
string[] files = Directory.GetFiles(sourceDir);
|
for (int i = 0; i < files.Length; i++)
|
{
|
string dest = Path.Combine(destinationDir, Path.GetFileName(files[i]));
|
File.Copy(files[i], dest, true); // true表示如果目标存在,则覆盖
|
}
|
}
|
|
private void ExcuteBuildLevels()
|
{
|
UpdateLevelSetting.SetAllLevelAssetBundleName();
|
ExcuteBuildAsset("maps");
|
}
|
|
private void ExcuteBuildUI()
|
{
|
AssetSource.allFromEditor = true;
|
//CheckFontSwitch.CheckAndReplaceFontSwitch();
|
UpdateUIPrefabSetting.SetAllUIPrefabAssetBundleName();
|
UpdateUIWindowSetting.SetAllUIWindowAssetBundleName();
|
|
ExcuteBuildAsset("ui");
|
}
|
|
private void SetAssetsVersion()
|
{
|
var fileInfos = new List<FileInfo>();
|
FileExtersion.GetAllDirectoryFileInfos(m_UserData.m_OutputPath, fileInfos);
|
for (int i = 0; i < fileInfos.Count; i++)
|
{
|
var fileInfo = fileInfos[i];
|
var fullName = FileExtersion.RemoveVersionFromFileFullName(fileInfo.FullName);
|
var newFullName = FileExtersion.AddVersionToFileFullName(fullName, m_Version);
|
if (fullName != newFullName)
|
{
|
File.Copy(fullName, newFullName, true);
|
File.Delete(fullName);
|
}
|
}
|
}
|
|
private void RemoveAssetsVersion()
|
{
|
var fileInfos = new List<FileInfo>();
|
FileExtersion.GetAllDirectoryFileInfos(m_UserData.m_OutputPath, fileInfos);
|
for (int i = 0; i < fileInfos.Count; i++)
|
{
|
var fileInfo = fileInfos[i];
|
var fullName = FileExtersion.RemoveVersionFromFileFullName(fileInfo.FullName);
|
if (fullName != fileInfo.FullName)
|
{
|
File.Copy(fileInfo.FullName, fullName, true);
|
File.Delete(fileInfo.FullName);
|
}
|
}
|
}
|
|
private void MakeAssetsVersionFile()
|
{
|
var fileInfos = new List<FileInfo>();
|
FileExtersion.GetAllDirectoryFileInfos(m_UserData.m_OutputPath, fileInfos);
|
AssetsVersionMaker.WriteAssetsVersionFile(Path.Combine(Directory.GetParent(Application.dataPath).FullName, m_UserData.m_OutputPath), fileInfos);
|
|
Debug.Log("生成AssetsVersion.txt文件完毕");
|
}
|
|
void ChangeBytes()
|
{
|
string path = StringUtility.Contact(m_UserData.m_OutputPath, "/logicbytes/");
|
// 原始DLL字节内容文件的路径
|
string originalDllBytesFilePath = path + "Assembly-CSharp.dll.bytes";
|
|
// 临时文件的路径,用于写入"abc"后再追加原始DLL字节内容
|
string tempFilePath = path + "temp_Assembly-CSharp.dll.bytes";
|
|
// 要写入的字符串"abc"
|
string contentToWrite = "abc";
|
|
// 将"abc"转换为字节数组
|
byte[] abcBytes = Encoding.ASCII.GetBytes(contentToWrite);
|
|
// 读取原始DLL字节内容到字节数组
|
byte[] originalDllBytes = File.ReadAllBytes(originalDllBytesFilePath);
|
|
// 将"abc"写入临时文件
|
using (FileStream fs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write))
|
{
|
fs.Write(abcBytes, 0, abcBytes.Length);
|
|
// 追加原始DLL字节内容到临时文件
|
fs.Write(originalDllBytes, 0, originalDllBytes.Length);
|
}
|
|
ReplaceFile(tempFilePath, originalDllBytesFilePath);
|
}
|
|
public void ReplaceFile(string sourceFile, string targetFile)
|
{
|
// 确保源文件存在
|
if (!File.Exists(sourceFile))
|
{
|
throw new FileNotFoundException("源文件未找到:" + sourceFile);
|
}
|
|
// 如果目标文件存在,则删除它
|
if (File.Exists(targetFile))
|
{
|
File.Delete(targetFile);
|
}
|
|
// 将源文件重命名为目标文件
|
File.Move(sourceFile, targetFile);
|
}
|
|
|
static int packageIndex
|
{
|
get { return LocalSave.GetInt("ClientPackageIndex", 1); }
|
set { LocalSave.SetInt("ClientPackageIndex", value); }
|
}
|
|
private void ExecuteBuildClientPackageIpaAppend()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, false, false);
|
}
|
|
private void ExecuteBuildClientPackageIpaReplace()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, false, true);
|
}
|
|
private void ExecuteBuildClientPackageApk()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, false, false);
|
}
|
|
private void ExecuteBuildClientPackageDevelopApk()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage.BuildPublishers(ClientPackage.SDK_PLUGIN_PROJECT, outputPath, ApkOutputPath, publishers, packageIndex, true, false);
|
}
|
|
private void ExecuteBuildClientPackageStandalone()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage_Standalone.Build(new ClientPackage_Standalone.BuildParams()
|
{
|
assetBundle = outputPath,
|
output = ApkOutputPath,
|
buildIndex = packageIndex,
|
development = false,
|
publisher = publishers,
|
});
|
}
|
|
private void ExecuteBuildClientPackageStandaloneDevelopment()
|
{
|
packageIndex++;
|
var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath);
|
ClientPackage_Standalone.Build(new ClientPackage_Standalone.BuildParams()
|
{
|
assetBundle = outputPath,
|
output = ApkOutputPath,
|
buildIndex = packageIndex,
|
development = true,
|
publisher = publishers,
|
});
|
}
|
|
private void ExecuteSwitchVersionConfig()
|
{
|
var newVersionConfigPath = StringUtility.Contact("Assets/Resources/ScriptableObject/Config/VersionConfig", ".asset");
|
var versionsFilePath = Application.dataPath + Path.DirectorySeparatorChar + "Editor/VersionConfigs/Versions.txt";
|
|
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] == publishers)
|
{
|
VersionConfig.Get().Read(line);
|
break;
|
}
|
}
|
}
|
|
private static void DirectoryCopy(string sourceDirName, string destDirName, string excludeEx = null)
|
{
|
// Get the subdirectories for the specified directory.
|
DirectoryInfo dir = new DirectoryInfo(sourceDirName);
|
|
// If the destination directory doesn't exist, create it.
|
if (!Directory.Exists(destDirName))
|
{
|
Directory.CreateDirectory(destDirName);
|
}
|
|
// Get the files in the directory and copy them to the new location.
|
FileInfo[] files = dir.GetFiles();
|
foreach (FileInfo file in files)
|
{
|
string temppath = Path.Combine(destDirName, file.Name);
|
if (excludeEx == null || file.Extension != excludeEx)
|
file.CopyTo(temppath, false);
|
}
|
|
DirectoryInfo[] dirs = dir.GetDirectories();
|
foreach (DirectoryInfo subdir in dirs)
|
{
|
string temppath = Path.Combine(destDirName, subdir.Name);
|
DirectoryCopy(subdir.FullName, temppath);
|
}
|
}
|
|
private void BrowseForFolder()
|
{
|
m_UserData.m_UseDefaultPath = false;
|
var newPath = EditorUtility.OpenFolderPanel("Bundle Folder", m_UserData.m_OutputPath, string.Empty);
|
if (!string.IsNullOrEmpty(newPath))
|
{
|
var gamePath = System.IO.Path.GetFullPath(".");
|
gamePath = gamePath.Replace("\\", "/");
|
if (newPath.StartsWith(gamePath) && newPath.Length > gamePath.Length)
|
newPath = newPath.Remove(0, gamePath.Length + 1);
|
m_UserData.m_OutputPath = newPath;
|
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
}
|
}
|
|
private void BrowseForApkOutput()
|
{
|
var newPath = EditorUtility.OpenFolderPanel("Apk Folder", ApkOutputPath, string.Empty);
|
if (!string.IsNullOrEmpty(newPath))
|
{
|
var gamePath = System.IO.Path.GetFullPath(".");
|
gamePath = gamePath.Replace("\\", "/");
|
if (newPath.StartsWith(gamePath) && newPath.Length > gamePath.Length)
|
newPath = newPath.Remove(0, gamePath.Length + 1);
|
ApkOutputPath = newPath;
|
}
|
}
|
|
private void BrowseForSDKProject()
|
{
|
var newPath = EditorUtility.OpenFolderPanel("Apk Folder", ClientPackage.SDK_PLUGIN_PROJECT, string.Empty);
|
if (!string.IsNullOrEmpty(newPath))
|
{
|
var gamePath = System.IO.Path.GetFullPath(".");
|
gamePath = gamePath.Replace("\\", "/");
|
if (newPath.StartsWith(gamePath) && newPath.Length > gamePath.Length)
|
newPath = newPath.Remove(0, gamePath.Length + 1);
|
ClientPackage.SDK_PLUGIN_PROJECT = newPath;
|
}
|
}
|
|
private void ResetPathToDefault()
|
{
|
m_UserData.m_UseDefaultPath = true;
|
m_UserData.m_OutputPath = "AssetBundles/";
|
m_UserData.m_OutputPath += m_UserData.m_BuildTarget.ToString();
|
//EditorUserBuildSettings.SetPlatformSettings(EditorUserBuildSettings.activeBuildTarget.ToString(), "AssetBundleOutputPath", m_OutputPath);
|
}
|
|
//Note: this is the provided BuildTarget enum with some entries removed as they are invalid in the dropdown
|
public enum ValidBuildTarget
|
{
|
//NoTarget = -2, --doesn't make sense
|
//iPhone = -1, --deprecated
|
//BB10 = -1, --deprecated
|
//MetroPlayer = -1, --deprecated
|
StandaloneOSXUniversal = 2,
|
StandaloneOSXIntel = 4,
|
StandaloneWindows = 5,
|
WebPlayer = 6,
|
WebPlayerStreamed = 7,
|
iOS = 9,
|
PS3 = 10,
|
XBOX360 = 11,
|
Android = 13,
|
StandaloneLinux = 17,
|
StandaloneWindows64 = 19,
|
WebGL = 20,
|
WSAPlayer = 21,
|
StandaloneLinux64 = 24,
|
StandaloneLinuxUniversal = 25,
|
WP8Player = 26,
|
StandaloneOSXIntel64 = 27,
|
BlackBerry = 28,
|
Tizen = 29,
|
PSP2 = 30,
|
PS4 = 31,
|
PSM = 32,
|
XboxOne = 33,
|
SamsungTV = 34,
|
N3DS = 35,
|
WiiU = 36,
|
tvOS = 37,
|
Switch = 38
|
}
|
|
[System.Serializable]
|
public class BuildTabData
|
{
|
public List<string> m_OnToggles;
|
public ValidBuildTarget m_BuildTarget = ValidBuildTarget.StandaloneWindows;
|
public CompressOptions m_Compression = CompressOptions.StandardCompression;
|
public string m_OutputPath = string.Empty;
|
public bool m_UseDefaultPath = true;
|
}
|
}
|
|
}
|