我还记得第一天来到乐趣,我的上司也就是主程就分配了我一个任务,时间给我安排的是2天,由于他不是很了解我的水平,估计是想用这个试试我吧! 不过我也不是什么水货,当时我预估了一下感觉这个东西最多也就半天可以搞定吧! 于是我就开始撸代码了………….
他给我的任务是要把服务器端的C#代码导出slua代码,目的是为了在服务器端使用lua代码.
于是我就开始看slua的源码,由于我之前没有接触过slua,只看过ulua的代码,而且没有使用过slua进行热更新过.但是我发现看了以后slua的导出和ulua的代码导出基本上是差不多的.原理也差不多只是风格可能有所不同,听网上说是ulua的性能比slua要好,不知道为什么公司要用slua.
既然他要我导出,那我肯定要从unity3D里面的slua导出里面开始看,其实slua的代码导出主要是在 "LuaCodeGen.cs" 这个C#文件.也就是说 基本把这文件上得代码或者类看懂就行了.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
public
class
LuaCodeGen : MonoBehaviour
//这个类是继承自MonoBehaviour
{
static
public
string
GenPath = SLuaSetting.Instance.UnityEngineGeneratePath;
public
delegate
void
ExportGenericDelegate(Type t,
string
ns);
static
bool
autoRefresh =
true
;
static
bool
IsCompiling {
get
{
if
(EditorApplication.isCompiling) {
Debug.Log(
"Unity Editor is compiling, please wait."
);
}
return
EditorApplication.isCompiling;
}
}
[InitializeOnLoad]
public
class
Startup
{
static
bool
isPlaying=
false
;
static
Startup()
{
EditorApplication.update += Update;
// use this delegation to ensure dispose luavm at last
EditorApplication.playmodeStateChanged+=()=>{
if
(isPlaying==
true
&& EditorApplication.isPlaying==
false
) {
if
(LuaState.main!=
null
) LuaState.main.Dispose();
}
isPlaying=EditorApplication.isPlaying;
};
}
static
void
Update(){
EditorApplication.update -= Update;
Lua3rdMeta.Instance.ReBuildTypes();
// Remind user to generate lua interface code
var
remindGenerate = !EditorPrefs.HasKey(
"SLUA_REMIND_GENERTE_LUA_INTERFACE"
) ||
EditorPrefs.GetBool(
"SLUA_REMIND_GENERTE_LUA_INTERFACE"
);
bool
ok = System.IO.Directory.Exists(GenPath+
"Unity"
);
if
(!ok && remindGenerate)
{
if
(EditorUtility.DisplayDialog(
"Slua"
, "Not found lua
interface
for
Unity, generate it now?
", "
Generate
", "
No"))
{
GenerateAll();
}
else
{
if
(!EditorUtility.DisplayDialog(
"Slua"
, "Remind you next time
when no lua
interface
found
for
Unity?
", "
OK",
"Don't remind me next time!"
))
{
EditorPrefs.SetBool(
"SLUA_REMIND_GENERTE_LUA_INTERFACE"
,
false
);
}
else
{
EditorPrefs.SetBool(
"SLUA_REMIND_GENERTE_LUA_INTERFACE"
,
true
);
}
}
}
}
}
[MenuItem(
"SLua/All/Make"
)]
static
public
void
GenerateAll()
//这里是 导出的入口函数
{
autoRefresh =
false
;
Generate();
GenerateUI();
Custom();
Generate3rdDll();
autoRefresh =
true
;
AssetDatabase.Refresh();
}
|
可能看到 在U3D中的导出SLUA的菜单按钮中是执行了 GenerateAll() 这个函数,这个函数的意思是导出全部的U3D的SLUA代码
这个函数里面有执行了
1
2
3
4
5
6
|
Generate();
GenerateUI();
Custom();
Generate3rdDll();
autoRefresh =
true
;
AssetDatabase.Refresh();
|
这个几个函数,其中Generate();这个函数是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
static
public
void
Generate()
{
if
(IsCompiling) {
return
;
}
Assembly assembly = Assembly.Load(
"UnityEngine"
);
Type[] types = assembly.GetExportedTypes();
List<
string
> uselist;
List<
string
> noUseList;
CustomExport.OnGetNoUseList(
out
noUseList);
CustomExport.OnGetUseList(
out
uselist);
// Get use and nouse list from custom export.
object
[] aCustomExport =
new
object
[1];
InvokeEditorMethod<ICustomExportPost>(
"OnGetUseList"
,
ref
aCustomExport);
if
(
null
!= aCustomExport[0])
{
if
(
null
!= uselist)
{
uselist.AddRange((List<
string
>)aCustomExport[0]);
}
else
{
uselist = (List<
string
>)aCustomExport[0];
}
}
aCustomExport[0] =
null
;
InvokeEditorMethod<ICustomExportPost>(
"OnGetNoUseList"
,
ref
aCustomExport);
if
(
null
!= aCustomExport[0])
{
if
((
null
!= noUseList))
{
noUseList.AddRange((List<
string
>)aCustomExport[0]);
}
else
{
noUseList = (List<
string
>)aCustomExport[0];
}
}
List<Type> exports =
new
List<Type>();
string
path = GenPath +
"Unity/"
;
foreach
(Type t
in
types)
{
if
(filterType(t, noUseList, uselist) && Generate(t, path))
exports.Add(t);
}
GenerateBind(exports,
"BindUnity"
, 0, path);
if
(autoRefresh)
AssetDatabase.Refresh();
Debug.Log(
"Generate engine interface finished"
);
}
|
可以看到这个函数有这么一句, 也就是说他是利用反射把UnityEngine.dll 里面的函数全部导出来了.
1
|
Assembly assembly = Assembly.Load(
"UnityEngine"
);
|
1
|
GenerateUI();
//则是把unity里面的UI函数接口导出来了.
|
1
2
|
Custom();
//则是把客户端用户标记的C#代码导出来
Generate3rdDll();
//则是把3rdDll导出来;
|
而 Generate(),GenerateUI();Generate3rdDll(); 这几个函数在服务器端是不需要的,因为服务器端是没有unity3D函数的.
所以只要修改Custom();函数就可以了,然后在需要导出的类前面加上导出的标记.就可以导出服务器端的代码.
具体修改如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
static
public
void
Custom()
{
List<Type> exports =
new
List<Type>();
string
path = GenPath +
"/Server/Server/LuaExport/"
;
//修改导出路径 这个可以自行定义
if
(!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
ExportGenericDelegate fun = (Type t,
string
ns) =>
{
if
(Generate(t, ns, path))
{
exports.Add(t);
Debug.LogFormat(
"{0} exported..."
, t.FullName);
}
};
Assembly assembly;
Type[] types;
// export self-dll
assembly = Assembly.GetExecutingAssembly();
//这句是最重要的,它的意思是获取当前正在运行的程序的函数然后导出来,也就是导出自身DLL代码
types = assembly.GetExportedTypes();
foreach
(Type t
in
types)
{
if
(t.IsDefined(
typeof
(CustomLuaClassAttribute),
false
))
{
fun(t,
null
);
}
}
CustomExport.OnAddCustomClass(fun);
//添加导出类
GenerateBind(exports,
"BindCustom"
, 3, path);
//绑定
}
|
最后把有关于U3D的代码去掉和多余的代码去掉就可以了.在需要导出的类上面添加 [SLua.CustomLuaClass] 特性
如
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
using
System.Collections;
using
UnityEngine;
[SLua.CustomLuaClass]
//添加标记 代表这个类会被导出
public
partial
class
DieController : UnitBaseComponent
{
public
void
DoDie(
int
attackerIdx,
int
attackType, Damage.CreatureHit hit)
{
DoDieImpl(attackerIdx, attackType, hit);
var
writer =
new
CombatProtocolWriter(sceneObject_, CombatProtocolEnum.S2C_SyncDie);
writer.Write(attackerIdx);
writer.Write(attackType);
#region 附带被击效果
if
(hit !=
null
)
{
writer.Write(
true
);
writer.Write(hit.attackPosition);
writer.Write(hit.creatureHitID);
writer.Write(hit.attackerObjID);
}
else
{
writer.Write(
false
);
}
#endregion
room_.Broadcast(writer);
}
|
然后再服务器端的Main函数里 添加一个导出命令参数就可以导出相应的类了
1
2
3
4
5
6
7
8
|
if
(args.Length >= 1 &&
"lua_export"
== args[0])
{
Debug.Log(
"Running in lua export mode..."
);
LuaCodeGen.GenerateAll();
//调用导出函数
AddNewLuaExport();
//给VS项目添加新生成的Slua导出文件
return
;
}
|
如添加一个 lua_export 命令, 在VS里面设置一下
启动程序后就可以导出了
另外还有一个小工具是,当你新生成的SLUA代码是不会自动添加到VS 的C#项目里面的还需要修改项目里的配置文件才会自动添加进去 所以我有做了一个小工具可以把新生成的 C#文件添加到项目中.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
private
static
void
AddNewLuaExport()
//也就是生成后需要执行的函数
{
List<
string
> fileList =
new
List<
string
>();
DirectoryInfo folder =
new
DirectoryInfo(
"../../Server/Server/LuaExport"
);
foreach
(FileInfo NextFile
in
folder.GetFiles())
{
fileList.Add(
"Server\\LuaExport\\"
+ NextFile.Name);
}
ModifyCsproj(
"../../Server/Server.csproj"
, fileList);
}
private
static
void
ModifyCsproj(
string
xmlFilePath, List<
string
> csFileList)
{
XmlDocument xmlDoc =
new
XmlDocument();
xmlDoc.Load(xmlFilePath);
XmlNodeList root_childlist = xmlDoc.ChildNodes;
XmlNode root_Project =
null
;
foreach
(XmlNode xn
in
root_childlist)
{
if
(xn.Name ==
"Project"
)
{
root_Project = xn;
break
;
}
}
XmlNodeList childlist_Project = root_Project.ChildNodes;
XmlNodeList childlist_Item =
null
;
XmlNode node_Compile =
null
;
List<XmlNode> nodes =
new
List<XmlNode>();
foreach
(XmlNode xn
in
childlist_Project)
{
if
(xn.Name ==
"ItemGroup"
)
{
if
(xn.FirstChild.Name ==
"Compile"
)
{
node_Compile = xn;
childlist_Item = node_Compile.ChildNodes;
break
;
}
}
}
foreach
(XmlElement x
in
childlist_Item)
{
if
(x.GetAttribute(
"Include"
).Contains(
"LuaExport"
))
{
nodes.Add(x);
}
}
for
(
int
i = 0; i < nodes.Count; i++)
{
node_Compile.RemoveChild(nodes[i]);
}
foreach
(
string
fileName
in
csFileList)
{
XmlElement xe_Compile = xmlDoc.CreateElement(
"Compile"
,
xmlDoc.DocumentElement.NamespaceURI);
xe_Compile.SetAttribute(
"Include"
, fileName);
node_Compile.AppendChild(xe_Compile);
}
xmlDoc.Save(xmlFilePath);
}
|
最后的福利就是把完整的导出代码也共享出来 哈哈
百度网盘:http://pan.baidu.com/s/1hrD58XY