2025-03-31 14:55:24 +08:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Text ;
using UnityEditor ;
using UnityEngine ;
using Debug = UnityEngine . Debug ;
using System.Text.RegularExpressions ;
using System.Linq ;
using HybridCLR.Editor.Settings ;
namespace HybridCLR.Editor.Installer
{
public class InstallerController
{
private const string hybridclr_repo_path = "hybridclr_repo" ;
private const string il2cpp_plus_repo_path = "il2cpp_plus_repo" ;
public int MajorVersion = > _curVersion . major ;
private readonly UnityVersion _curVersion ;
private readonly HybridclrVersionManifest _versionManifest ;
private readonly HybridclrVersionInfo _curDefaultVersion ;
public string PackageVersion { get ; private set ; }
public string InstalledLibil2cppVersion { get ; private set ; }
public InstallerController ( )
{
_curVersion = ParseUnityVersion ( Application . unityVersion ) ;
_versionManifest = GetHybridCLRVersionManifest ( ) ;
2025-06-12 18:00:06 +08:00
_curDefaultVersion = _versionManifest . versions . FirstOrDefault ( v = > _curVersion . isTuanjieEngine ? v . unity_version = = $"{_curVersion.major}-tuanjie" : v . unity_version = = _curVersion . major . ToString ( ) ) ;
2025-03-31 14:55:24 +08:00
PackageVersion = LoadPackageInfo ( ) . version ;
InstalledLibil2cppVersion = ReadLocalVersion ( ) ;
}
private HybridclrVersionManifest GetHybridCLRVersionManifest ( )
{
string versionFile = $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/hybridclr_version.json" ;
return JsonUtility . FromJson < HybridclrVersionManifest > ( File . ReadAllText ( versionFile , Encoding . UTF8 ) ) ;
}
private PackageInfo LoadPackageInfo ( )
{
string packageJson = $"{SettingsUtil.ProjectDir}/Packages/{SettingsUtil.PackageName}/package.json" ;
return JsonUtility . FromJson < PackageInfo > ( File . ReadAllText ( packageJson , Encoding . UTF8 ) ) ;
}
[Serializable]
class PackageInfo
{
public string name ;
public string version ;
}
[Serializable]
class VersionDesc
{
public string branch ;
//public string hash;
}
[Serializable]
class HybridclrVersionInfo
{
public string unity_version ;
public VersionDesc hybridclr ;
public VersionDesc il2cpp_plus ;
}
[Serializable]
class HybridclrVersionManifest
{
public List < HybridclrVersionInfo > versions ;
}
private class UnityVersion
{
public int major ;
public int minor1 ;
public int minor2 ;
2025-06-12 18:00:06 +08:00
public bool isTuanjieEngine ;
2025-03-31 14:55:24 +08:00
public override string ToString ( )
{
return $"{major}.{minor1}.{minor2}" ;
}
}
private static readonly Regex s_unityVersionPat = new Regex ( @"(\d+)\.(\d+)\.(\d+)" ) ;
private UnityVersion ParseUnityVersion ( string versionStr )
{
var matches = s_unityVersionPat . Matches ( versionStr ) ;
if ( matches . Count = = 0 )
{
return null ;
}
Match match = matches [ matches . Count - 1 ] ;
int major = int . Parse ( match . Groups [ 1 ] . Value ) ;
int minor1 = int . Parse ( match . Groups [ 2 ] . Value ) ;
int minor2 = int . Parse ( match . Groups [ 3 ] . Value ) ;
2025-06-12 18:00:06 +08:00
bool isTuanjieEngine = versionStr . Contains ( "t" ) ;
return new UnityVersion { major = major , minor1 = minor1 , minor2 = minor2 , isTuanjieEngine = isTuanjieEngine } ;
2025-03-31 14:55:24 +08:00
}
public string GetCurrentUnityVersionMinCompatibleVersionStr ( )
{
return GetMinCompatibleVersion ( MajorVersion ) ;
}
public string GetMinCompatibleVersion ( int majorVersion )
{
switch ( majorVersion )
{
2025-06-12 18:00:06 +08:00
case 2019 : return "2019.4.0" ;
case 2020 : return "2020.3.0" ;
case 2021 : return "2021.3.0" ;
case 2022 : return "2022.3.0" ;
case 2023 : return "2023.2.0" ;
case 6000 : return "6000.0.0" ;
2025-03-31 14:55:24 +08:00
default : return $"2020.3.0" ;
}
}
public enum CompatibleType
{
Compatible ,
MaybeIncompatible ,
Incompatible ,
}
public CompatibleType GetCompatibleType ( )
{
UnityVersion version = _curVersion ;
if ( version = = null )
{
return CompatibleType . Incompatible ;
}
2025-06-12 18:00:06 +08:00
if ( ( version . major = = 2019 & & version . minor1 < 4 )
| | ( version . major > = 2020 & & version . major < = 2022 & & version . minor1 < 3 ) )
2025-03-31 14:55:24 +08:00
{
return CompatibleType . MaybeIncompatible ;
}
return CompatibleType . Compatible ;
}
public string HybridclrLocalVersion = > _curDefaultVersion ? . hybridclr ? . branch ;
public string Il2cppPlusLocalVersion = > _curDefaultVersion ? . il2cpp_plus ? . branch ;
private string GetIl2CppPathByContentPath ( string contentPath )
{
return $"{contentPath}/il2cpp" ;
}
public string ApplicationIl2cppPath = > GetIl2CppPathByContentPath ( EditorApplication . applicationContentsPath ) ;
public string LocalVersionFile = > $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/generated/libil2cpp-version.txt" ;
private string ReadLocalVersion ( )
{
if ( ! File . Exists ( LocalVersionFile ) )
{
return null ;
}
return File . ReadAllText ( LocalVersionFile , Encoding . UTF8 ) ;
}
public void WriteLocalVersion ( )
{
InstalledLibil2cppVersion = PackageVersion ;
File . WriteAllText ( LocalVersionFile , PackageVersion , Encoding . UTF8 ) ;
Debug . Log ( $"Write installed version:'{PackageVersion}' to {LocalVersionFile}" ) ;
}
public void InstallDefaultHybridCLR ( )
{
InstallFromLocal ( PrepareLibil2cppWithHybridclrFromGitRepo ( ) ) ;
}
public bool HasInstalledHybridCLR ( )
{
return Directory . Exists ( $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr" ) ;
}
2025-06-12 18:00:06 +08:00
private string GetUnityIl2CppDllInstallLocation ( )
{
#if UNITY_EDITOR_WIN
return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/net471/Unity.IL2CPP.dll" ;
#else
return $"{SettingsUtil.LocalIl2CppDir}/build/deploy/il2cppcore/Unity.IL2CPP.dll" ;
#endif
}
private string GetUnityIl2CppDllModifiedPath ( string curVersionStr )
{
#if UNITY_EDITOR_WIN
return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Win.dll" ;
#else
return $"{SettingsUtil.ProjectDir}/{SettingsUtil.HybridCLRDataPathInPackage}/ModifiedUnityAssemblies/{curVersionStr}/Unity.IL2CPP-Mac.dll" ;
#endif
}
2025-03-31 14:55:24 +08:00
void CloneBranch ( string workDir , string repoUrl , string branch , string repoDir )
{
BashUtil . RemoveDir ( repoDir ) ;
BashUtil . RunCommand ( workDir , "git" , new string [ ] { "clone" , "-b" , branch , "--depth" , "1" , repoUrl , repoDir } ) ;
}
private string PrepareLibil2cppWithHybridclrFromGitRepo ( )
{
string workDir = SettingsUtil . HybridCLRDataDir ;
Directory . CreateDirectory ( workDir ) ;
//BashUtil.RecreateDir(workDir);
// clone hybridclr
string hybridclrRepoURL = HybridCLRSettings . Instance . hybridclrRepoURL ;
string hybridclrRepoDir = $"{workDir}/{hybridclr_repo_path}" ;
CloneBranch ( workDir , hybridclrRepoURL , _curDefaultVersion . hybridclr . branch , hybridclrRepoDir ) ;
if ( ! Directory . Exists ( hybridclrRepoDir ) )
{
throw new Exception ( $"clone hybridclr fail. url: {hybridclrRepoURL}" ) ;
}
// clone il2cpp_plus
string il2cppPlusRepoURL = HybridCLRSettings . Instance . il2cppPlusRepoURL ;
string il2cppPlusRepoDir = $"{workDir}/{il2cpp_plus_repo_path}" ;
CloneBranch ( workDir , il2cppPlusRepoURL , _curDefaultVersion . il2cpp_plus . branch , il2cppPlusRepoDir ) ;
if ( ! Directory . Exists ( il2cppPlusRepoDir ) )
{
throw new Exception ( $"clone il2cpp_plus fail. url: {il2cppPlusRepoDir}" ) ;
}
Directory . Move ( $"{hybridclrRepoDir}/hybridclr" , $"{il2cppPlusRepoDir}/libil2cpp/hybridclr" ) ;
return $"{il2cppPlusRepoDir}/libil2cpp" ;
}
public void InstallFromLocal ( string libil2cppWithHybridclrSourceDir )
{
RunInitLocalIl2CppData ( ApplicationIl2cppPath , libil2cppWithHybridclrSourceDir , _curVersion ) ;
}
private void RunInitLocalIl2CppData ( string editorIl2cppPath , string libil2cppWithHybridclrSourceDir , UnityVersion version )
{
if ( GetCompatibleType ( ) = = CompatibleType . Incompatible )
{
Debug . LogError ( $"Incompatible with current version, minimum compatible version: {GetCurrentUnityVersionMinCompatibleVersionStr()}" ) ;
return ;
}
string workDir = SettingsUtil . HybridCLRDataDir ;
Directory . CreateDirectory ( workDir ) ;
// create LocalIl2Cpp
string localUnityDataDir = SettingsUtil . LocalUnityDataDir ;
BashUtil . RecreateDir ( localUnityDataDir ) ;
// copy MonoBleedingEdge
BashUtil . CopyDir ( $"{Directory.GetParent(editorIl2cppPath)}/MonoBleedingEdge" , $"{localUnityDataDir}/MonoBleedingEdge" , true ) ;
// copy il2cpp
BashUtil . CopyDir ( editorIl2cppPath , SettingsUtil . LocalIl2CppDir , true ) ;
// replace libil2cpp
string dstLibil2cppDir = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp" ;
BashUtil . CopyDir ( $"{libil2cppWithHybridclrSourceDir}" , dstLibil2cppDir , true ) ;
// clean Il2cppBuildCache
BashUtil . RemoveDir ( $"{SettingsUtil.ProjectDir}/Library/Il2cppBuildCache" , true ) ;
2025-06-12 18:00:06 +08:00
if ( version . major = = 2019 )
{
string curVersionStr = version . ToString ( ) ;
string srcIl2CppDll = GetUnityIl2CppDllModifiedPath ( curVersionStr ) ;
if ( File . Exists ( srcIl2CppDll ) )
{
string dstIl2CppDll = GetUnityIl2CppDllInstallLocation ( ) ;
File . Copy ( srcIl2CppDll , dstIl2CppDll , true ) ;
Debug . Log ( $"copy {srcIl2CppDll} => {dstIl2CppDll}" ) ;
}
else
{
throw new Exception ( $"the modified Unity.IL2CPP.dll of {curVersionStr} isn't found. please install hybridclr in 2019.4.40 first, then switch to your unity version" ) ;
}
}
2025-03-31 14:55:24 +08:00
if ( HasInstalledHybridCLR ( ) )
{
WriteLocalVersion ( ) ;
Debug . Log ( "Install Sucessfully" ) ;
}
else
{
Debug . LogError ( "Installation failed!" ) ;
}
}
}
}