因为服务器使用java进行编程,而战斗核心逻辑使用C#实现。所以问题就是将二者融合,即java写的服务器调用C#代码进行战斗验证。
核心思路梳理
首先明确核心思路,即通过Java通过JNI调用C++的代码,再通过C++调用C#代码,以此完成Java调用C#代码的功能。
按照这个思路,JNI基本是固定的,而C++的具体实现有多种形式:
- 通过C++(CLI),调用C#代码。但是这一部分仅在Windows下编译,所以我们并未采用。
- 通过C++调用.Net Core,这种方式公司有采用,也已经相对稳定执行,不过我们在探索中并未使用该方案(此方案在中文互联网中的可参考资料不多)。
- 通过C++调用Mono,这种方案还是能在网上找到不少方案,且执行难度较低,Mono官方文档有提供内嵌的API。我们便是使用此方案,后续内容也以此为基础进行展开。
实现细节
此处对验证demo的实现细节进行记录,若内容太小白或有错误欢迎指正。
JAVA
Java调用C++代码,网上教程一大堆。
核心是定义native
方法之后,在java代码当中调用即可。
在定义native
方法之后,可以调用javac -h xxx
生成指定的.h
文件。后续对C++代码的开发便是依据这个头文件进行。
需要注意,此处的头文件中引用了jni.h
,该头文件位于jdk的bin目录下。
C++
JNI的类型转换等问题,此处不进行讲解,网上详细的教程挺多。
这里主要进行以下几点说明:
1. Mono的API
cpp
文件中需要引入mono的头文件:
#include <mono/jit/jit.h>
#include <mono/metadata/assembly>
#include <mono/metadata/debug-helpers.h>
Windows下这几个头文件位于mono安装目录中的include文件夹内。
linux下若从官网安装,则应当位于/usr/include/mono-2.0
路径内(CentOS 8通过官网安装后的结果,也可通过find
进行查找)。
2. MonoDomain的实例
通过调用mono_jit_init
方法,可以获取到一个MonoDomain
实例,后续操作都以此为基础执行。
但是要注意mono_jit_init
在运行时的生命周期中只能调用一次,否则报错:
You are registering the same counter address twice
。
3. Linux下的编译
C++代码在Linux下的编译主流以gcc与CMake为主。这里只记录一件糗事,就是我在编译动态库时使用的指令是gcc
,但是代码是C++,所以编译过程未报错但是就是无法正常执行,直到我把指令改成了g++
(吐槽一下自己的半吊子:P)。
Mono
Mono作为社区力量,只在此放上相关链接,好做记录并指引后来者。
文档和API已经说明的很详细了(只是当时为了找到文档和API,在某度中生生搜了半天也没结果)。
CSharp
C#代码则可以正常编写,无论以static实现,或以类实例形式,都可以在C++中调用mono相关方法实现调用。
因为我们的核心需求是Java调用C#,所以C++代码更像是起到了桥的作用。
所以我们在C#中仅定义3-5个静态方法作为Bridge,使用他们封装我们的核心逻辑。
坑(风险)
因为只是技术验证的记录,所以当前所能遇到的坑无非是一些很小的、或是因为不熟悉而导致的一些编译问题。
如果按照这套方案使用到生产环境当中,那么关于DLL之间的相互引用与加载、Java多线程调用、以及C#代码实际运行时可能出现的各种未知异常都会是需要面临的风险点(俗称“坑”)。
所以此处待补充,也欢迎有经验的小伙伴可以分享。
网友评论