自定义mono,实现unityandroid平台代码更新(代码片段)

_Captain _Captain     2022-12-06     609

关键词:

原理

在做这个之前,需要了解:

1、Unity是基于Mono的,我们写的代码都被编译成DLL,然后由Mono读取这个DLL并解析运行。

2、在Mono眼里,DLL和普通的资源文件没什么区别。


去年做过了修改Mono 来实现加密DLL,防破解。

Unity3d 加密 Assembly-CSharp.dll (Android平台) 全记录


现在项目有代码热更的需求,而去年的虚拟机被删除了。就重新走了一遍流程。

参考了以下文章:

http://blog.sina.com.cn/s/blog_9e5d42ee0102vvtg.html


转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

在 Android 中,由 libmono.so 来加载 Assembly-CSharp.dll 。

libmono.so  这就是 Mono 了 。



Unity3d 是基于 Mono2.0 的,而 Mono2.0是免费开源的。所以基于各种开源协议 ,Unity 官方也将自己修改过的 Mono 开源出来,我们下载过来然后修改 重新编译出自己的 libmono.so 。

项目托管在 Github 上,项目地址:

https://github.com/Unity-Technologies/mono

我们项目用的是Unity5.3.2,所以下载mono的分支

https://github.com/Unity-Technologies/mono/tree/unity-5.3


了解到一些原理背景后就可以开始进行操作了。

1、安装ubuntu系统


Ubuntu 系统下载:

http://www.ubuntu.org.cn/download/desktop

    http://old-releases.ubuntu.com/releases/14.04.1/ubuntu-14.04.1-desktop-i386.iso?_ga=1.187436840.1241524278.1457318071  


如果你对Ubuntu不是很熟悉的话,我建议重新创建一个新的虚拟机。

我这里安装的是32位的Ubuntu14.


安装好Ubuntu之后,首先要设置一下软件源为国内的镜像站,这样安装软件会很快。

在左边的快速启动面板中点击“软件中心”按钮,

再把鼠标移到最上边出现菜单栏,点“编辑-软件源”

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

在出来的软件源面板下边,点击“下拉列表,选择“其他站点”;


在出来的服务器列表中,选择搜狐、163 或 cn99 的站点都可以,然后点右下角的“选择服务器”按钮返回;



2、下载ANDROID_NDK

安装完 Ubuntu 后,在 Ubuntu 中   ,注意32 和64位区别

64 位下载 :

http://pan.baidu.com/s/1dDAqnK1

32位下载  :

http://pan.baidu.com/s/1sjoneRr

sudo su 切换到root安装

./android-ndk-r10e-linux-x86.bin

安装后在安装目录里面找到 RELEASE.txt ,里面记录着NDK 完整版本号,修改为 r10e

(Mono的编译脚本是读取这个RELEASE.txt中记录的版本号,然后和编译脚本中填写的版本号做匹配的,如果不匹配就会去Google下载)


设置环境变量 ANDROID_NDK_ROOT

sudo gedit /etc/bashrc

添加一行

export ANDROID_NDK_ROOT=/home/captain/Downloads/android-ndk-r10e;

让环境变量立即生效

source /etc/bashrc

测试是否添加成功

echo $ANDROID_NDK_ROOT 


3、编译 Development 版本的 libmono.so

1、从 Github 下载 unity-mono,我这里下载的5.3版本

https://github.com/Unity-Technologies/mono/tree/unity-5.3



如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

https://github.com/ThisisGame/Unity5.3_Android_DLL_HotFix


2、下载好之后解压,然后拷贝编译脚本到mono根目录

cp external/buildscripts/build_runtime_android.sh ./


3、运行编译脚本,提示没有安装git,安装git

sudo apt-get install autoconf

4、修改脚本build_runtime_android.sh

sudo gedit build_runtime_android.sh

搜索KRAIT_PATCH_PATH 修改

KRAIT_PATCH_PATH="$CWD/android_krait_signal_handler/build"

搜索(cd "$KRAIT_PATCH_PATH" &&修改

(cd "$KRAIT_PATCH_PATH" && perl ./build.pl)

5、android_krait_signal_handler/build.pl 删除第一行注释

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

6、安装其它的依赖库

* autoconf  
* automake  
* bison  
* gcc  
* gettext  
* glib >= 2.0  
* libtool  
* make  
* perl

7、再次执行脚本,编译成功。


这个so 就是mono的库,我们可以用它替换掉unity自带的。


4、修改mono源码,读取下载的新版本DLL

找到 /metadata/image.c 这个文件

找到

mono_image_open_from_data_with_name

修改如下:

static FILE* OpenFileWithPath( const char* path )



    const char* fileMode ="rb";

    return fopen(path, fileMode );




static char* ReadStringFromFile(const char* pathName,int* size)



    FILE* file = OpenFileWithPath( pathName);

    if (file == NULL)

        return 0;

    

    fseek(file, 0, SEEK_END);

    

    int length = ftell(file);

    fseek(file, 0, SEEK_SET);

    if (length < 0)

    

        fclose( file );

        return 0;

    

    *size = length;

    char* outData = g_try_malloc (length);

    int readLength = fread(outData, 1, length, file);

    

    fclose(file);

    

    if (readLength != length)

    

        //if(readLength == length)

        //    JNI_OnLoad(0,0);

        //    JNI_OnUnload(0,0);

        //

        g_free (outData);

        return 0;

    

    

    return outData;



MonoImage *
mono_image_open_from_data_with_name (char *data, guint32 data_len, gboolean need_copy, MonoImageOpenStatus *status, gboolean refonly, const char *name)

	//修改开始1
	int datasize=0;
	if(strstr(name,"Assembly-CSharp.dll"))
	
		//重新计算路径
		const char* _pack = strstr(name,"com.");
		const char* _pfie = strstr(name,"-");
		char _name[512];
		memset(_name,0,512);
		int _len0 = (int)(_pfie - _pack);
		memcpy(_name , "/data/data/",11);
		memcpy(_name + 11, _pack,_len0);
		memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp.dll",26);
		g_message("momo: path = %s\\n", _name);
		char* bytes = ReadStringFromFile (_name,&datasize);
		if(datasize > 0)
		
			data = bytes;
			data_len = datasize;
		
	
	if(strstr(name,"Assembly-CSharp-firstpass.dll"))
	
		//重新计算路径

             	const char* _pack = strstr(name,"com.");

        	const char* _pfie = strstr(name,"-");

        	char _name[512];

        	memset(_name,0,512);

        	int _len0 = (int)(_pfie - _pack);

        	memcpy(_name , "/data/data/",11);

        	memcpy(_name + 11, _pack,_len0);

        	memcpy(_name + 11 + _len0 ,"/files/Assembly-CSharp-firstpass.dll",36);

        

		g_message("momo: path = %s\\n", _name);

        

		char* bytes = ReadStringFromFile (_name,&datasize);

		if(datasize > 0)
		

			data = bytes;

			data_len = datasize;

		
	
	//修改结束1
	
	MonoCLIImageInfo *iinfo;
	MonoImage *image;
	char *datac;

	if (!data || !data_len) 
		if (status)
			*status = MONO_IMAGE_IMAGE_INVALID;
		return NULL;
	
	datac = data;
	if (need_copy) 
		datac = g_try_malloc (data_len);
		if (!datac) 
			if (status)
				*status = MONO_IMAGE_ERROR_ERRNO;
			return NULL;
		
		memcpy (datac, data, data_len);
	

 	//修改开始2

	if(datasize > 0 && data != 0)
	

		g_free (data); 

	
	//修改结束2

	image = g_new0 (MonoImage, 1);
	image->raw_data = datac;
	image->raw_data_len = data_len;
	image->raw_data_allocated = need_copy;
	image->name = (name == NULL) ? g_strdup_printf ("data-%p", datac) : g_strdup(name);
	iinfo = g_new0 (MonoCLIImageInfo, 1);
	image->image_info = iinfo;
	image->ref_only = refonly;
	image->ref_count = 1;

	image = do_mono_image_load (image, status, TRUE, TRUE);
	if (image == NULL)
		return NULL;

	return register_image (image);

修改的效果是,mono在读取完默认位置的dll之后,我们添加的代码会判断有没有下载的新版本的dll位于 /data/data/xx/file 目录,如果有,就读取新版本的dll替换掉默认位置的dll。


保存后,重新编译so文件。


5、编译Release版本

修改所有的 build_runtime_android.sh

找到 -fpic -g 去掉 -g

这样再编译就得到Release版本的so文件

转自http://blog.csdn.net/huutu http://www.liveslives.com http://www.thisisgame.com.cn

6、导出工程,可以用脚本自动替换so文件。

/** 
 * 文件名:BuildPostprocessor.cs 
 * Des:在导出Eclipse工程之后对替换mono.so
 * Author:Captain 
 * **/


using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using System.IO;

public class BuildPostprocessor

    [PostProcessBuildAttribute(1)]
    public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
    
        if (target == BuildTarget.Android && (!pathToBuiltProject.EndsWith(".apk")))
        
            Debug.Log("target: " + target.ToString());
            Debug.Log("pathToBuiltProject: " + pathToBuiltProject);
            Debug.Log("productName: " + PlayerSettings.productName);

            Debug.Log("Current is : " + EditorUserBuildSettings.development.ToString());

            //替换 libmono.so;  
            if (EditorUserBuildSettings.development)
            
                string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";
                File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/armeabi-v7a/libmono.so", armv7a_so_path, true);

                string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";
                File.Copy(Application.dataPath + "/HotFix/Editor/libs/development/x86/libmono.so", x86_so_path, true);
            
            else
            
                string armv7a_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/armeabi-v7a/libmono.so";
                File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/armeabi-v7a/libmono.so", armv7a_so_path, true);

                string x86_so_path = pathToBuiltProject + "/" + PlayerSettings.productName + "/" + "libs/x86/libmono.so";
                File.Copy(Application.dataPath + "/HotFix/Editor/libs/release/x86/libmono.so", x86_so_path, true);
            

            Debug.Log("HotFix libmono.so Success !!");
        
    


如果你也是Unity5.3版本,那么可以直接Clone我编译好的Mono。

https://github.com/ThisisGame/Unity5.3_Android_DLL_HotFix

mono的aot实现

...-il2cpp.htmlJIT方式:Unity的跨平台技术是通过一个Mono虚拟机实现的。而这个虚拟机更新太慢,不能很好地适应众多的平台。 AOT方式:unity公司就自行研发了IL2cpp,把本来应该再mono的虚拟机上跑的中间代码转换成cpp代码,这样再... 查看详情

为自定义平台实现 REST sdk

】为自定义平台实现RESTsdk【英文标题】:implementingRESTsdkforcustomplatform【发布时间】:2016-08-2003:05:47【问题描述】:我正在为一个新平台实现paypalREST集成我被卡住了,希望能得到任何帮助我得到的有趣问题是我从文档页面复制的R... 查看详情

C# mono 进程间、应用程序间跨平台消息传递实现。 (如何)

】C#mono进程间、应用程序间跨平台消息传递实现。(如何)【英文标题】:C#monointerprocess,interapplicationcrossplatformmessagingimplementation.(Howto)【发布时间】:2013-09-2703:49:27【问题描述】:我正在开发应用程序和c#,目前,我只从事Window... 查看详情

自定义tempdata跨平台思路

一:TempData的自定义实现。。。TempData是用Session实现的,既然是Session,那模式是线程方式。。。这样的Session是没法进行跨平台的。。。那么这就涉及到如何在多台机器上部署、存储...关系型数据库:sqlserver/mysqlnosql:mongodb,redis... 查看详情

java微信公众平台开发--微信自定义菜单的创建实现(代码片段)

  自定义菜单这个功能在我们普通的编辑模式下是可以直接在后台编辑的,但是一旦我们进入开发模式之后我们的自定义菜单就需要自己用代码实现,所以对于刚开始接触的人来说可能存在一定的疑惑,这里我说下平时我们在... 查看详情

hfun.快速开发平台=》自定义列表实例

  应用系统中数据列表的展现是开发内容之一,实现的方式基本是通过编号具体的访问列表页实现,通过检索条件进行数据源的获取,列字段的描述,还可能会有检索条件的实现,列表数据的导出等功能。  为了将重复工作... 查看详情

在 Mono 上使用自定义 SSL 客户端证书 System.Net.HttpClient

】在Mono上使用自定义SSL客户端证书System.Net.HttpClient【英文标题】:UsingcustomSSLclientcertificatesSystem.Net.HttpClientonMono【发布时间】:2014-08-2522:04:17【问题描述】:我正在使用来自NuGet的MicrosoftHTTPClientLibraries,我基本上是在尝试使用X50... 查看详情

快速开发平台之公文签章自定义报表的实现

...最近做了一个电子政务的项目,其中用到了公文签章和自定义报表,这两个功能很常用,我就收集到自己的开发框架中了,同时也把实现方法分享给大家。  http://www.learun.cn:8090 在线demo公文签章公文签章是从网上找的... 查看详情

远程代答平台如何实现打码盈利?

远程代答平台的作用: 帮助软件开发作者进行验证码的识别解答。 例如: 研发软件:批量点赞,批量下载,批量发送软件。 面临问题:ip受限,验证码验证 解决方案:  用户自己输入(用户在使用过... 查看详情

微信公众平台开发教程---自定义菜单

...概述:如果只有输入框,可能太简单,感觉像命令行。自定义菜单,给我们提供了很大的灵活性,更符合用户的操作习惯。在一个小小的微信对话页面,可以实现更多的功能。菜单直观明了,不仅能提供事件响应,还支持URL跳转... 查看详情

qtlinux平台屏蔽按键事件(installeventfilter使用)自定义拦截按键输入(代码片段)

文章目录背景焦点focusfocusPolicykeypress和keyreleaseinstallEventFilter重写事件过滤器过滤组件的按键事件最终效果Qt是一个跨平台开发的框架,可以实现一套代码多平台编译运行。但是有时候我们想实现的功能却和平台深度挂钩,... 查看详情

xamarin.forms自定义用户界面控件实现一个hybridwebview(混合webview)

...lementingaHybridWebView呈现一个特定于平台的视图Xamarin.Forms自定义用户界面控件应该来自视图类(Viewclass),用于在屏幕上放置布局和控件。本文演示了如何为HybridWebView(混合webview)自定义控件创建自定义渲染器,该控件演示了如... 查看详情

[qt]自定义qstyle——实现qprogressbar自定义样式(代码片段)

[Qt]自定义QStyle——实现QProgressBar自定义样式实现效果预览前言? 我们都知道Qt作为一个跨平台的桌面程序开发框架,其对样式的匹配度非常的友好。正因为如此,使用自定义style开发出自己觉得看起来比较舒服的样式对开发应用... 查看详情

跨平台应用开发进阶(四十)自定义插件及引用(代码片段)

...用示例四、拓展阅读一、前言正如将可复用功能封装为自定义组件以供他人使用一样,在uni-app开发框架中提供了另一种形式的自定义插件,并可将该插件提交至uni-app插件市场。二、插件制作制作插件前,首先要清楚... 查看详情

跨平台应用开发进阶(四十)自定义插件及引用(代码片段)

...用示例四、拓展阅读一、前言正如将可复用功能封装为自定义组件以供他人使用一样,在uni-app开发框架中提供了另一种形式的自定义插件,并可将该插件提交至uni-app插件市场。二、插件制作制作插件前,首先要清楚... 查看详情

oauth2自定义granter与provider实现自定义身份认证(代码片段)

Oauth2自定义Granter与Provider实现自定义身份验证需求描述实现思路自定义Token自定义Granter自定义Provider配置Provider修改Client_details表测试需求描述公司的软件开发平台基于Oauth2实现身份认证,但今年某地区用户提出特殊需求——... 查看详情

security基础:部署zabbix监控平台配置及使用zabbix监控系统自定义zabbix监控项目实现zabbix报警功能

一、部署Zabbix监控平台目标:本案例要求部署一台Zabbix监控服务器,一台被监控主机,为进一步执行具体的监控任务做准备:   在监控服务器上安装LAMP环境   修改PHP配置文件,满足Zabbix需求   ... 查看详情

是在 Mono 跨平台开发吗?

】是在Mono跨平台开发吗?【英文标题】:IsdevelopinginMonocross-platform?【发布时间】:2010-08-0706:47:35【问题描述】:单声道跨平台开发的标准是什么?如何为Windows编译(在Linux中),如何在Linux中运行(因为没有.NETJIT编译器)?那... 查看详情