Unity Editor v4.3.1f1 源码编译笔记

2018-05-21 18:00:00

0x00 前言

Unity 是“手游时代”的最热门的游戏开发引擎,很多团队使用 Unity 进行开发,市面上使用 Unity 开发的热门游戏更是数不胜数,甚至暴雪的《炉石传说》也使用 Unity 开发,可见 Unity 是一款很强大的引擎。然而对于一些求知欲旺盛的开发者,Unity 有一个缺点,就是它的核心部分是不开放源码的,现在国内一些大公司也陆续购买了 Unity 的源码进行二次开发,可惜我供职的公司还仅仅在计划购买,等到真的拿到代码还不知道要多久时间。一次偶然的机会在论坛上看到有人分享了一份 Unity 代码,虽然只是几年前的 4.x 版本,但也多少能窥出一点端倪,于是有了这篇笔记所记录的 Unity Editor v4.3.1f1 的编译过程。

0x01 源码获取

unity-source-4.3.1f1-e1ff3bcdcd18-pcmacandroidios.zip 【略】

0x02 准备工作

庆幸的是源码压缩包中还带有文档,通过简单的阅读得知,这个版本的源码建议使用 Windows 7 SP1 + Visual Studio 2010 SP1。我的电脑里已经装了 vs2015vs2017 了。不想把全家桶都装上,于是干脆 hyper-v 创建了一个虚拟机,干干净净,也免去了很多麻烦。步骤简单记录如下:

  • Hyper-V 全新安装 Windows (enwindows10consumereditionsversion1803updatedmarch2018x64dvd12063379.iso)
  • 安装 Visual Studio 2010 (envisualstudio2010ultimatex86dvd_509116.iso)
  • 安装 Visual Studio 2010 SP1 (VS10SP1-KB983509.exe)
  • 安装 Active Perl (ActivePerl-5.22.4.2205-MSWin32-x86-64int-403863.exe)
  • 解压源码包

有几个地方需要注意:

  • 最好安装英文系统,如果你使用的中文系统又不打算安装虚拟机,那么在编译之前修改 Projects\Jam\Editor.jam 文件,在 360 行插入 /wd4819 (当然你也可以用其他的方法解决4819错误,比如把报错的源文件另存为指定的 unicode 格式)
  • 一定要安装 Visual Studio 2010 SP1,否则编译 CPUInfo.cpp 时会报错,找不到 _xgetbv 方法
  • ~~最好安装 x86 版本的 Perl~~
  • 解压的时候注意最终路径名不要过长,否则下一步解压依赖库的时候会报错,如果是 X:\unity 最好

0x03 解压依赖库&生成vs工程文件

在解压出来的 Unity 源码路径下运行: perl build.pl --prepare

此时会在 Projects\JamGenerated\_workspace.vs2010_ 目录下生成 solution 文件,如果你仅仅只想更方便的阅读源码,那么到这一步就可以了 (Projects\Sublime 下面有 Sublime 的工程,习惯使用 Sublime 的可以直接打开)

0x04 处理许可管理相关代码

这一步其实理论上可以略过,因为 Unity 4.x 也还是可以运行的,证明激活服务器还在运行,但是我尝试过不修改任何管理 License 的代码,总是会弹出一个 ssl peer certificate or ssh remote key was not okwarning

最干净的办法是修改 Editor\Src\LicenseInfo.cpp,把所有用到 LicenseManger 的地方全部替换掉,代码如下:

// some include...

LicenseInfo* gSingleton = NULL;

LicenseInfo::LicenseInfo()
{
    InitializeProtection();
}

void LicenseInfo::InitializeProtection() 
{
    gSingleton = this;
    m_Tokens = 1; // make token 1 cause some places check if token is zero
}

void LicenseInfo::SetRXValue(const char* value) {}

void LicenseInfo::SignalUserClosedWindow() {}

void LicenseInfo::GenericErrorDialog(const char* title) {}

void LicenseInfo::UnknownErrorDialog(const char* title) {}

void LicenseInfo::DisplayErrorDialog(const char* title, const char* url) {}

void LicenseInfo::ReloadEditorUI () {}

void LicenseInfo::Tick() {}

void LicenseInfo::ShowUpdateFeedbackDialog(bool success) {}

bool LicenseInfo::GetLargeFlag(LicenseInfoFlag f) { return false; }

string LicenseInfo::GetLicenseString () { return "Cracked for study"; }

bool LicenseInfo::OngoingServerCommunication() { return true; }

string LicenseInfo::GetMachineID() { return ""; }

string LicenseInfo::GetAuthToken () { return ""; }

UInt64 LicenseInfo::GetRawFlags ()
{
    return m_Tokens;
}

void LicenseInfo::Reauthorize() {}

LicenseInfo::~LicenseInfo()
{
    if (gSingleton == this)
        gSingleton = NULL;
}

LicenseInfo* LicenseInfo::Get ()
{
    if(gSingleton == NULL)
        gSingleton = new LicenseInfo();
    return gSingleton;
}

void LicenseInfo::Cleanup ()
{
    delete gSingleton;
    gSingleton = NULL;
}

string LicenseInfo::GetDisplaySerialNumber () { return ""; }

int LicenseInfo::SaveLicenseFile ( string licenseData ) { return 0; }

void LicenseInfo::ReturnLicense () {}

bool LicenseInfo::IsLicenseUpdated () { return true; }

void LicenseInfo::CallActivationServer () {}

void LicenseInfo::CallActivationServer (string TX_RX_IDS) {}

void LicenseInfo::QueryLicenseUpdate (bool forceCheck) {}

void LicenseInfo::SignalServerError() {}

void LicenseInfo::NewActivation () {}

void LicenseInfo::ManualActivation () {}

const std::string LicenseInfo::GetLicenseURL() { return ""; }

bool LicenseInfo::UnknownStatus() { return false; }

void LicenseLog (const char* format, ...) {}

然后修改 Editor\Src\LicenseInfo.h,把 LicenseInfo::Flag 这个方法的实现修改如下: cpp static bool Flag (LicenseInfoFlag f) { return true; }

这样就会默认所有功能都是开启的,也不会检查本地是否有 license 文件了。

如同解题的方法永远不止一个,我们也有很多其他的方法来解决这个问题。比如在 WinMain 函数里把 LicenseInfo 的相关调用注释掉,但是因为还有很多其他地方使用 LicenseInfo,所以不如把 LicenseInfo 的实现改掉更快。也可以通过离线激活的方式,从 Unity 官网通过上传 Unity_v4.3.1f1.alf 再下载一个 Unity_v4.x.ulf 导入编辑器来激活。但是我在使用这个方式的时候遇到从 ulf 文件中读取 StopDate 时崩溃,需要修改 Editor\Src\LicenseManager.cpp1709 行为 std::string stopString = "2112-01-27T08:00:00";//GetDate("StopDate", doc); 来绕过。

0x05 编译

做了这么久的准备,总算可以编译代码了。其实这一步最简单!

Unity 源码路径下运行: perl build.pl build --target=WindowsEditor

当然你也可以在 target 中加入 WindowsStandalonePlayer 等其他的平台,记得用逗号隔开就好。

等待大约10分钟。

等待时间插播一个其他话题。Unity 的代码量其实并不大,Runtime 部分30w行代码,Editor 代码14w行。相对于 Unreal 4 的150+80w代码已经相形见绌。Lumberyard 的代码因为分布较散,不好统计,但印象中比 Unreal 更多,毕竟塞进来了整个 CryENGINE,而且 AzCore AzFramework LmbrCentral 也不小,更何况还有繁杂的各类 Gems

Unity: ``` λ cloc Runtime\ 1842 text files. 1841 unique files. 219 files ignored.

https://github.com/AlDanial/cloc v 1.66 T=6.72 s (258.9 files/s, 68280.3 lines/s)

Language files blank comment code

C++ 708 46572 16649 230438 C/C++ Header 904 21755 10788 110499 C# 105 2117 1365 12547 Assembly 10 545 646 1918 CSS 2 99 13 756 C 2 174 253 713 MSBuild script 4 0 21 375 HLSL 2 42 18 231 DOS Batch 1 26 0 56

Perl 1 6 0 42

SUM: 1739 71336 29753 357575

λ cloc Editor\Src\ 602 text files. 601 unique files. 33 files ignored.

https://github.com/AlDanial/cloc v 1.66 T=1.88 s (318.0 files/s, 103003.2 lines/s)

Language files blank comment code

C++ 304 24339 17044 128097

C/C++ Header 295 5252 2827 16445

SUM: 599 29591 19871 144542

Unreal 4

λ cloc Engine\Source\Runtime\ 7644 text files. 7640 unique files. 1108 files ignored.

https://github.com/AlDanial/cloc v 1.66 T=51.05 s (148.4 files/s, 43955.9 lines/s)

Language files blank comment code

C++ 3044 216871 110840 1103798 C/C++ Header 4366 144631 208261 452020 C# 160 850 316 5195 XML 2 58 10 331 JavaScript 1 58 64 261

Windows Resource File 2 37 46 129

SUM: 7575 362505 319537 1561734

λ cloc Engine\Source\Editor\ 4650 text files. 4648 unique files. 480 files ignored.

https://github.com/AlDanial/cloc v 1.66 T=31.38 s (148.0 files/s, 39353.6 lines/s)

Language files blank comment code

C++ 2038 143587 65619 752908 C/C++ Header 2498 57829 69772 136169 C# 105 951 812 7208

MSBuild script 2 0 14 109

SUM: 4643 202367 136217 896394

0x06 最后
---

编译之后的可执行文件在 `build\WindowsEditor\Unity.exe`
编译完就可以开心的断点看代码了。其实我在不能断点的情况下靠着强大的 `Resharper C++` 也阅读了一些代码,在这里也安利一下,如果还在用 `Visual Assist` 的可以考虑换这个工具,静态分析更加强大。

以后有时间再把看到的 `Unity` 中的一些代码总结到这里,给自己挖个坑,想填了再来填。(P.S. `Lumberyard` 的坑还没填的)