├── .gitignore
├── LICENSE
├── README.en.md
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── hjc
│ │ ├── lua
│ │ ├── LuaBattleConst.java
│ │ ├── LuaBattleManager.java
│ │ ├── LuaBattlePlatform.java
│ │ ├── LuaInit.java
│ │ ├── annotation
│ │ │ ├── LuaParam.java
│ │ │ ├── LuaServerLib.java
│ │ │ ├── LuaServerLibFunc.java
│ │ │ └── LuaServerModel.java
│ │ ├── compiler
│ │ │ ├── LuaCompiler.java
│ │ │ └── luajc.java
│ │ ├── convertor
│ │ │ ├── LuaJavaBeanConvertor.java
│ │ │ ├── LuaProtobufConvertor.java
│ │ │ ├── LuaServerFileConverter.java
│ │ │ ├── LuaServerLibFileConverter.java
│ │ │ └── LuaServerModelFileConverter.java
│ │ ├── engine
│ │ │ ├── ILuaJEngine.java
│ │ │ ├── LuaJCmdEnum.java
│ │ │ └── LuaJEngine.java
│ │ ├── exception
│ │ │ └── LuaException.java
│ │ └── log
│ │ │ ├── LuaLogLevel.java
│ │ │ ├── LuaLogTool.java
│ │ │ └── LuaTracebackException.java
│ │ └── util
│ │ ├── Assert.java
│ │ ├── FileUtil.java
│ │ ├── NamedThreadFactory.java
│ │ ├── ScanUtil.java
│ │ ├── StringUtil.java
│ │ └── enumutil
│ │ ├── EnumUtil.java
│ │ └── IndexedEnum.java
└── resources
│ ├── ServerLib.template
│ ├── ServerLibLuaFile.template
│ ├── ServerModelLuaFile.template
│ └── log4j2.xml
└── test
├── java
└── com
│ └── hjc
│ └── demo
│ ├── convert
│ ├── ConvertLibLuaFileTest.java
│ ├── ConvertModelLuaFileTest.java
│ ├── README.md
│ ├── fall
│ │ └── FallDictData.java
│ ├── lib
│ │ └── LuaBattleFunction.java
│ └── skill
│ │ ├── SkillBaseNode.java
│ │ ├── SkillConfigData.java
│ │ ├── SkillNodeExitData.java
│ │ └── SkillNodeParam.java
│ └── service
│ └── BattleDemoService.java
├── lua
├── FightCoreLua.lua
├── FightManager.lua
├── config
│ ├── fall
│ │ └── FallDictData.lua
│ └── skill
│ │ ├── SkillBaseNode.lua
│ │ ├── SkillConfigData.lua
│ │ ├── SkillNodeExitData.lua
│ │ └── SkillNodeParam.lua
├── interface
│ ├── Command.lua
│ └── Looper.lua
├── lib
│ ├── ServerLib.lua
│ ├── ServerLogTool.lua
│ ├── ServerLuaBattle.lua
│ └── class.lua
└── logic
│ ├── BattleRoom.lua
│ └── UnitManager.lua
└── resources
├── ServerLib.template
├── ServerLibLuaFile.template
├── ServerModelLuaFile.template
└── log4j2.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea/
2 | /target/classes/
3 | /target/
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.en.md:
--------------------------------------------------------------------------------
1 | # Java-lua battle framework
2 |
3 | ![Powered][1]
4 |
5 | [1]: https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/lua/lua.png
6 |
7 | [中文文档](https://github.com/hjcenry/lua-java-battle/blob/master/README.md)
8 |
9 | > **Java based on LuaJ uses Lua's battle framework**
10 |
11 | The framework is based on the secondary encapsulation implementation of LuaJ(https://luaj.sourceforge.net)
12 |
13 | # It provides the following functions
14 |
15 | - Call encapsulation of LuAj's base interface
16 | - Simplify the setup of the LuaJ environment
17 | - Manages Lua battles and provides interfaces
18 | - Lua object-oriented usage scheme(class.lua)
19 | - [Example of Lua combat framework](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/service/BattleDemoService.java)
20 | - Luaj's Guide to stomping pits
21 |
22 | This framework provides a Java-Lua battle framework with the following advantages and disadvantages
23 |
24 | - **Advantages**:
25 |
26 | 1. Common logic LUA code: The front and rear end can use the same set of combat logic code based on the same set of language, just need to design the common war framework, can realize one piece of code used in two places. The front and back end programmers can also develop together based on this framework, which can improve the development efficiency to a certain extent for both state synchronization and frame synchronization.
27 | 2. The LuaJ framework is by far the most efficient way to invoke Lua in Java.
28 | 3. Lua code can be used directly without compilation, and you can design a set of hot logic through LuaJ
29 |
30 | - **Disadvantages**(**Guidelines on pit**):
31 |
32 | 1. More heap and meta space for the JVM
33 |
34 | Luaj provides two compilers, LuaC and LuaJC. Luac will create a LuaClosure object after the LOAD file, and it will parse the lua command line by line as it runs, but it won't run very efficiently.
35 | The principle of LuaJc is to compile it into Java bytecode and load it into memory through its JavaLoader (inheriting ClassLoader), which is equivalent to compiling and running it many times.
36 | However, those familiar with the Java class loading mechanism should know that during this process, the JVM creates Klass information in the Meta space and stores the Klass reference in the ClassLoader. Meanwhile, Luaj's JavaLoader also does one thing: cache dynamically generated bytecode byte[]
37 | In this case, starting a Lua environment increases the heap and meta footprint of the JVM.
38 |
39 | 2. Not as efficient as native Java
40 |
41 | The authors of LuAJ describe that LuAJ is almost as efficient or even more efficient than native Lua virtual machines. But in my actual tests, I didn't compare Luaj to native Lua, I compared Luaj to native Java, and the performance was far worse.
42 | By observing the compiled source code of luaj, we can also find that an operation like i++, for example, can be directly operated on the basic data type int in Java, but in luaj, int is wrapped by the Integer wrapper class (LuaInteger).
43 |
44 | - Both a strength and a weakness
45 | 1. Flexible Lua code
46 |
47 | Flexibility is a double-edged sword. If used well, it can greatly improve development efficiency. If not used well, it can not only improve development efficiency, but also bring great pain to development and maintenance, which is very important for the ability of low-level development.
48 |
49 | > **From what has been discussed above**:Whether you want to use Lua as your Java server's combat logic code depends on the situation, whether the advantages of Lua give you great benefits of your code and you can live with its disadvantages or have other solutions to overcome its disadvantages.
50 |
51 | `Welcome to use, any bugs and optimization requirements, welcome to discuss issues`
52 |
53 | # Java Doc
54 |
55 | https://hjcenry.com/lua-java-battle/doc/
56 |
57 | # Quick Start
58 |
59 | See BattleDemoService for a complete code example
60 |
61 | # Maven repositories
62 |
63 | ```xml
64 |
65 | io.github.hjcenry
66 | lua-java-battle
67 | 1.0
68 |
69 | ```
70 |
71 | ## 1. Specify the Lua parameter
72 |
73 | ```java
74 | LuaInit.LuaInitBuilder luaInit=LuaInit.builder();
75 | luaInit.preScript("print('Hello Lua Battle!!!')");
76 | // Set the Lua root path
77 | luaInit.luaRootPath("F:\\project\\lua-java-battle\\src\\main\\lua\\");
78 | // Load the Lua call interface directory
79 | luaInit.luaLoadDirectories("interface");
80 | // Load the Lua main file
81 | luaInit.luaLoadFiles("FightManager.lua");
82 | // Show log
83 | luaInit.showLog(true);
84 | ```
85 |
86 | ## 2. Initialize the Lua environment
87 |
88 | ```java
89 | LuaBattleManager.getInstance().init(luaInit.build());
90 | ```
91 |
92 | ## 3. Initialize and cache Lua methods called by Java
93 |
94 | ```java
95 | // All the methods you need to start with
96 | this.xxxFunction=this.initFunction("XXX.xxx");
97 | this.xxxFunction2=this.initFunction("xxx");
98 | ```
99 |
100 | ## 4. Call Lua methods
101 |
102 | ```java
103 | this.xxxFunction.invoke();
104 | this.xxxFunction2.invoke(LuaNumber.valueOf(123));
105 | ```
106 |
107 | > `The above is a simple example of how this framework should be used. BattleDemoService provides a set of examples comparing the completion of the Lua combat framework`
108 |
109 | # How to use and examples
110 |
111 | 1. [Battle Service Example](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/service/BattleDemoService.java)
112 | 2. [Example of Lua-Java data conversion tool](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/conver/ConvertModelLuaFileTest.java)
113 | 3. [Example of Lua-Java library conversion tool](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/conver/ConvertLibLuaFileTest.java)
114 |
115 | # The relevant data
116 |
117 | 1. https://www.lua.org/ lua官网
118 | 2. https://luaj.sourceforge.net luaj官网
119 |
120 | # Communication
121 |
122 | - Wechat:hjcenry
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Java-Lua 战斗框架
2 |
3 | ![Powered][1]
4 |
5 | [1]: https://raw.githubusercontent.com/github/explore/80688e429a7d4ef2fca1e82350fe8e3517d3494d/topics/lua/lua.png
6 |
7 | [README in english](https://github.com/hjcenry/lua-java-battle/blob/master/README.en.md)
8 |
9 | > **基于luaj实现的java使用lua的战斗框架**
10 |
11 | 该框架基于luaj的二次封装实现(https://luaj.sourceforge.net)
12 |
13 | # 主要提供以下功能
14 |
15 | - luaj基础接口的调用封装
16 | - 简化luaj环境搭建步骤
17 | - 管理lua战斗并提供接口
18 | - lua面向对象使用方案(class.lua)
19 | - lua战斗框架[使用示例](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/service/BattleDemoService.java)
20 | - luaj踩坑指南
21 |
22 | 该框架提供Java-Lua的战斗框架,有以下优缺点
23 |
24 | - **优点**:
25 |
26 | 1. 公用逻辑lua代码:前后端可以基于同一套语言使用同一套战斗逻辑代码,只需要设计好共战框架,即可实现一份代码两处使用。前后端程序员也可以基于这套框架共同开发,这无论是对于状态同步还是帧同步来说,都可以一定程度提升开发效率。
27 | 2. luaj框架相比其他java调用lua方式,是目前位置效率最高的。
28 | 3. lua代码无需编译即可直接使用,可以通过luaj设计一套热更逻辑
29 |
30 | - **缺点**(**踩坑指南**):
31 |
32 | 1. 占用jvm更多的堆和meta空间
33 |
34 | luaj提供两种编译器,luac和luajc。其中luac在load文件之后,会创建一个LuaClosure对象,其运行过程中会逐行解析lua命令,当然其运行效率也不会太高。
35 | 而luajc的原理是通过编译成Java字节码,并通过它的JavaLoader(继承ClassLoader)加载到内存,相当于一次编译多次运行。
36 | 但是熟悉的Java类加载机制的朋友应该清楚,这个过程中,JVM会在meta空间创建Klass信息,并在ClassLoader保存Klass引用。与此同时,luaj的JavaLoader也做了一件事:缓存动态生成的字节码byte[]
37 | 这种情况下,启动一个lua环境则会增加JVM的堆和meta空间的占用。
38 |
39 | 2. 运行效率不如原生Java
40 |
41 | luaj的作者描述luaj的运行效率基本和原生lua虚拟机运行效率相当,甚至反超。但在我的实际测试中,我没有拿luaj和原生lua对比,而是拿luaj和原生java相比,其性能是远不如原生Java的。
42 | 通过观察luaj编译后的源码也能发现,拿i++这样一个操作来举例,原本在Java中,是可以直接对基本数据类型int进行操作的,而在luaj中,会对int进行类似Integer的包装类(LuaInteger)进行包装。
43 |
44 | - 既是优点也是缺点:
45 | 1. 灵活的lua代码
46 |
47 | 灵活是一把双刃剑,用好了可以大幅提升开发效率,而用的不好的话,不仅不能提升开发效率,还可能对开发和维护,都带来极大的痛苦,这非常考研底层开发的能力。
48 |
49 | > **综上所述**:是否使用lua作为Java服务器的战斗逻辑代码,需要根据实际情况而定,它的优点是否给你代码巨大好处,同时你也能忍受它的缺点或者有其他方案克服它的缺点。
50 |
51 | `欢迎大家使用,有任何bug以及优化需求,欢迎提issue讨论`
52 |
53 | # Java Doc
54 |
55 | https://hjcenry.com/lua-java-battle/doc/
56 |
57 | # 快速开始
58 |
59 | 完整代码示例可以参考BattleDemoService
60 |
61 | # maven地址
62 |
63 | ```xml
64 |
65 | io.github.hjcenry
66 | lua-java-battle
67 | 1.0
68 |
69 | ```
70 |
71 | ## 1. 指定Lua参数
72 |
73 | ```java
74 | LuaInit.LuaInitBuilder luaInit=LuaInit.builder();
75 | luaInit.preScript("print('Hello Lua Battle!!!')");
76 | // 设置lua根路径
77 | luaInit.luaRootPath("F:\\project\\lua-java-battle\\src\\main\\lua\\");
78 | // 加载lua调用接口目录
79 | luaInit.luaLoadDirectories("interface");
80 | // 加载lua主文件
81 | luaInit.luaLoadFiles("FightManager.lua");
82 | // 展示log
83 | luaInit.showLog(true);
84 | ```
85 |
86 | ## 2. 初始化Lua环境
87 |
88 | ```java
89 | LuaBattleManager.getInstance().init(luaInit.build());
90 | ```
91 |
92 | ## 3. 初始化并缓存Java调用的Lua方法
93 |
94 | ```java
95 | // 初始所有需要用到的方法
96 | this.xxxFunction=this.initFunction("XXX.xxx");
97 | this.xxxFunction2=this.initFunction("xxx");
98 | ```
99 |
100 | ## 4. 调用Lua方法
101 |
102 | ```java
103 | this.xxxFunction.invoke();
104 | this.xxxFunction2.invoke(LuaNumber.valueOf(123));
105 | ```
106 |
107 | > `以上是简单的示例这个框架应该如何使用,BattleDemoService中提供了一套比较完成Lua战斗框架的示例`
108 |
109 | # 使用方法以及例子
110 |
111 | 1. [战斗Service示例](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/service/BattleDemoService.java)
112 | 2. [Lua-Java数据转换工具使用示例](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/conver/ConvertModelLuaFileTest.java)
113 | 3. [Lua-Java库转换工具使用示例](https://github.com/hjcenry/lua-java-battle/src/main/test/com/hjc/demo/conver/ConvertLibLuaFileTest.java)
114 |
115 | # 相关资料
116 |
117 | 1. https://www.lua.org/ lua官网
118 | 2. https://luaj.sourceforge.net luaj官网
119 |
120 | # 交流
121 |
122 | - 微信:hjcenry
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | lua-java-battle
8 | Java To Lua battle framework based on LuaJ
9 | https://github.com/hjcenry/lua-java-battle
10 |
11 | io.github.hjcenry
12 | lua-java-battle
13 | 1.0
14 |
15 |
16 | 8
17 | 8
18 |
19 |
20 |
21 |
22 | The Apache Software License, Version 2.0
23 | http://www.apache.org/licenses/LICENSE-2.0.txt
24 |
25 |
26 |
27 |
28 |
29 | hjcenry
30 | hejincheng1209@163.com
31 | https://hjcenry.com
32 |
33 |
34 |
35 |
36 | https://github.com/hjcenry/lua-java-battle
37 | scm:git:git://github.com/hjcenry/lua-java-battle.git
38 | scm:git:ssh://github.com/hjcenry/lua-java-battle.git
39 |
40 |
41 |
42 |
43 |
44 | org.apache.logging.log4j
45 | log4j-slf4j-impl
46 | 2.18.0
47 |
48 |
49 | org.slf4j
50 | slf4j-api
51 | 1.7.36
52 |
53 |
54 | org.apache.logging.log4j
55 | log4j-api
56 | 2.18.0
57 |
58 |
59 | org.apache.logging.log4j
60 | log4j-core
61 | 2.18.0
62 |
63 |
64 |
65 | org.apache.commons
66 | commons-lang3
67 | 3.4
68 |
69 |
70 |
71 | commons-beanutils
72 | commons-beanutils
73 | 1.9.2
74 |
75 |
76 |
77 | org.apache.commons
78 | commons-collections4
79 | 4.4
80 |
81 |
82 |
83 | com.google.guava
84 | guava
85 | 19.0
86 |
87 |
88 |
89 | org.luaj
90 | luaj-jse
91 | 3.0.1
92 |
93 |
94 | bcel
95 | bcel
96 | 5.1
97 |
98 |
99 |
100 | org.projectlombok
101 | lombok
102 | 1.18.12
103 |
104 |
105 |
106 | com.google.protobuf
107 | protobuf-java
108 | 2.6.1
109 |
110 |
111 | com.googlecode.protobuf-java-format
112 | protobuf-java-format
113 | 1.4
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | org.apache.maven.plugins
122 | maven-source-plugin
123 | 2.2.1
124 |
125 |
126 | package
127 |
128 | jar-no-fork
129 |
130 |
131 |
132 |
133 |
134 | org.apache.maven.plugins
135 | maven-compiler-plugin
136 | 3.0
137 |
138 | 1.8
139 | 1.8
140 | true
141 | UTF-8
142 | true
143 |
144 |
145 |
146 |
147 | maven-deploy-plugin
148 |
149 |
150 | deploy
151 | deploy
152 |
153 | deploy
154 |
155 |
156 |
157 |
158 |
159 |
160 | org.apache.maven.plugins
161 | maven-source-plugin
162 | 2.2.1
163 |
164 |
165 | package
166 |
167 | jar-no-fork
168 |
169 |
170 |
171 |
172 |
173 |
174 | org.apache.maven.plugins
175 | maven-javadoc-plugin
176 | 2.9.1
177 |
178 | private
179 | true
180 | UTF-8
181 | UTF-8
182 | UTF-8
183 | -Xdoclint:none
184 |
185 |
186 |
187 |
188 | package
189 |
190 | jar
191 |
192 |
193 |
194 |
195 |
196 |
197 | org.apache.maven.plugins
198 | maven-gpg-plugin
199 | 1.6
200 |
201 |
202 | verify
203 |
204 | sign
205 |
206 |
207 |
208 |
209 |
210 |
211 | org.apache.maven.plugins
212 | maven-release-plugin
213 | 2.5.3
214 |
215 |
216 |
217 | org.sonatype.plugins
218 | nexus-staging-maven-plugin
219 | 1.6.8
220 | true
221 |
222 | ossrh
223 | https://s01.oss.sonatype.org/
224 | true
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 | ossrh
233 | Nexus Snapshot Repository
234 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2
235 |
236 |
237 | ossrh
238 | Nexus Snapshot Repository
239 | https://s01.oss.sonatype.org/content/repositories/snapshots
240 |
241 |
242 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/LuaBattleConst.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua;
2 |
3 | /**
4 | * @author hejincheng
5 | * @version 1.0
6 | * @date 2022/8/17 23:15
7 | **/
8 | public class LuaBattleConst {
9 |
10 | /**
11 | * 默认lua包目录
12 | */
13 | public static final String DEFAULT_LUA_PACKAGE_PATH = "?.lua;%s/?.lua";
14 | /**
15 | * lua加载文件分隔符
16 | */
17 | public static final String LUA_LOAD_FILE_SEPARATE = ",";
18 | /**
19 | * lua文件后缀
20 | */
21 | public static final String LUA_FILE_EXT = ".lua";
22 | /**
23 | * 分隔符"."
24 | */
25 | public static final String SPLIT_POINT = "\\.";
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/LuaBattleManager.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua;
2 |
3 | import com.hjc.lua.engine.ILuaJEngine;
4 | import com.hjc.lua.engine.LuaJEngine;
5 | import com.hjc.util.FileUtil;
6 | import com.hjc.util.StringUtil;
7 | import lombok.Getter;
8 | import org.apache.commons.collections4.CollectionUtils;
9 | import org.luaj.vm2.Globals;
10 | import org.luaj.vm2.LuaFunction;
11 | import org.luaj.vm2.LuaValue;
12 | import org.luaj.vm2.Varargs;
13 | import org.slf4j.Logger;
14 | import org.slf4j.LoggerFactory;
15 |
16 | import java.io.File;
17 | import java.util.ArrayList;
18 | import java.util.Collections;
19 | import java.util.List;
20 |
21 | /**
22 | * @author hejincheng
23 | * @version 1.0
24 | * @date 2022/8/17 23:05
25 | **/
26 | public class LuaBattleManager {
27 |
28 | private final Logger logger = LoggerFactory.getLogger(LuaBattleManager.class);
29 |
30 | private static volatile LuaBattleManager instance;
31 |
32 | /**
33 | * lua全局对象
34 | */
35 | @Getter
36 | private Globals globals;
37 | /**
38 | * lua引擎
39 | */
40 | @Getter
41 | private ILuaJEngine luaEngine;
42 |
43 | private LuaBattleManager() {
44 | }
45 |
46 | public static LuaBattleManager getInstance() {
47 | if (instance == null) {
48 | synchronized (LuaBattleManager.class) {
49 | if (instance == null) {
50 | instance = new LuaBattleManager();
51 | }
52 | }
53 | }
54 | return instance;
55 | }
56 |
57 | /**
58 | * 初始化lua战斗管理器
59 | *
60 | * @param luaInit lua初始参数
61 | */
62 | public void init(LuaInit luaInit) {
63 | // 实例Lua全局引擎
64 | luaEngine = new LuaJEngine();
65 | // lua包目录
66 | String packagePath = StringUtil.isEmpty(luaInit.getLuaPackagePath()) ? String.format(LuaBattleConst.DEFAULT_LUA_PACKAGE_PATH, luaInit.getLuaRootPath()) : luaInit.getLuaPackagePath();
67 |
68 | // 加载lua文件
69 | List loadFiles = this.loadLuaFileList(luaInit.getLuaRootPath(), luaInit.getLuaLoadFiles(), luaInit.getLuaLoadDirectories());
70 | if (CollectionUtils.isEmpty(loadFiles)) {
71 | // 创建战斗全局globals
72 | logger.warn(String.format("lua.init.err.lua.load.files.null \n\troot:\t[%s]\n\tfiles:\t[%s]\n\tdir:\t[%s]",
73 | luaInit.getLuaRootPath(), luaInit.getLuaLoadFiles(), luaInit.getLuaLoadDirectories()));
74 | }
75 |
76 | // 创建Globals
77 | Globals globals = luaInit.getGlobals() != null ? luaInit.getGlobals() : LuaBattlePlatform.buildGlobals(packagePath);
78 | if (globals == null) {
79 | logger.error("lua.init.err.create.globals.err");
80 | return;
81 | }
82 |
83 | // 初始化Globals
84 | if (!luaEngine.initGlobals(luaInit.getPreScript(), loadFiles, globals)) {
85 | logger.error("lua.init.err.init.globals.err");
86 | return;
87 | }
88 |
89 | if (luaInit.isShowLog()) {
90 | this.luaEngine.logGlobals(globals);
91 | }
92 | this.globals = globals;
93 | }
94 |
95 | private List loadLuaFileList(String luaRootPath, String loadFiles, String loadDirectory) {
96 | List loadLuaFileList = new ArrayList<>();
97 | if (StringUtil.isEmpty(luaRootPath)) {
98 | // lua根路径空
99 | logger.error("lua.root.path.null");
100 | return Collections.emptyList();
101 | }
102 | if (!StringUtil.isEmpty(loadFiles)) {
103 | // add需要加载的文件
104 | for (String filePath : loadFiles.split(LuaBattleConst.LUA_LOAD_FILE_SEPARATE)) {
105 | // 找文件
106 | File file = FileUtil.findFile(luaRootPath +
107 | filePath, LuaBattleConst.LUA_FILE_EXT);
108 | if (file != null) {
109 | loadLuaFileList.add(file);
110 | }
111 | }
112 | }
113 | if (!StringUtil.isEmpty(loadDirectory)) {
114 | // add需要加载的文件目录
115 | for (String dirPath : loadDirectory.split(LuaBattleConst.LUA_LOAD_FILE_SEPARATE)) {
116 | // 找出该目录下的所有文件
117 | List fileList = FileUtil.getAllFileInDir(luaRootPath +
118 | dirPath, LuaBattleConst.LUA_FILE_EXT, true);
119 | if (!CollectionUtils.isEmpty(fileList)) {
120 | loadLuaFileList.addAll(fileList);
121 | }
122 | }
123 | }
124 | logger.info("{}.loadLuaFileList...", this.getClass().getSimpleName());
125 | for (File file : loadLuaFileList) {
126 | logger.info("load lua file:{}", file.getAbsolutePath());
127 | }
128 | return loadLuaFileList;
129 | }
130 |
131 | //===========================================================================================================================================================
132 | // 常用对外接口
133 | //===========================================================================================================================================================
134 |
135 | /**
136 | * 获取Lua方法
137 | *
138 | * @param luaModule Lua全局对象
139 | * @param funcName 方法名
140 | * @return Lua方法
141 | */
142 | public LuaFunction getLuaFunction(LuaValue luaModule, String funcName) {
143 | return this.luaEngine.getLuaFunction(luaModule, funcName);
144 | }
145 |
146 | /**
147 | * 获取lua方法
148 | *
149 | * @param funcName 方法名
150 | * @return lua方法
151 | */
152 | public LuaFunction getLuaFunction(String funcName) {
153 | return this.luaEngine.getLuaFunction(this.globals, funcName);
154 | }
155 |
156 | /**
157 | * 获取Lua对象
158 | *
159 | * @param luaObj Lua全局对象
160 | * @param objName 对象名
161 | * @return Lua对象
162 | */
163 | public LuaValue getLuaObj(LuaValue luaObj, String objName) {
164 | return this.luaEngine.getLuaObj(luaObj, objName);
165 | }
166 |
167 | /**
168 | * 调用字符串脚本
169 | *
170 | * @param script 脚本
171 | * @return 脚本返回值
172 | */
173 | public LuaValue callScript(String script) {
174 | return this.luaEngine.callScript(this.globals, script);
175 | }
176 |
177 | /**
178 | * 调用lua方法
179 | * 原始调用:无任何封装和规范的方法,可自行发挥
180 | *
181 | * @param luaModel lua模块
182 | * @param varargs 参数
183 | * @return {@link Varargs}
184 | */
185 | public Varargs invokeLua(String luaModel, Varargs varargs) {
186 | return this.luaEngine.invokeLua(this.globals, luaModel, varargs);
187 | }
188 |
189 | /**
190 | * 调用lua方法
191 | * 原始调用:无任何封装和规范的方法,可自行发挥
192 | *
193 | * @param luaValue lua对象
194 | * @param varargs 参数
195 | * @return {@link Varargs}
196 | */
197 | public Varargs invokeLua(LuaValue luaValue, Varargs varargs) {
198 | return this.luaEngine.invokeLua(luaValue, varargs);
199 | }
200 |
201 | /**
202 | * 初始化lua执行方法
203 | * 可按"."分割查找
204 | *
205 | * @param funcName 方法名
206 | * @return lua方法
207 | */
208 | public LuaFunction initExecuteFunction(String funcName) {
209 | // 消息执行方法
210 | if (StringUtil.isEmpty(funcName)) {
211 | logger.error("lua.init.execute.function.err.null");
212 | return null;
213 | }
214 | // 按“.”分割,支持按模块调用
215 | String[] functionFullNameArr = funcName.split(LuaBattleConst.SPLIT_POINT);
216 | LuaValue luaModule = globals;
217 | String functionName = null;
218 | // 遍历模块目录
219 | for (int index = 0; index < functionFullNameArr.length; index++) {
220 | if (index == functionFullNameArr.length - 1) {
221 | // 找出方法名
222 | functionName = functionFullNameArr[index];
223 | break;
224 | }
225 | // lua模块
226 | luaModule = luaEngine.getLuaObj(luaModule, functionFullNameArr[index]);
227 | }
228 | if (luaModule == null) {
229 | logger.error(String.format("lua.init.execute.function.err.[%s].null", funcName));
230 | return null;
231 | }
232 | LuaFunction luaExecuteFunction = luaEngine.getLuaFunction(luaModule, functionName);
233 | if (luaExecuteFunction == null) {
234 | logger.error(String.format("lua.init.execute.function.err.[%s].null", funcName));
235 | return null;
236 | }
237 | return luaExecuteFunction;
238 | }
239 | //===========================================================================================================================================================
240 | // 常用对外接口
241 | //===========================================================================================================================================================
242 | }
243 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/LuaBattlePlatform.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua;
2 |
3 | import com.hjc.util.StringUtil;
4 | import org.luaj.vm2.Globals;
5 | import org.luaj.vm2.LoadState;
6 | import org.luaj.vm2.LuaTable;
7 | import org.luaj.vm2.LuaValue;
8 | import org.luaj.vm2.compiler.LuaC;
9 | import org.luaj.vm2.lib.Bit32Lib;
10 | import org.luaj.vm2.lib.CoroutineLib;
11 | import org.luaj.vm2.lib.DebugLib;
12 | import org.luaj.vm2.lib.PackageLib;
13 | import org.luaj.vm2.lib.StringLib;
14 | import org.luaj.vm2.lib.TableLib;
15 | import org.luaj.vm2.lib.jse.JseBaseLib;
16 | import org.luaj.vm2.lib.jse.JseIoLib;
17 | import org.luaj.vm2.lib.jse.JseMathLib;
18 | import org.luaj.vm2.lib.jse.JseOsLib;
19 | import org.luaj.vm2.lib.jse.LuajavaLib;
20 | import org.luaj.vm2.luajc.LuaJC;
21 |
22 | /**
23 | * @ClassName LuaBattlePlatform
24 | * @Description lua战斗库
25 | * @Author hejincheng
26 | * @Date 2021/12/10 19:35
27 | * @Version 1.0
28 | **/
29 | public class LuaBattlePlatform {
30 |
31 | /**
32 | * 构建全局对象
33 | *
34 | * @return lua全局对象
35 | */
36 | public static Globals buildGlobals(String packagePath) {
37 | Globals globals = new Globals();
38 | globals.load(new JseBaseLib());
39 |
40 | globals.load(new PackageLib());
41 | if (!StringUtil.isEmpty(packagePath)) {
42 | // 加载lua包目录
43 | LuaTable packageLib = (LuaTable) globals.get("package");
44 | packageLib.set(LuaValue.valueOf("path"), LuaValue.valueOf(packagePath));
45 | }
46 |
47 | globals.load(new Bit32Lib());
48 | globals.load(new TableLib());
49 | globals.load(new StringLib());
50 | globals.load(new CoroutineLib());
51 | globals.load(new JseMathLib());
52 | globals.load(new JseIoLib());
53 | globals.load(new JseOsLib());
54 | globals.load(new DebugLib());
55 | globals.load(new LuajavaLib());
56 | LoadState.install(globals);
57 | LuaC.install(globals);
58 | // 使用luajc编译器,比默认luac编译器快3倍
59 | LuaJC.install(globals);
60 | return globals;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/LuaInit.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua;
2 |
3 | import lombok.Builder;
4 | import lombok.Getter;
5 | import org.luaj.vm2.Globals;
6 |
7 | /**
8 | * @author hejincheng
9 | * @version 1.0
10 | * @date 2022/8/17 23:24
11 | **/
12 | @Builder
13 | @Getter
14 | public class LuaInit {
15 |
16 | /**
17 | * lua预处理脚本
18 | */
19 | private String preScript;
20 | /**
21 | * lua根目录
22 | */
23 | private String luaRootPath;
24 | /**
25 | * lua包目录
26 | */
27 | private String luaPackagePath;
28 | /**
29 | * lua加载的文件(以英文逗号分割)
30 | */
31 | private String luaLoadFiles;
32 | /**
33 | * lua加载的目录(以英文逗号分割)
34 | */
35 | private String luaLoadDirectories;
36 | /**
37 | * 自定义lua globals
38 | */
39 | private Globals globals;
40 | /**
41 | * log
42 | */
43 | private boolean showLog;
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/annotation/LuaParam.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Lua参数
10 | *
11 | * @author hejincheng
12 | * @version 1.0
13 | * @date 2022/2/19 10:24
14 | **/
15 | @Target({ElementType.PARAMETER, ElementType.FIELD})
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface LuaParam {
18 |
19 | /**
20 | * lua字段名
21 | *
22 | * @return lua字段名
23 | */
24 | String value() default "";
25 |
26 | /**
27 | * 字段注释
28 | *
29 | * @return 字段注释
30 | */
31 | String comment() default "";
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/annotation/LuaServerLib.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Lua服务端调用库
10 | *
11 | * @author hejincheng
12 | * @version 1.0
13 | * @date 2022/2/19 10:24
14 | **/
15 | @Target(ElementType.TYPE)
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface LuaServerLib {
18 |
19 | /**
20 | * lua字段名
21 | *
22 | * @return lua字段名 lua中是self.xxx = _xxx
23 | */
24 | String fieldName() default "";
25 |
26 | /**
27 | * lua类名
28 | *
29 | * @return lua类名
30 | */
31 | String className() default "";
32 |
33 | /**
34 | * 注释
35 | *
36 | * @return 注释
37 | */
38 | String comment() default "";
39 |
40 | /**
41 | * 生成文件目录
42 | *
43 | * @return 文件目录,以BattleLogic/Lua为根路径开始
44 | */
45 | String fileDir() default "";
46 |
47 | /**
48 | * 是否加到ServerLib
49 | *
50 | * @return 是否加到ServerLib
51 | */
52 | boolean addToServerLib() default true;
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/annotation/LuaServerLibFunc.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Lua服务端调用库
10 | *
11 | * @author hejincheng
12 | * @version 1.0
13 | * @date 2022/2/19 10:24
14 | **/
15 | @Target(ElementType.METHOD)
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface LuaServerLibFunc {
18 |
19 | /**
20 | * 注释
21 | *
22 | * @return 注释
23 | */
24 | String comment() default "";
25 |
26 | /**
27 | * 返回注释
28 | *
29 | * @return 返回注释
30 | */
31 | String returnComment() default "";
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/annotation/LuaServerModel.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Lua服务端Model
10 | *
11 | * @author hejincheng
12 | * @version 1.0
13 | * @date 2022/2/19 10:24
14 | **/
15 | @Target(ElementType.TYPE)
16 | @Retention(RetentionPolicy.RUNTIME)
17 | public @interface LuaServerModel {
18 |
19 | /**
20 | * lua类名
21 | *
22 | * @return lua类名
23 | */
24 | String className() default "";
25 |
26 | /**
27 | * 注释
28 | *
29 | * @return 注释
30 | */
31 | String comment() default "";
32 |
33 | /**
34 | * 生成文件目录
35 | *
36 | * @return 文件目录,以BattleLogic/Lua为根路径开始
37 | */
38 | String fileDir() default "";
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/compiler/LuaCompiler.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.compiler;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.hjc.util.StringUtil;
5 | import org.apache.commons.collections4.KeyValue;
6 | import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | import java.io.File;
11 | import java.util.List;
12 |
13 | /**
14 | * lua编译工具类
15 | * 由于luaj在运行中产生动态字节码,无法看到源码,因此可通过LuaCompiler手动编译得到一份完整class字节码文件
16 | *
17 | * @author hejincheng
18 | * @version 1.0
19 | * @date 2022/2/22 16:29
20 | **/
21 | public class LuaCompiler {
22 |
23 | private static final Logger logger = LoggerFactory.getLogger(LuaCompiler.class);
24 |
25 | /**
26 | * 编译lua文件为class
27 | *
28 | * @param classPath 编译后的class路径
29 | * @param luaPath lua文件路径
30 | * @param ignores 忽略的目录名,以英文逗号分割
31 | * @param showLog 是否展示详细的编译log
32 | */
33 | public static void compile(String classPath, String luaPath, String ignores, boolean showLog) {
34 | compile(classPath, luaPath, null, ignores, showLog);
35 | }
36 |
37 | /**
38 | * 编译lua文件为class
39 | *
40 | * @param classPath 编译后的class路径
41 | * @param luaPath lua文件路径
42 | * @param packageName lua包目录名
43 | * @param ignores 忽略的目录名,以英文逗号分割
44 | * @param showLog 是否展示详细的编译log
45 | */
46 | public static void compile(String classPath, String luaPath, String packageName, String ignores, boolean showLog) {
47 | // 忽略的文件和目录
48 | if (StringUtil.isEmpty(packageName)) {
49 | packageName = new File(luaPath).getAbsolutePath().replace(File.separator, "_").replace(":", "");
50 | }
51 | List> keyValues = Lists.newArrayList(
52 | // 包名前缀
53 | new DefaultKeyValue("-p", packageName),
54 | // 递归编译所有
55 | new DefaultKeyValue("-r", ""),
56 | // load class验证生成的字节码
57 | new DefaultKeyValue("-l", ""),
58 | // 生成class目录
59 | new DefaultKeyValue("-d", classPath),
60 | // lua文件目录
61 | new DefaultKeyValue("-s", luaPath),
62 | // 忽略的lua文件
63 | new DefaultKeyValue("-i", ignores),
64 | // lua文件目录
65 | new DefaultKeyValue("/", "")
66 | );
67 | if (showLog) {
68 | // 显示信息
69 | keyValues.add(new DefaultKeyValue("-v", ""));
70 | }
71 |
72 | // 编译
73 | luajc.compile(keyValues, logger);
74 | }
75 |
76 | /**
77 | * Test Main
78 | *
79 | * @param args
80 | */
81 | public static void main(String[] args) {
82 | String classPath = "E:\\lua\\";
83 | String luaPath = "E:\\thd\\BattleLogic\\Lua\\";
84 | String ignores = "mobdebug.lua;dataTable;proto;protobuf;Test;Battle/Configs;Battle/LogicV2;Common";
85 | compile(classPath, luaPath, "BattleLogic", ignores, true);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/compiler/luajc.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.compiler;
2 | /*******************************************************************************
3 | * Copyright (c) 2009-2012 Luaj.org. All rights reserved.
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | * THE SOFTWARE.
22 | ******************************************************************************/
23 |
24 | import org.apache.commons.collections4.CollectionUtils;
25 | import org.apache.commons.collections4.KeyValue;
26 | import org.apache.commons.lang3.StringUtils;
27 | import org.luaj.vm2.Globals;
28 | import org.luaj.vm2.Lua;
29 | import org.luaj.vm2.lib.jse.JsePlatform;
30 | import org.luaj.vm2.luajc.LuaJC;
31 | import org.slf4j.Logger;
32 |
33 | import java.io.File;
34 | import java.io.FileInputStream;
35 | import java.io.FileOutputStream;
36 | import java.io.InputStreamReader;
37 | import java.util.*;
38 |
39 | /**
40 | * Compiler for lua files to compile lua sources or lua binaries into java classes.
41 | */
42 | public class luajc {
43 | private static final String version = Lua._VERSION + " Copyright (C) 2012 luaj.org";
44 |
45 | private static final String usage =
46 | "usage: java -cp luaj-jse.jar,bcel-5.2.jar luajc [options] fileordir [, fileordir ...]\n" +
47 | "Available options are:\n" +
48 | " - process stdin\n" +
49 | " -s src source directory\n" +
50 | " -i ignore files\n" +
51 | " -d dir destination directory\n" +
52 | " -p pkg package prefix to apply to all classes\n" +
53 | " -m generate main(String[]) function for JSE\n" +
54 | " -r recursively compile all\n" +
55 | " -l load classes to verify generated bytecode\n" +
56 | " -c enc use the supplied encoding 'enc' for input files\n" +
57 | " -v verbose\n";
58 |
59 | private static void usageExit() {
60 | System.err.println(usage);
61 | System.exit(-1);
62 | }
63 |
64 | private String destDir = ".";
65 | private boolean genMain = false;
66 | private boolean recurse = false;
67 | private boolean verbose = false;
68 | private boolean loadClasses = false;
69 | private String encoding = null;
70 | private String pkgPrefix = null;
71 | private String srcDir = ".";
72 | private final List files = new ArrayList<>();
73 | private final List ignoreFileNames = new ArrayList<>();
74 | private final Globals globals;
75 | private final Logger logger;
76 |
77 | public static void compile(List> keyValues, Logger logger) {
78 | new luajc(keyValues, logger);
79 | }
80 |
81 | private luajc(List> keyValues, Logger logger) {
82 | this.logger = logger;
83 |
84 | // process args
85 | List seeds = new ArrayList<>();
86 |
87 | // get stateful args
88 | if (!CollectionUtils.isEmpty(keyValues)) {
89 | for (KeyValue keyValue : keyValues) {
90 | String key = keyValue.getKey();
91 | String value = keyValue.getValue();
92 | if (!key.startsWith("-")) {
93 | seeds.add(key);
94 | } else {
95 | switch (key.charAt(1)) {
96 | case 'i':
97 | Collections.addAll(ignoreFileNames, value.split(";"));
98 | break;
99 | case 's':
100 | srcDir = value;
101 | break;
102 | case 'd':
103 | destDir = value;
104 | break;
105 | case 'l':
106 | loadClasses = true;
107 | break;
108 | case 'p':
109 | pkgPrefix = value;
110 | break;
111 | case 'm':
112 | genMain = true;
113 | break;
114 | case 'r':
115 | recurse = true;
116 | break;
117 | case 'c':
118 | encoding = value;
119 | break;
120 | case 'v':
121 | verbose = true;
122 | break;
123 | default:
124 | usageExit();
125 | break;
126 | }
127 | }
128 | }
129 | }
130 |
131 | // echo version
132 | if (verbose) {
133 | this.logger.info(version);
134 | this.logger.info("lua目录\t: " + srcDir);
135 | this.logger.info("class目录\t: " + destDir);
136 | this.logger.info("编译lua文件:\t " + seeds);
137 | this.logger.info("递归:\t" + recurse);
138 | }
139 |
140 | // need at least one seed
141 | if (seeds.size() <= 0) {
142 | this.logger.error(usage);
143 | System.exit(-1);
144 | }
145 |
146 | // collect up files to process
147 | for (String seed : seeds) {
148 | collectFiles(srcDir + "/" + seed);
149 | }
150 |
151 | // check for at least one file
152 | if (files.size() <= 0) {
153 | this.logger.error("no files found in " + seeds);
154 | System.exit(-1);
155 | }
156 |
157 | // process input files
158 | globals = JsePlatform.standardGlobals();
159 | for (InputFile file : files) {
160 | processFile(file);
161 | }
162 | }
163 |
164 | private void collectFiles(String path) {
165 | File f = new File(path);
166 | if (f.isDirectory() && recurse) {
167 | scanDir(f, pkgPrefix);
168 | } else if (f.isFile()) {
169 | File dir = f.getAbsoluteFile().getParentFile();
170 | if (dir != null) {
171 | scanFile(f, pkgPrefix);
172 | }
173 | }
174 | }
175 |
176 | private void scanDir(File dir, String javapackage) {
177 | File[] f = dir.listFiles();
178 | if (f == null) {
179 | return;
180 | }
181 |
182 | String dirPath = dir.getAbsolutePath();
183 | for (String ignoreFileName : ignoreFileNames) {
184 | if (StringUtils.isEmpty(ignoreFileName)) {
185 | continue;
186 | }
187 | if (ignoreFileName.endsWith(".lua")) {
188 | continue;
189 | }
190 | String ignoreDirFile = new File(srcDir + ignoreFileName).getAbsolutePath();
191 | if (ignoreDirFile.equals(dirPath)) {
192 | // 忽略目录
193 | if (verbose) {
194 | this.logger.warn("忽略目录:" + dirPath);
195 | }
196 | return;
197 | }
198 | }
199 |
200 | for (File file : f) {
201 | scanFile(file, javapackage);
202 | }
203 | }
204 |
205 | private void scanFile(File f, String javaPackage) {
206 | if (f.exists()) {
207 | if (f.isDirectory() && recurse) {
208 | scanDir(f, (javaPackage != null ? javaPackage + "." + f.getName() : f.getName()));
209 | } else if (f.isFile() && f.getName().endsWith(".lua")) {
210 | for (String ignoreFileName : ignoreFileNames) {
211 | if (StringUtils.isEmpty(ignoreFileName)) {
212 | continue;
213 | }
214 | if (ignoreFileName.equals(f.getName())) {
215 | // 忽略文件
216 | if (verbose) {
217 | this.logger.warn("忽略文件:" + ignoreFileName);
218 | }
219 | return;
220 | }
221 | }
222 | files.add(new InputFile(f, javaPackage));
223 | }
224 | }
225 | }
226 |
227 | private static final class LocalClassLoader extends ClassLoader {
228 | private final Hashtable, ?> t;
229 |
230 | private LocalClassLoader(Hashtable, ?> t) {
231 | this.t = t;
232 | }
233 |
234 | @Override
235 | public Class> findClass(String classname) throws ClassNotFoundException {
236 | byte[] bytes = (byte[]) t.get(classname);
237 | if (bytes != null) {
238 | return defineClass(classname, bytes, 0, bytes.length);
239 | }
240 | return super.findClass(classname);
241 | }
242 | }
243 |
244 | class InputFile {
245 | public String luaChunkName;
246 | public String srcFileName;
247 | public File inFile;
248 | public File outDir;
249 | public String javaPackage;
250 |
251 | public InputFile(File f, String javaPackage) {
252 | this.inFile = f;
253 | String subDir = javaPackage != null ? javaPackage.replace('.', '/') : null;
254 | String outDirPath = subDir != null ? destDir + "/" + subDir : destDir;
255 | this.javaPackage = javaPackage;
256 | this.srcFileName = (subDir != null ? subDir + "/" : "") + inFile.getName();
257 | this.luaChunkName = (subDir != null ? subDir + "/" : "") + inFile.getName().substring(0, inFile.getName().lastIndexOf('.'));
258 | this.inFile = f;
259 | this.outDir = new File(outDirPath);
260 | }
261 | }
262 |
263 | private void processFile(InputFile inf) {
264 | if (!inf.outDir.mkdirs()) {
265 | return;
266 | }
267 | try {
268 | if (verbose) {
269 | this.logger.info("目标文件=" + inf.luaChunkName + " lua源文件=" + inf.srcFileName);
270 | }
271 |
272 | // create the chunk
273 | FileInputStream fis = new FileInputStream(inf.inFile);
274 |
275 | String luaChunkName = inf.luaChunkName;
276 | if (luaChunkName.contains("/")) {
277 | luaChunkName = luaChunkName.substring(luaChunkName.lastIndexOf("/") + 1);
278 | }
279 | String srcFileName = inf.srcFileName;
280 |
281 | final Hashtable, ?> t = encoding != null ?
282 | LuaJC.instance.compileAll(new InputStreamReader(fis, encoding), luaChunkName, srcFileName, globals, genMain) :
283 | LuaJC.instance.compileAll(fis, luaChunkName, srcFileName, globals, genMain);
284 | fis.close();
285 |
286 | // write out the chunk
287 | for (Enumeration> e = t.keys(); e.hasMoreElements(); ) {
288 | String key = (String) e.nextElement();
289 | byte[] bytes = (byte[]) t.get(key);
290 | if (key.indexOf('/') >= 0) {
291 | String d = (destDir != null ? destDir + "/" : "") + key.substring(0, key.lastIndexOf('/'));
292 | new File(d).mkdirs();
293 | }
294 |
295 | String dirPath = inf.outDir.getAbsolutePath();
296 | String destPath = dirPath + "/" + key + ".class";
297 |
298 | if (verbose) {
299 | this.logger.info(" " + destPath + " (" + bytes.length + " bytes)");
300 | }
301 | FileOutputStream fos = new FileOutputStream(destPath);
302 | fos.write(bytes);
303 | fos.close();
304 | }
305 |
306 | // try to load the files
307 | if (loadClasses) {
308 | ClassLoader loader = new LocalClassLoader(t);
309 | for (Enumeration> e = t.keys(); e.hasMoreElements(); ) {
310 | String classname = (String) e.nextElement();
311 | try {
312 | Class> c = loader.loadClass(classname);
313 | Object o = c.newInstance();
314 | if (verbose) {
315 | this.logger.info(" load类文件[" + classname + "] - " + o);
316 | }
317 | } catch (Exception ex) {
318 | this.logger.error(" load类文件[" + classname + "]失败: " + ex);
319 | }
320 | }
321 | }
322 |
323 | } catch (Exception e) {
324 | this.logger.error(" load类文件[" + inf.srcFileName + "]失败: " + e);
325 | e.printStackTrace(System.err);
326 | System.err.flush();
327 | }
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/convertor/LuaJavaBeanConvertor.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.convertor;
2 |
3 | import com.hjc.lua.exception.LuaException;
4 | import com.hjc.util.enumutil.EnumUtil;
5 | import com.hjc.util.enumutil.IndexedEnum;
6 | import org.luaj.vm2.LuaTable;
7 | import org.luaj.vm2.LuaValue;
8 |
9 | import java.lang.reflect.Field;
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Map;
14 |
15 | /**
16 | * lua和javaBean互相转换的转换器
17 | *
18 | * @ClassName LuaJavaBeanConverter
19 | * @Description
20 | * @Author hejincheng
21 | * @Date 2022/1/6 11:48
22 | * @Version 1.0
23 | **/
24 | public class LuaJavaBeanConvertor {
25 |
26 | /**
27 | * JavaBean对象转换LuaTable
28 | *
29 | * @param bean JavaBean对象
30 | * @return LuaTable
31 | */
32 | public static LuaTable convertJavaBeanToLuaTable(Object bean) throws IllegalAccessException, LuaException {
33 | if (bean == null) {
34 | return new LuaTable(0, 0);
35 | }
36 | Class> clz = bean.getClass();
37 | Field[] fields = clz.getDeclaredFields();
38 |
39 | LuaTable luaTable = new LuaTable();
40 | for (Field field : fields) {
41 | // 取出值
42 | field.setAccessible(true);
43 | Object fieldValue = field.get(bean);
44 | field.setAccessible(false);
45 | LuaValue luaValue = convertFieldToLuaValue(field.getType(), bean.getClass().getSimpleName(), fieldValue);
46 | if (luaValue != null) {
47 | luaTable.set(field.getName(), luaValue);
48 | }
49 | }
50 | return luaTable;
51 | }
52 |
53 | /**
54 | * 转换字段为LuaValue
55 | *
56 | * @param fieldClz 字段类
57 | * @param beanName bean名
58 | * @param obj 对象
59 | * @return LuaValue
60 | */
61 | private static LuaValue convertFieldToLuaValue(Class> fieldClz, String beanName, Object obj) throws LuaException, IllegalAccessException {
62 | // 数据类型
63 | DataType dataType = DataType.valueOf(fieldClz);
64 | if (dataType == null) {
65 | // 找不到这个类型,递归处理它
66 | return convertJavaBeanToLuaTable(obj);
67 | }
68 |
69 | if (List.class.isAssignableFrom(fieldClz)) {
70 | if (obj == null) {
71 | return new LuaTable(0, 0);
72 | }
73 | // list数据
74 | List> list = (List>) obj;
75 | // 创建list同等大小的table
76 | LuaTable luaArray = LuaTable.tableOf(list.size(), 0);
77 | for (int index = 0; index < list.size(); index++) {
78 | Object dataObj = list.get(index);
79 | // 转换LuaValue
80 | LuaValue listValue = dataType.castObjectLuaValue(dataObj, beanName);
81 | if (listValue != null) {
82 | luaArray.insert(index + 1, listValue);
83 | }
84 | }
85 | return luaArray;
86 | }
87 |
88 | if (obj == null) {
89 | return LuaValue.NIL;
90 | }
91 | return dataType.castObjectLuaValue(obj, beanName);
92 | }
93 |
94 | /**
95 | * 数据类型
96 | *
97 | * @ClassName LuaProtoConverter
98 | * @Author hejincheng
99 | * @Date 2021/12/9 11:12
100 | * @Version 1.0
101 | **/
102 | enum DataType implements IndexedEnum {
103 | // int
104 | INT(0, int.class) {
105 | @Override
106 | public LuaValue castObjectLuaValue(Object value, String beanName) {
107 | return LuaValue.valueOf((int) value);
108 | }
109 | },
110 | INT_BOX(1, Integer.class) {
111 | @Override
112 | public LuaValue castObjectLuaValue(Object value, String beanName) {
113 | return LuaValue.valueOf((Integer) value);
114 | }
115 | },
116 | // long
117 | LONG(2, long.class) {
118 | @Override
119 | public LuaValue castObjectLuaValue(Object value, String beanName) {
120 | return LuaValue.valueOf((double) (long) value);
121 | }
122 | },
123 | LONG_BOX(3, Long.class) {
124 | @Override
125 | public LuaValue castObjectLuaValue(Object value, String beanName) {
126 | return LuaValue.valueOf((double) (Long) value);
127 | }
128 | },
129 | // float
130 | FLOAT(4, float.class) {
131 | @Override
132 | public LuaValue castObjectLuaValue(Object value, String beanName) {
133 | return LuaValue.valueOf((float) value);
134 | }
135 | },
136 | FLOAT_BOX(5, Float.class) {
137 | @Override
138 | public LuaValue castObjectLuaValue(Object value, String beanName) {
139 | return LuaValue.valueOf((Float) value);
140 | }
141 | },
142 | // double
143 | DOUBLE(6, double.class) {
144 | @Override
145 | public LuaValue castObjectLuaValue(Object value, String beanName) {
146 | return LuaValue.valueOf((double) value);
147 | }
148 | },
149 | DOUBLE_BOX(7, Double.class) {
150 | @Override
151 | public LuaValue castObjectLuaValue(Object value, String beanName) {
152 | return LuaValue.valueOf((Double) value);
153 | }
154 | },
155 | // bool
156 | BOOLEAN(8, boolean.class) {
157 | @Override
158 | public LuaValue castObjectLuaValue(Object value, String beanName) {
159 | return LuaValue.valueOf((boolean) value);
160 | }
161 | },
162 | BOOLEAN_BOX(9, Boolean.class) {
163 | @Override
164 | public LuaValue castObjectLuaValue(Object value, String beanName) {
165 | return LuaValue.valueOf((Boolean) value);
166 | }
167 | },
168 | // string
169 | STRING(10, String.class) {
170 | @Override
171 | public LuaValue castObjectLuaValue(Object value, String beanName) {
172 | if (value == null) {
173 | return LuaValue.EMPTYSTRING;
174 | }
175 | return LuaValue.valueOf(value.toString());
176 | }
177 | },
178 | // list
179 | ARRAY_LIST(11, ArrayList.class) {
180 | @Override
181 | public LuaValue castObjectLuaValue(Object value, String beanName) throws IllegalAccessException, LuaException {
182 | if (value == null) {
183 | return LuaValue.EMPTYSTRING;
184 | }
185 | // 递归转换子table
186 | return convertFieldToLuaValue(value.getClass(), beanName, value);
187 | }
188 | },
189 | // enum
190 | ENUM(12, Enum.class),
191 | ;
192 |
193 | private Class> javaClass;
194 | private int index;
195 |
196 | DataType(int index, Class> javaClass) {
197 | this.javaClass = javaClass;
198 | this.index = index;
199 | }
200 |
201 | @Override
202 | public int getIndex() {
203 | return this.index;
204 | }
205 |
206 | private static final List values = IndexedEnum.IndexedEnumUtil.toIndexes(DataType.values());
207 |
208 | private static final Map, DataType> CLASS_VALUE_MAP = new HashMap<>();
209 |
210 | static {
211 | for (DataType dataType : values) {
212 | if (dataType == null) {
213 | continue;
214 | }
215 | CLASS_VALUE_MAP.put(dataType.javaClass, dataType);
216 | }
217 | }
218 |
219 | public static List getValues() {
220 | return values;
221 | }
222 |
223 | public static DataType valueOf(int value) {
224 | return EnumUtil.valueOf(values, value);
225 | }
226 |
227 | public static DataType valueOf(Class> javaClass) {
228 | return CLASS_VALUE_MAP.get(javaClass);
229 | }
230 |
231 | /**
232 | * java对象转换成luaTable
233 | *
234 | * @param value java对象
235 | * @param beanName
236 | * @return
237 | */
238 | public LuaValue castObjectLuaValue(Object value, String beanName) throws IllegalAccessException, LuaException {
239 | return null;
240 | }
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/convertor/LuaProtobufConvertor.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.convertor;
2 |
3 | import com.google.protobuf.ByteString;
4 | import com.google.protobuf.Descriptors;
5 | import com.google.protobuf.GeneratedMessage;
6 | import com.google.protobuf.Message;
7 | import com.hjc.lua.exception.LuaException;
8 | import com.hjc.util.enumutil.EnumUtil;
9 | import com.hjc.util.enumutil.IndexedEnum;
10 | import org.apache.commons.collections4.MapUtils;
11 | import org.luaj.vm2.LuaBoolean;
12 | import org.luaj.vm2.LuaTable;
13 | import org.luaj.vm2.LuaValue;
14 | import org.slf4j.Logger;
15 | import org.slf4j.LoggerFactory;
16 |
17 | import java.lang.reflect.InvocationTargetException;
18 | import java.lang.reflect.Method;
19 | import java.util.ArrayList;
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 |
24 | /**
25 | * lua - proto转换器
26 | *
27 | * @ClassName LuaProtoConverter
28 | * @Description lua协议转换
29 | * @Author hejincheng
30 | * @Date 2021/12/8 19:20
31 | * @Version 1.0
32 | **/
33 | public class LuaProtobufConvertor {
34 |
35 | private static Logger log = LoggerFactory.getLogger(LuaProtobufConvertor.class);
36 |
37 | /**
38 | * proto msg消息转换成luaTable
39 | *
40 | * @param msg proto消息
41 | * @return 转换后的luaTable
42 | */
43 | public static LuaTable convertProtoToLuaTable(GeneratedMessage msg) throws LuaException {
44 | LuaTable luaTable = new LuaTable();
45 | Map fieldMap = msg.getAllFields();
46 | if (MapUtils.isEmpty(fieldMap)) {
47 | return luaTable;
48 | }
49 | for (Map.Entry entry : fieldMap.entrySet()) {
50 | Descriptors.FieldDescriptor key = entry.getKey();
51 |
52 | // 当前支持的数据类型
53 | DataType dataType = DataType.valueOf(key.getJavaType().ordinal());
54 | if (dataType == null) {
55 | // 不支持该转换类型
56 | LuaException luaException = new LuaException(String.format("lua converter not support data type : %s", key.getJavaType()));
57 | log.error("msg.convert.to.lua.err", luaException);
58 | throw luaException;
59 | }
60 | LuaValue luaValue;
61 | if (key.isRepeated()) {
62 | // list数据
63 | List> repeatList = (List>) entry.getValue();
64 | // 创建list同等大小的table
65 | LuaTable luaArray = LuaTable.tableOf(repeatList.size(), 0);
66 | for (int index = 0; index < repeatList.size(); index++) {
67 | Object value = repeatList.get(index);
68 | // 转换LuaValue
69 | LuaValue listValue = dataType.castObjectLuaValue(value);
70 | if (listValue != null) {
71 | // 坑!!!lua从1开始,luaj也是,insert 0会按hash插入,
72 | luaArray.insert(index + 1, listValue);
73 | }
74 | }
75 | luaValue = luaArray;
76 | } else {
77 | // 直接转换LuaValue
78 | luaValue = dataType.castObjectLuaValue(entry.getValue());
79 | }
80 | if (luaValue != null) {
81 | // lua有值才set到table中
82 | luaTable.set(key.getName(), luaValue);
83 | }
84 | }
85 | return luaTable;
86 | }
87 |
88 | /**
89 | * luaTable转换成proto msg
90 | *
91 | * @param msgClz 消息对象
92 | * @param luaTable 要转换的LuaTable
93 | * @param proto类(需要包含Builder)
94 | * @return proto msg
95 | */
96 | public static T convertLuaTableToProto(Class msgClz, LuaTable luaTable)
97 | throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, LuaException {
98 | if (luaTable == null) {
99 | throw new LuaException(String.format("%s convert error : luaTable is null", msgClz));
100 | }
101 | Message.Builder builder = getClassBuilder(msgClz);
102 | return (T) convertLuaTableToProto(builder, luaTable);
103 | }
104 |
105 | private static Message.Builder getClassBuilder(Class> msgClz)
106 | throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
107 | Method newBuilderMethod = msgClz.getMethod("newBuilder");
108 | return (Message.Builder) newBuilderMethod.invoke(null);
109 | }
110 |
111 | private static GeneratedMessage convertLuaTableToProto(Message.Builder builder, LuaTable luaTable)
112 | throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, LuaException {
113 | for (Descriptors.FieldDescriptor field : builder.getDescriptorForType().getFields()) {
114 |
115 | String fieldName = field.getName();
116 | LuaValue luaValue = luaTable.get(fieldName);
117 | Class> fieldClass = builder.getField(field).getClass();
118 |
119 | // 当前支持的数据类型
120 | DataType dataType = DataType.valueOf(field.getJavaType().ordinal());
121 | if (dataType == null) {
122 | // 不支持该转换类型
123 | LuaException luaException = new LuaException(String.format("lua converter not support data type : %s", field.getJavaType()));
124 | log.error("lua.convert.to.msg.err", luaException);
125 | throw luaException;
126 | }
127 | Object object;
128 | if (field.isRepeated()) {
129 | // list数据
130 | List fieldList = null;
131 | if (luaValue != LuaValue.NIL) {
132 | LuaTable repeatLuaTables = (LuaTable) luaValue;
133 | int tableLen = repeatLuaTables.rawlen();
134 | // 创建table同等大小的list
135 | fieldList = new ArrayList<>(tableLen);
136 | for (int i = 1; i <= tableLen; i++) {
137 | LuaValue fieldLuaValue = repeatLuaTables.get(i);
138 | // 转换java类
139 | Message.Builder fieldBuilder = dataType.getFieldBuilder(builder, field);
140 | Object javaObject = dataType.castLuaValueToObject(fieldBuilder, fieldLuaValue);
141 | if (javaObject != null) {
142 | fieldList.add(javaObject);
143 | }
144 | }
145 | } else {
146 | fieldList = new ArrayList<>(0);
147 | }
148 | object = fieldList;
149 | } else {
150 | // 直接转换java类
151 | Message.Builder fieldBuilder = null;
152 | if (field.getJavaType().ordinal() == DataType.MESSAGE.getIndex()) {
153 | // MESSAGE类型需要消息体Builder
154 | fieldBuilder = getClassBuilder(fieldClass);
155 | }
156 | object = dataType.castLuaValueToObject(fieldBuilder, luaValue);
157 | }
158 | if (object != null) {
159 | builder.setField(field, object);
160 | }
161 | }
162 | return (GeneratedMessage) builder.build();
163 | }
164 |
165 | /**
166 | * 数据类型
167 | *
168 | * @ClassName LuaProtoConverter
169 | * @Author hejincheng
170 | * @Date 2021/12/9 11:12
171 | * @Version 1.0
172 | **/
173 | enum DataType implements IndexedEnum {
174 | // int
175 | INT(Descriptors.FieldDescriptor.JavaType.INT, int.class) {
176 | @Override
177 | public LuaValue castObjectLuaValue(Object value) {
178 | return LuaValue.valueOf((int) value);
179 | }
180 |
181 | @Override
182 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
183 | return value.toint();
184 | }
185 | },
186 | // long
187 | LONG(Descriptors.FieldDescriptor.JavaType.LONG, long.class) {
188 | @Override
189 | public LuaValue castObjectLuaValue(Object value) {
190 | return LuaValue.valueOf((double) (long) value);
191 | }
192 |
193 | @Override
194 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
195 | return value.tolong();
196 | }
197 | },
198 | // float
199 | FLOAT(Descriptors.FieldDescriptor.JavaType.FLOAT, float.class) {
200 | @Override
201 | public LuaValue castObjectLuaValue(Object value) {
202 | return LuaValue.valueOf((float) value);
203 | }
204 |
205 | @Override
206 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
207 | return value.tofloat();
208 | }
209 | },
210 | // double
211 | DOUBLE(Descriptors.FieldDescriptor.JavaType.DOUBLE, double.class) {
212 | @Override
213 | public LuaValue castObjectLuaValue(Object value) {
214 | return LuaValue.valueOf((double) value);
215 | }
216 |
217 | @Override
218 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
219 | return value.todouble();
220 | }
221 | },
222 | // bool
223 | BOOLEAN(Descriptors.FieldDescriptor.JavaType.BOOLEAN, boolean.class) {
224 | @Override
225 | public LuaValue castObjectLuaValue(Object value) {
226 | return LuaBoolean.valueOf(Boolean.parseBoolean(value.toString()));
227 | }
228 |
229 | @Override
230 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
231 | return value.toboolean();
232 | }
233 | },
234 | // string
235 | STRING(Descriptors.FieldDescriptor.JavaType.STRING, Object.class) {
236 | @Override
237 | public LuaValue castObjectLuaValue(Object value) {
238 | return LuaValue.valueOf(value.toString());
239 | }
240 |
241 | @Override
242 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) {
243 | return value.toString();
244 | }
245 | },
246 | // bytes
247 | BYTE_STRING(Descriptors.FieldDescriptor.JavaType.BYTE_STRING, ByteString.class),
248 | // enum
249 | ENUM(Descriptors.FieldDescriptor.JavaType.ENUM, Enum.class),
250 | // message
251 | MESSAGE(Descriptors.FieldDescriptor.JavaType.MESSAGE, GeneratedMessage.class) {
252 | @Override
253 | public LuaValue castObjectLuaValue(Object value) throws LuaException {
254 | // 递归转换子table
255 | return LuaProtobufConvertor.convertProtoToLuaTable((GeneratedMessage) value);
256 | }
257 |
258 | @Override
259 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value)
260 | throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, LuaException {
261 | // 递归转换子对象
262 | if (!value.istable()) {
263 | return null;
264 | }
265 | return LuaProtobufConvertor.convertLuaTableToProto(builder, (LuaTable) value);
266 | }
267 |
268 | @Override
269 | public Message.Builder getFieldBuilder(Message.Builder builder, Descriptors.FieldDescriptor field) {
270 | return builder.newBuilderForField(field);
271 | }
272 | },
273 | ;
274 |
275 | private Descriptors.FieldDescriptor.JavaType javaType;
276 | private Class> javaClass;
277 |
278 | DataType(Descriptors.FieldDescriptor.JavaType javaType, Class> javaClass) {
279 | this.javaType = javaType;
280 | this.javaClass = javaClass;
281 | }
282 |
283 | @Override
284 | public int getIndex() {
285 | return this.javaType.ordinal();
286 | }
287 |
288 | private static final List values = IndexedEnum.IndexedEnumUtil.toIndexes(DataType.values());
289 |
290 | private static final Map, DataType> classValueMap = new HashMap<>();
291 |
292 | static {
293 | for (DataType dataType : values) {
294 | if (dataType == null) {
295 | continue;
296 | }
297 | classValueMap.put(dataType.javaClass, dataType);
298 | }
299 | }
300 |
301 | public static List getValues() {
302 | return values;
303 | }
304 |
305 | public static DataType valueOf(int value) {
306 | return EnumUtil.valueOf(values, value);
307 | }
308 |
309 | public static DataType valueOf(Class> javaClass) {
310 | return classValueMap.get(javaClass);
311 | }
312 |
313 | /**
314 | * java对象转换成luaTable
315 | *
316 | * @param value java对象
317 | * @return luaTable
318 | */
319 | public LuaValue castObjectLuaValue(Object value) throws LuaException {
320 | return null;
321 | }
322 |
323 | /**
324 | * luaTable转换成java对象
325 | *
326 | * @param builder 新对象builder(仅MESSAGE类型有用,其他传null)
327 | * @param value lua对象
328 | * @return java对象
329 | */
330 | public Object castLuaValueToObject(Message.Builder builder, LuaValue value) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, LuaException {
331 | return null;
332 | }
333 |
334 | /**
335 | * 获取属性的builder
336 | * 只有MESSAGE类型才有实现
337 | *
338 | * @param builder 父类型builder
339 | * @param field 属性
340 | * @return 属性的builder
341 | */
342 | public Message.Builder getFieldBuilder(Message.Builder builder, Descriptors.FieldDescriptor field) {
343 | return null;
344 | }
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/convertor/LuaServerFileConverter.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.convertor;
2 |
3 | import com.hjc.lua.annotation.LuaServerLib;
4 | import com.hjc.util.StringUtil;
5 | import org.apache.commons.collections4.KeyValue;
6 | import org.apache.commons.collections4.MapUtils;
7 | import org.luaj.vm2.*;
8 |
9 | import java.io.*;
10 | import java.text.SimpleDateFormat;
11 | import java.util.*;
12 |
13 | /**
14 | * @author hejincheng
15 | * @version 1.0
16 | * @date 2022/2/18 14:54
17 | **/
18 | public class LuaServerFileConverter {
19 |
20 | /**
21 | * 模板文件路径
22 | */
23 | protected static String templateFilePath;
24 | /**
25 | * lua根路径
26 | */
27 | protected static String luaLoadRootPath;
28 | /**
29 | * Java扫描路径
30 | */
31 | protected static String javaScanPackage;
32 | /**
33 | * ServerLib文件路径
34 | */
35 | protected static String serverLibFilePath;
36 |
37 | static void initParams() {
38 | // 用户自定义lua地址了
39 | String customLuaRootPath = System.getProperty("luaRootPath");
40 | if (!StringUtil.isEmpty(customLuaRootPath)) {
41 | luaLoadRootPath = customLuaRootPath;
42 | } else {
43 | luaLoadRootPath = "C:\\Lua\\";
44 | }
45 | // 自定义模板文件目录
46 | String customTemplateFilePath = System.getProperty("templateFilePath");
47 | if (!StringUtil.isEmpty(customTemplateFilePath)) {
48 | templateFilePath = customTemplateFilePath;
49 | } else {
50 | templateFilePath = Objects.requireNonNull(LuaServerFileConverter.class.getResource("/")).getPath();
51 | }
52 | // 自定义模板文件目录
53 | String customJavaScanPackage = System.getProperty("javaScanPackage");
54 | if (!StringUtil.isEmpty(customJavaScanPackage)) {
55 | javaScanPackage = customJavaScanPackage;
56 | } else {
57 | javaScanPackage = "com.hjc";
58 | }
59 | // 自定义模板文件目录
60 | String customServerLibFilePath = System.getProperty("serverLibFilePath");
61 | if (!StringUtil.isEmpty(customServerLibFilePath)) {
62 | serverLibFilePath = customServerLibFilePath;
63 | } else {
64 | serverLibFilePath = "Lib\\Server\\";
65 | }
66 | }
67 |
68 | /**
69 | * 读文件
70 | *
71 | * @param templateFile 模板文件
72 | * @param luaFileName lua文件名
73 | * @return 读取字符串
74 | */
75 | public static StringBuilder readFile(String templateFile, String luaFileName) {
76 | File file = new File(templateFile);
77 | if (!file.exists()) {
78 | System.err.println(file.getAbsolutePath() + " not exist");
79 | return null;
80 | }
81 |
82 | String s;
83 | StringBuilder builder = new StringBuilder();
84 | Date nowDate = Calendar.getInstance().getTime();
85 | try (BufferedReader br = new BufferedReader(new FileReader(file))) {
86 | while ((s = br.readLine()) != null) {
87 | s = s.replace("${USER}", System.getProperty("user.name"));
88 | s = s.replace("${DATE}", new SimpleDateFormat("yyyy-MM-dd").format(nowDate));
89 | s = s.replace("${TIME}", new SimpleDateFormat("HH:mm:ss").format(nowDate));
90 | s = s.replace("${NAME}", luaFileName);
91 | builder.append(s).append("\n");
92 | }
93 | } catch (IOException e) {
94 | e.printStackTrace();
95 | }
96 | return builder;
97 | }
98 |
99 | /**
100 | * 写文件
101 | *
102 | * @param content 内容
103 | * @param luaFile 文件
104 | */
105 | public static void writeFile(String content, String luaFile) {
106 | File file = new File(luaFile);
107 | if (file.exists()) {
108 | if (!file.delete()) {
109 | return;
110 | }
111 | }
112 | try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
113 | bw.write(content);
114 | System.out.println("Generate File " + file.getAbsolutePath());
115 | } catch (IOException e) {
116 | System.err.println("[" + luaFile + "]写入文件错误,请检查参数luaRootPath或serverLibFilePath(生成ServerLib时使用)是否正确设置");
117 | e.printStackTrace();
118 | }
119 | }
120 |
121 | /**
122 | * 获取lua方法的文本
123 | *
124 | * @param luaFileName 文件名
125 | * @param functionName 方法名
126 | * @param paramTypeMap 参数类型 <参数名,<参数类型,参数注释>>
127 | * @param returnType 返回类型
128 | * @param funcComment 方法注释
129 | * @return lua方法文本
130 | */
131 | public static String getLuaFunctionString(String luaFileName, String functionName, LinkedHashMap, String>> paramTypeMap, Class> returnType, String funcComment) {
132 | // 方法注释
133 | StringBuilder funcCommentBuilder = new StringBuilder();
134 | if (!StringUtil.isEmpty(funcComment)) {
135 | funcCommentBuilder.append(funcComment);
136 | }
137 | // 参数名字
138 | StringBuilder paramNameBuilder = new StringBuilder("(");
139 | // 参数类型
140 | StringBuilder paramCommentBuilder = new StringBuilder();
141 |
142 | if (!MapUtils.isEmpty(paramTypeMap)) {
143 | for (Map.Entry, String>> entry : paramTypeMap.entrySet()) {
144 | String paramName = entry.getKey();
145 | Class> type = entry.getValue().getKey();
146 | String parComment = entry.getValue().getValue();
147 |
148 | if (paramCommentBuilder.length() > 0) {
149 | paramCommentBuilder.append("\n");
150 | }
151 | paramCommentBuilder.append(String.format("---@param %s %s %s", entry.getKey(), getLuaTypeString(type), parComment));
152 |
153 | if (paramNameBuilder.length() > 1) {
154 | paramNameBuilder.append(", ");
155 | }
156 | paramNameBuilder.append(paramName);
157 | }
158 | }
159 |
160 | paramNameBuilder.append(")");
161 |
162 | String functionString = "";
163 | if (!StringUtil.isEmpty(funcCommentBuilder.toString())) {
164 | functionString += "-- " + funcCommentBuilder + "\n";
165 | } else {
166 | functionString += "-- \n";
167 | }
168 | if (!StringUtil.isEmpty(paramCommentBuilder.toString())) {
169 | functionString += paramCommentBuilder + "\n";
170 | }
171 | functionString += "---@type function\n";
172 |
173 | if (returnType != null) {
174 | functionString += "---@return " + getLuaTypeString(returnType) + "\n";
175 | }
176 |
177 | functionString += "---@public\n" +
178 | "function " + luaFileName + ":" + functionName +
179 | paramNameBuilder + "\n" +
180 | " return\n" +
181 | "end\n";
182 | return functionString;
183 | }
184 |
185 | public static String getLuaCtorFunctionString(String luaFileName, LinkedHashMap> fieldTypes) {
186 | if (MapUtils.isEmpty(fieldTypes)) {
187 | return "";
188 | }
189 | StringBuilder fieldParamBuilder = new StringBuilder("(");
190 | StringBuilder fieldBuilder = new StringBuilder();
191 | for (Map.Entry> entry : fieldTypes.entrySet()) {
192 | String name = entry.getKey();
193 |
194 | // 赋值语句
195 | fieldBuilder.append(" self.").append(name).append(" = ").append("_").append(name).append("\n");
196 |
197 | // 参数
198 | if (fieldParamBuilder.length() > 1) {
199 | fieldParamBuilder.append(", ");
200 | }
201 | fieldParamBuilder.append("_").append(name);
202 | }
203 | fieldParamBuilder.append(")");
204 |
205 | String functionString = "function " + luaFileName + ":ctor" +
206 | fieldParamBuilder + "\n";
207 | if (!StringUtil.isEmpty(fieldBuilder.toString())) {
208 | functionString += fieldBuilder;
209 | }
210 | functionString += "end\n";
211 | return functionString;
212 | }
213 |
214 | public static String getLuaClassFieldCommentString(LinkedHashMap> fieldTypes, LinkedHashMap fieldComments) {
215 | if (MapUtils.isEmpty(fieldTypes)) {
216 | return "\n";
217 | }
218 | StringBuilder fieldCommentBuilder = new StringBuilder();
219 | for (Map.Entry> entry : fieldTypes.entrySet()) {
220 | String name = entry.getKey();
221 | String type = getLuaTypeString(entry.getValue());
222 | String comment = fieldComments.get(name);
223 |
224 | // 注释
225 | fieldCommentBuilder.append("---@field ").append(name).append(" ").append(type);
226 | if (StringUtil.isNotEmpty(comment)) {
227 | fieldCommentBuilder.append(" ").append(comment);
228 | }
229 | fieldCommentBuilder.append("\n");
230 | }
231 |
232 | return fieldCommentBuilder.toString();
233 | }
234 |
235 | public static String getLuaTypeString(Class> type) {
236 | if (type == null) {
237 | return "nil";
238 | }
239 | if (type.equals(Integer.class) || type.equals(int.class)
240 | || type.equals(Long.class) || type.equals(long.class)
241 | || type.equals(Float.class) || type.equals(float.class)
242 | || type.equals(Double.class) || type.equals(double.class)
243 | || type.equals(LuaNumber.class)
244 | ) {
245 | return "number";
246 | }
247 | if (type.equals(Boolean.class) || type.equals(boolean.class) || type.equals(LuaBoolean.class)) {
248 | return "boolean";
249 | }
250 | if (type.equals(String.class) || type.equals(Character.class) || type.equals(char.class) || type.equals(LuaString.class)) {
251 | return "string";
252 | }
253 | if (type.equals(LuaTable.class)) {
254 | return "table";
255 | }
256 | if (type.equals(LuaValue.class)) {
257 | return "any";
258 | }
259 | if (type.equals(List.class)) {
260 | return "table";
261 | }
262 | if (type.isArray()) {
263 | return getLuaTypeString(type.getComponentType()) + "[]";
264 | }
265 | LuaServerLib luaServerLib = type.getAnnotation(LuaServerLib.class);
266 | if (luaServerLib != null) {
267 | return StringUtil.isEmpty(luaServerLib.className()) ? type.getSimpleName() : luaServerLib.className();
268 | } else {
269 | return type.getSimpleName();
270 | }
271 | }
272 |
273 | }
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/convertor/LuaServerLibFileConverter.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.convertor;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.hjc.lua.annotation.LuaParam;
5 | import com.hjc.lua.annotation.LuaServerLib;
6 | import com.hjc.lua.annotation.LuaServerLibFunc;
7 | import com.hjc.util.ScanUtil;
8 | import com.hjc.util.StringUtil;
9 | import org.apache.commons.collections4.CollectionUtils;
10 | import org.apache.commons.collections4.KeyValue;
11 | import org.apache.commons.collections4.keyvalue.DefaultKeyValue;
12 |
13 | import java.io.File;
14 | import java.lang.reflect.Method;
15 | import java.lang.reflect.Parameter;
16 | import java.util.LinkedHashMap;
17 | import java.util.List;
18 |
19 | /**
20 | * 生成lua调用java的静态方法的lua工具类
21 | *
22 | *
23 | * 使用方法:
24 | *
1. 把需要Lua调用的Java类加上注解{@link LuaServerLib}
25 | * 2. 把Java类中需要生成的方法加上注解{@link LuaServerLibFunc}
26 | * 3. 对方法中所有的参数加上注解{@link LuaParam}
27 | * 4.运行这个类main方法,加上VM启动参数-DluaRootPath指定lua路径
28 | *
29 | * -DluaRootPath=lua项目根路径
30 | * -DtemplateFilePath=模板文件路径(默认取框架自带模板)
31 | * -DjavaScanPackage=要扫描的Java包路径(默认com.hjc)
32 | * -DserverLibFilePath=ServerLib文件路径(默认Lib\\Server)
33 | *
34 | * 5.同时生成lua文件,并更新ServerLib.lua文件中的引用
35 | *
36 | *
37 | * @author hejincheng
38 | * @version 1.0
39 | * @date 2022/2/18 11:47
40 | **/
41 | public class LuaServerLibFileConverter extends LuaServerFileConverter {
42 |
43 | private static String templateFile = "ServerLibLuaFile.template";
44 |
45 | private static String serverLibTemplateFile = "ServerLib.template";
46 |
47 | /**
48 | * ServerLib文件名
49 | */
50 | private static final String SERVER_LIB_FILE_NAME = "ServerLib";
51 |
52 | public static void main(String[] args) {
53 | initParams();
54 | templateFile = templateFilePath + templateFile;
55 | serverLibTemplateFile = templateFilePath + serverLibTemplateFile;
56 |
57 | // 找出所有ServerLib的文件
58 | boolean findFiles = false;
59 | List> serverLibClassList = Lists.newArrayList();
60 | for (Class> javaClass : ScanUtil.getClasses(javaScanPackage)) {
61 | LuaServerLib luaServerLib = javaClass.getAnnotation(LuaServerLib.class);
62 | if (luaServerLib == null) {
63 | continue;
64 | }
65 | findFiles = true;
66 | if (luaServerLib.addToServerLib()) {
67 | serverLibClassList.add(javaClass);
68 | }
69 |
70 | String javaSimpleName = javaClass.getSimpleName();
71 | String className = luaServerLib.className();
72 | String fileDir = luaServerLib.fileDir();
73 | String comment = luaServerLib.comment();
74 |
75 | // 默认类名
76 | className = StringUtil.isEmpty(className) ? javaSimpleName : className;
77 | // 文件路径
78 | fileDir = StringUtil.isEmpty(fileDir) ? templateFilePath : luaLoadRootPath + File.separator + fileDir;
79 |
80 | // 生成lua文件
81 | generateLuaFile(javaClass, fileDir, className, comment);
82 | }
83 |
84 | if (!findFiles) {
85 | System.out.println(String.format("包目录[%s]没有找到@LuaServerLib的文件", javaScanPackage));
86 | return;
87 | }
88 |
89 | genServerLibLuaFile(serverLibClassList);
90 | }
91 |
92 | /**
93 | * 生成ServerLib.lua
94 | *
95 | * @param serverLibClassList 类list
96 | */
97 | public static void genServerLibLuaFile(List> serverLibClassList) {
98 | if (CollectionUtils.isEmpty(serverLibClassList)) {
99 | return;
100 | }
101 | StringBuilder templateBuilder = readFile(serverLibTemplateFile, SERVER_LIB_FILE_NAME);
102 | if (templateBuilder == null) {
103 | return;
104 | }
105 |
106 | StringBuilder fieldsCommentBuilder = new StringBuilder();
107 | StringBuilder fieldsBuilder = new StringBuilder();
108 |
109 | for (Class> javaClass : serverLibClassList) {
110 | LuaServerLib luaServerLib = javaClass.getAnnotation(LuaServerLib.class);
111 | String javaSimpleName = javaClass.getSimpleName();
112 | String className = luaServerLib.className();
113 | String fieldName = luaServerLib.fieldName();
114 | // 默认类名
115 | className = StringUtil.isEmpty(className) ? javaSimpleName : className;
116 | // 默认类名首字母小写
117 | fieldName = StringUtil.isEmpty(fieldName) ? javaSimpleName.substring(0, 1).toLowerCase() + javaSimpleName.substring(1) : fieldName;
118 |
119 | fieldsCommentBuilder.append("---@field ").append(fieldName).append(" ").append(className).append("\n");
120 | fieldsBuilder.append(" self.").append(fieldName).append(" = ").append("luajava.bindClass(\"").append(javaClass.getName()).append("\")").append("\n");
121 | }
122 |
123 | String luaFile = templateBuilder.toString().replace("${SELF_FIELDS}", fieldsBuilder.toString());
124 | luaFile = luaFile.replace("${FIELDS_COMMENT}", fieldsCommentBuilder.toString());
125 |
126 | // 写文件
127 | writeFile(luaFile, luaLoadRootPath + File.separator + serverLibFilePath + File.separator + SERVER_LIB_FILE_NAME + ".lua");
128 | }
129 |
130 | /**
131 | * 生成类lua文件
132 | *
133 | * @param javaClz
134 | * @param fileDir
135 | * @param className
136 | * @param comment
137 | */
138 | public static void generateLuaFile(Class> javaClz, String fileDir, String className, String comment) {
139 | StringBuilder templateBuilder = readFile(templateFile, className);
140 | if (templateBuilder == null) {
141 | return;
142 | }
143 |
144 | String template = templateBuilder.toString();
145 |
146 | Class> superClass = javaClz.getSuperclass();
147 | LuaServerLib superLib = superClass.getAnnotation(LuaServerLib.class);
148 | if (superLib != null) {
149 | String superClassName = superLib.className();
150 | superClassName = StringUtil.isEmpty(superClassName) ? superClass.getSimpleName() : superClassName;
151 |
152 | template = template.replace("${SUPER_CLASS}", superClassName);
153 | template = template.replace("${SUPER_CLASS_COMMENT}", superClassName);
154 | } else {
155 | template = template.replace("${SUPER_CLASS}", "nil");
156 | template = template.replace("${SUPER_CLASS_COMMENT}", "table");
157 | }
158 |
159 | // 注释
160 | if (!StringUtil.isEmpty(comment)) {
161 | template = template.replace("${COMMENT}", "---\n--- " + comment + "\n");
162 | } else {
163 | template = template.replace("${COMMENT}", "");
164 | }
165 |
166 | // 方法
167 | StringBuilder functionBuilder = new StringBuilder();
168 | for (Method method : javaClz.getDeclaredMethods()) {
169 | LuaServerLibFunc luaServerLibFunc = method.getAnnotation(LuaServerLibFunc.class);
170 | if (luaServerLibFunc == null) {
171 | continue;
172 | }
173 |
174 | System.out.println("method : \t" + method.getName());
175 |
176 | LinkedHashMap, String>> paramType = new LinkedHashMap<>();
177 | for (Parameter parameter : method.getParameters()) {
178 | LuaParam luaParam = parameter.getAnnotation(LuaParam.class);
179 | String parName = parameter.getName();
180 | String parComment = "";
181 | Class> parType = parameter.getType();
182 | if (luaParam != null) {
183 | parName = luaParam.value();
184 | parComment = luaParam.comment();
185 | }
186 | DefaultKeyValue, String> value = new DefaultKeyValue<>(parType, parComment);
187 | paramType.put(parName, value);
188 | }
189 | String function = getLuaFunctionString(className, method.getName(), paramType, method.getReturnType(), luaServerLibFunc.comment());
190 | functionBuilder.append(function);
191 | functionBuilder.append("\n");
192 | }
193 | String luaFile = template.replace("${FUNCTIONS}", functionBuilder.toString());
194 |
195 | String luaFilePath = fileDir + File.separator + className + ".lua";
196 | // 写文件
197 | writeFile(luaFile, luaFilePath);
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/convertor/LuaServerModelFileConverter.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.convertor;
2 |
3 | import com.google.common.collect.Lists;
4 | import com.google.common.collect.Sets;
5 | import com.hjc.lua.annotation.LuaParam;
6 | import com.hjc.lua.annotation.LuaServerModel;
7 | import com.hjc.util.ScanUtil;
8 | import com.hjc.util.StringUtil;
9 |
10 | import java.io.File;
11 | import java.lang.reflect.Array;
12 | import java.lang.reflect.Field;
13 | import java.lang.reflect.ParameterizedType;
14 | import java.lang.reflect.Type;
15 | import java.util.*;
16 |
17 | /**
18 | * 生成javaBean转换lua文件
19 | *
20 | *
21 | * 使用方法:
22 | *
1. 把需要转换的JavaBean类加上注解{@link LuaServerModel}
23 | * 2.运行这个类方法,加上VM启动参数-DluaRootPath指定lua路径
24 | *
25 | * -DluaRootPath=lua项目根路径
26 | * -DtemplateFilePath=模板文件路径(默认取框架自带模板)
27 | * -DjavaScanPackage=要扫描的Java包路径(默认com.hjc)
28 | *
29 | * 3.同时生成lua文件,并更新ServerLib.lua文件中的引用
30 | *
31 | *
32 | * @author hejincheng
33 | * @version 1.0
34 | * @date 2022/2/18 11:47
35 | **/
36 | public class LuaServerModelFileConverter extends LuaServerFileConverter {
37 |
38 | private static String templateFile = "ServerModelLuaFile.template";
39 |
40 | /**
41 | * 忽略生成文件的类
42 | */
43 | private static final Set> IGNORE_GENERATE_TYPE = Sets.newHashSet(Integer.class, Float.class, Double.class, String.class, Boolean.class);
44 |
45 | public static void main(String[] args) {
46 | initParams();
47 | templateFile = templateFilePath + File.separator + templateFile;
48 |
49 | // 找出所有ServerModel的文件
50 | boolean findFiles = false;
51 | for (Class> javaClass : ScanUtil.getClasses(javaScanPackage)) {
52 | LuaServerModel luaServerModel = javaClass.getAnnotation(LuaServerModel.class);
53 | if (luaServerModel == null) {
54 | continue;
55 | }
56 | findFiles = true;
57 |
58 | String javaSimpleName = javaClass.getSimpleName();
59 | String className = luaServerModel.className();
60 | String fileDir = luaServerModel.fileDir();
61 | String comment = luaServerModel.comment();
62 |
63 | // 默认类名
64 | className = StringUtil.isEmpty(className) ? javaSimpleName : className;
65 | // 文件路径
66 | fileDir = StringUtil.isEmpty(fileDir) ? templateFilePath : luaLoadRootPath + File.separator + fileDir;
67 |
68 | // 生成lua文件
69 | generateLuaFile(javaClass, fileDir, className, comment);
70 | }
71 | if (!findFiles) {
72 | System.out.println(String.format("包目录[%s]没有找到@LuaServerModel的文件", javaScanPackage));
73 | }
74 | }
75 |
76 | public static void generateLuaFile(Class> javaClz, String fileDir, String className, String comment) {
77 | if (IGNORE_GENERATE_TYPE.contains(javaClz)) {
78 | return;
79 | }
80 | StringBuilder templateBuilder = readFile(templateFile, className);
81 | if (templateBuilder == null) {
82 | return;
83 | }
84 | String template = templateBuilder.toString();
85 |
86 | LinkedHashMap> fieldTypes = new LinkedHashMap<>();
87 | LinkedHashMap fieldComments = new LinkedHashMap<>();
88 |
89 | List declaredFieldList = Lists.newArrayList(javaClz.getDeclaredFields());
90 | Class> superClz = javaClz.getSuperclass();
91 |
92 | if (superClz != null && superClz != Object.class) {
93 | declaredFieldList.addAll(Arrays.asList(superClz.getDeclaredFields()));
94 | }
95 |
96 | Class> superClass = javaClz.getSuperclass();
97 | LuaServerModel superModel = superClass.getAnnotation(LuaServerModel.class);
98 | if (superModel != null) {
99 | String superClassName = superModel.className();
100 | superClassName = StringUtil.isEmpty(superClassName) ? superClass.getSimpleName() : superClassName;
101 |
102 | template = template.replace("${SUPER_CLASS}", superClassName);
103 | template = template.replace("${SUPER_CLASS_COMMENT}", superClassName);
104 | } else {
105 | template = template.replace("${SUPER_CLASS}", "nil");
106 | template = template.replace("${SUPER_CLASS_COMMENT}", "table");
107 | }
108 |
109 | // 注释
110 | if (!StringUtil.isEmpty(comment)) {
111 | template = template.replace("${COMMENT}", "---\n--- " + comment + "\n");
112 | } else {
113 | template = template.replace("${COMMENT}", "");
114 | }
115 |
116 | // 属性
117 | for (Field declaredField : declaredFieldList) {
118 |
119 | String fieldName = declaredField.getName();
120 | String fieldComment = null;
121 | Class> fieldType = declaredField.getType();
122 |
123 | LuaParam luaParam = declaredField.getAnnotation(LuaParam.class);
124 | if (luaParam != null) {
125 | if (StringUtil.isNotEmpty(luaParam.value())) {
126 | fieldName = luaParam.value();
127 | }
128 | if (StringUtil.isNotEmpty(luaParam.comment())) {
129 | fieldComment = luaParam.comment();
130 | }
131 | }
132 |
133 | if (List.class.isAssignableFrom(declaredField.getType())) {
134 | // List类型
135 | Type ft = declaredField.getGenericType();
136 | Class> subClz = (Class>) ((ParameterizedType) ft).getActualTypeArguments()[0];
137 | Object tmpObj = Array.newInstance(subClz, 0);
138 | fieldType = tmpObj.getClass();
139 | } else if (Map.class.isAssignableFrom(declaredField.getType())) {
140 | // Map类型
141 | Type ft = declaredField.getGenericType();
142 | Class> subClz = (Class>) ((ParameterizedType) ft).getActualTypeArguments()[1];
143 | Object tmpObj = Array.newInstance(subClz, 0);
144 | fieldType = tmpObj.getClass();
145 | }
146 |
147 | fieldTypes.put(fieldName, fieldType);
148 | fieldComments.put(fieldName, fieldComment);
149 | }
150 | String ctorFunction = getLuaCtorFunctionString(className, fieldTypes);
151 |
152 | String fieldsComment = getLuaClassFieldCommentString(fieldTypes, fieldComments);
153 | String luaFile = template.replace("${CTOR_FUNCTION}", ctorFunction);
154 | luaFile = luaFile.replace("${FILEDS_COMMENTS}", fieldsComment);
155 |
156 | String luaFilePath = fileDir + File.separator + className + ".lua";
157 | writeFile(luaFile, luaFilePath);
158 | }
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/engine/ILuaJEngine.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.engine;
2 |
3 | import org.luaj.vm2.Globals;
4 | import org.luaj.vm2.LuaFunction;
5 | import org.luaj.vm2.LuaTable;
6 | import org.luaj.vm2.LuaValue;
7 | import org.luaj.vm2.Varargs;
8 |
9 | import java.io.File;
10 | import java.util.List;
11 |
12 | /**
13 | * LuaJ引擎接口
14 | *
15 | * @ClassName ILuaJEngine
16 | * @Author hejincheng
17 | * @Date 2021/12/8 17:58
18 | * @Version 1.0
19 | **/
20 | public interface ILuaJEngine {
21 |
22 | /**
23 | * 创建Lua全局对象
24 | *
25 | * @return Lua全局对象
26 | */
27 | Globals createLuaGlobals();
28 |
29 | /**
30 | * 创建Lua全局对象,并加载文件
31 | *
32 | * @param loadPackagePath 加载包目录
33 | * @param preScript 预处理脚本
34 | * @param loadFilesPath 加载文件
35 | * @return Lua全局对象
36 | */
37 | Globals createLuaGlobals(String loadPackagePath, String preScript, String... loadFilesPath);
38 |
39 | /**
40 | * 创建Lua全局对象,并加载文件
41 | *
42 | * @param loadPackagePath 加载包目录
43 | * @param preScript 预处理脚本
44 | * @param loadFileList 加载文件list
45 | * @return Lua全局对象
46 | */
47 | Globals createLuaGlobals(String loadPackagePath, String preScript, List loadFileList);
48 |
49 | /**
50 | * 初始化Globals
51 | *
52 | * @param preScript 预处理脚本
53 | * @param loadFilesPath 加载文件list
54 | * @param globals Globals
55 | * @return 初始成功
56 | */
57 | boolean initGlobals(String preScript, String[] loadFilesPath, Globals globals);
58 |
59 | /**
60 | * 初始化Globals
61 | *
62 | * @param preScript 预处理脚本
63 | * @param loadFileList 加载文件list
64 | * @param globals Globals
65 | * @return 初始成功
66 | */
67 | boolean initGlobals(String preScript, List loadFileList, Globals globals);
68 |
69 | /**
70 | * 打印globals
71 | *
72 | * @param globals lua全局对象
73 | */
74 | void logGlobals(Globals globals);
75 |
76 | /**
77 | * 获取Lua方法
78 | *
79 | * @param luaModule Lua全局对象
80 | * @param funcName 方法名
81 | * @return Lua方法
82 | */
83 | LuaFunction getLuaFunction(LuaValue luaModule, String funcName);
84 |
85 | /**
86 | * 获取Lua对象
87 | *
88 | * @param luaObj Lua全局对象
89 | * @param objName 对象名
90 | * @return Lua对象
91 | */
92 | LuaValue getLuaObj(LuaValue luaObj, String objName);
93 |
94 | /**
95 | * 调用字符串脚本
96 | *
97 | * @param globals Lua全局对象
98 | * @param script 脚本
99 | * @return 脚本返回值
100 | */
101 | LuaValue callScript(Globals globals, String script);
102 |
103 | /**
104 | * 调用lua方法
105 | * 原始调用:无任何封装和规范的方法,可自行发挥
106 | *
107 | * @param globals 全局对象
108 | * @param luaModel lua模块
109 | * @param varargs 参数
110 | * @return {@link Varargs}
111 | */
112 | Varargs invokeLua(Globals globals, String luaModel, Varargs varargs);
113 |
114 | /**
115 | * 调用lua方法
116 | * 原始调用:无任何封装和规范的方法,可自行发挥
117 | *
118 | * @param luaValue lua对象
119 | * @param varargs 参数
120 | * @return {@link Varargs}
121 | */
122 | Varargs invokeLua(LuaValue luaValue, Varargs varargs);
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/engine/LuaJCmdEnum.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.engine;
2 |
3 | import com.hjc.util.enumutil.EnumUtil;
4 | import com.hjc.util.enumutil.IndexedEnum;
5 | import lombok.Getter;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * LuaJ中默认环境支持的调用命令
11 | *
12 | * @ClassName LuaJCmdEnum
13 | * @Author hejincheng
14 | * @Date 2021/12/7 17:38
15 | * @Version 1.0
16 | **/
17 | public enum LuaJCmdEnum implements IndexedEnum {
18 | _G(0, "_G"),
19 | _VERSION(1, "_VERSION"),
20 | ASSERT(2, "assert"),
21 | COLLECT_GARBAGE(3, "collectgarbage"),
22 | DO_FILE(4, "dofile"),
23 | ERROR(5, "error"),
24 | GET_META_TABLE(6, "getmetatable"),
25 | LOAD(7, "load"),
26 | LOAD_FILE(8, "loadfile"),
27 | PCALL(9, "pcall"),
28 | PRINT(10, "print"),
29 | RAW_EQUAL(11, "rawequal"),
30 | RAW_GET(12, "rawget"),
31 | RAW_LEN(13, "rawlen"),
32 | RAW_SET(14, "rawset"),
33 | SELECT(15, "select"),
34 | SET_META_TABLE(16, "setmetatable"),
35 | TO_NUMBER(17, "tonumber"),
36 | TO_STRING(18, "tostring"),
37 | TYPE(19, "type"),
38 | XPCALL(20, "xpcall"),
39 | NEXT(21, "next"),
40 | PAIRS(22, "pairs"),
41 | IPAIRS(23, "ipairs"),
42 | ;
43 |
44 | private int type;
45 | @Getter
46 | private String cmd;
47 |
48 | LuaJCmdEnum(int type, String cmd) {
49 | this.type = type;
50 | this.cmd = cmd;
51 | }
52 |
53 | @Override
54 | public int getIndex() {
55 | return this.type;
56 | }
57 |
58 | private static final List values = IndexedEnumUtil.toIndexes(LuaJCmdEnum.values());
59 |
60 | public static List getValues() {
61 | return values;
62 | }
63 |
64 | public static LuaJCmdEnum valueOf(int value) {
65 | return EnumUtil.valueOf(values, value);
66 | }
67 | }
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/engine/LuaJEngine.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.engine;
2 |
3 | import com.hjc.lua.LuaBattlePlatform;
4 | import com.hjc.lua.exception.LuaException;
5 | import com.hjc.lua.log.LuaLogTool;
6 | import com.hjc.util.StringUtil;
7 | import org.apache.logging.log4j.LogManager;
8 | import org.apache.logging.log4j.Logger;
9 | import org.luaj.vm2.*;
10 |
11 | import java.io.File;
12 | import java.util.List;
13 |
14 | /**
15 | * luaj引擎实现
16 | *
17 | * @ClassName LuaJEngine
18 | * @Description
19 | * @Author hejincheng
20 | * @Date 2021/12/7 17:25
21 | * @Version 1.0
22 | **/
23 | public class LuaJEngine implements ILuaJEngine {
24 |
25 | public static Logger log = LogManager.getLogger(LuaJEngine.class);
26 |
27 | @Override
28 | public Globals createLuaGlobals() {
29 | return createLuaGlobals(null, null);
30 | }
31 |
32 | @Override
33 | public Globals createLuaGlobals(String loadPackagePath, String preScript, String... loadFilesPath) {
34 | Globals globals = LuaBattlePlatform.buildGlobals(loadPackagePath);
35 | if (!initGlobals(preScript, loadFilesPath, globals)) {
36 | return null;
37 | }
38 | return globals;
39 | }
40 |
41 | @Override
42 | public Globals createLuaGlobals(String loadPackagePath, String preScript, List loadFileList) {
43 | Globals globals = LuaBattlePlatform.buildGlobals(loadPackagePath);
44 | if (!initGlobals(preScript, loadFileList, globals)) {
45 | return null;
46 | }
47 | return globals;
48 | }
49 |
50 | @Override
51 | public boolean initGlobals(String preScript, String[] loadFilesPath, Globals globals) {
52 | if (!StringUtil.isEmpty(preScript)) {
53 | this.callScript(globals, preScript);
54 | }
55 | for (String filePath : loadFilesPath) {
56 | if (!loadFile(filePath, globals)) {
57 | return false;
58 | }
59 | }
60 | return true;
61 | }
62 |
63 | @Override
64 | public boolean initGlobals(String preScript, List loadFileList, Globals globals) {
65 | if (!StringUtil.isEmpty(preScript)) {
66 | this.callScript(globals, preScript);
67 | }
68 | for (File file : loadFileList) {
69 | if (!loadFile(file.getAbsolutePath(), globals)) {
70 | return false;
71 | }
72 | }
73 | return true;
74 | }
75 |
76 | private boolean loadFile(String loadFilePath, Globals globals) {
77 | if (!StringUtil.isEmpty(loadFilePath)) {
78 | // load文件
79 | LuaValue doFile = getLuaObj(globals, LuaJCmdEnum.DO_FILE.getCmd());
80 | LuaValue loadFile = LuaValue.valueOf(loadFilePath);
81 | // call调用会执行一遍lua文件,并加载所有function
82 | try {
83 | synchronized (this) {
84 | doFile.call(loadFile);
85 | }
86 | } catch (Exception e) {
87 | log.error(String.format("LuaEngine.load.file.[%s].err", loadFilePath), e);
88 | return false;
89 | }
90 | }
91 | return true;
92 | }
93 |
94 | @Override
95 | public void logGlobals(Globals globals) {
96 | log.info(String.format("#%s.print.load.values...", this.getClass().getSimpleName()));
97 | for (LuaValue luaKey : globals.keys()) {
98 | log.info(luaKey + "\t:\t" + globals.get(luaKey));
99 | }
100 | }
101 |
102 | @Override
103 | public LuaFunction getLuaFunction(LuaValue luaModule, String funcName) {
104 | LuaValue luaValue = getLuaObj(luaModule, funcName);
105 | if (luaValue == LuaValue.NIL) {
106 | return null;
107 | }
108 | if (!luaValue.isfunction()) {
109 | return null;
110 | }
111 | return (LuaFunction) luaValue;
112 | }
113 |
114 | @Override
115 | public LuaValue getLuaObj(LuaValue luaObj, String objName) {
116 | if (luaObj == null || luaObj == LuaValue.NIL) {
117 | return LuaValue.NIL;
118 | }
119 | return luaObj.get(objName);
120 | }
121 |
122 | @Override
123 | public LuaValue callScript(Globals globals, String script) {
124 | if (globals == null) {
125 | log.error("", new LuaException("call script globals null"));
126 | return null;
127 | }
128 | if (StringUtil.isEmpty(script)) {
129 | log.error("", new LuaException("call script null"));
130 | return null;
131 | }
132 | LuaValue luaScript = globals.load(script);
133 | return luaScript.call();
134 | }
135 |
136 | @Override
137 | public Varargs invokeLua(Globals globals, String luaModel, Varargs varargs) {
138 | if (globals == null) {
139 | globals = createLuaGlobals();
140 | }
141 | LuaValue luaValue = getLuaObj(globals, luaModel);
142 | if (luaValue == null) {
143 | return null;
144 | }
145 | try {
146 | return luaValue.invoke(varargs);
147 | } catch (org.luaj.vm2.LuaError luaError) {
148 | log.error(LuaLogTool.traceback(luaError, luaError.getMessage()));
149 | return null;
150 | }
151 | }
152 |
153 | @Override
154 | public Varargs invokeLua(LuaValue luaValue, Varargs varargs) {
155 | if (luaValue == null) {
156 | log.error("invoke.err", new LuaException("invoke luaFunction null"));
157 | return null;
158 | }
159 | try {
160 | if (varargs == null) {
161 | return luaValue.invoke();
162 | }
163 | return luaValue.invoke(varargs);
164 | } catch (Exception luaError) {
165 | log.error(LuaLogTool.traceback(luaError, luaError.getMessage()));
166 | return null;
167 | }
168 | }
169 |
170 | }
171 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/exception/LuaException.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.exception;
2 |
3 | /**
4 | * @ClassName LuaException
5 | * @Description
6 | * @Author hejincheng
7 | * @Date 2021/12/8 18:04
8 | * @Version 1.0
9 | **/
10 | public class LuaException extends Exception {
11 |
12 | public LuaException(String msg) {
13 | super(msg);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/log/LuaLogLevel.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.log;
2 |
3 | import com.hjc.util.enumutil.EnumUtil;
4 | import com.hjc.util.enumutil.IndexedEnum;
5 | import lombok.Getter;
6 | import org.apache.logging.log4j.Level;
7 | import org.slf4j.Logger;
8 |
9 | import java.util.List;
10 |
11 | /**
12 | * lua使用的log等级
13 | *
14 | * @ClassName LuaLogLevel
15 | * @Author hejincheng
16 | * @Date 2021/12/16 19:03
17 | * @Version 1.0
18 | **/
19 | public enum LuaLogLevel implements IndexedEnum {
20 | // info
21 | INFO(1) {
22 | @Override
23 | public void log(Logger logger, String msg) {
24 | logger.info(msg);
25 | }
26 |
27 | @Override
28 | public boolean isEnabled(Logger logger) {
29 | return logger.isInfoEnabled();
30 | }
31 | },
32 | // warn
33 | WARN(2) {
34 | @Override
35 | public void log(Logger logger, String msg) {
36 | logger.warn(msg);
37 | }
38 |
39 | @Override
40 | public boolean isEnabled(Logger logger) {
41 | return logger.isWarnEnabled();
42 | }
43 | },
44 | DEBUG(3) {
45 | @Override
46 | public void log(Logger logger, String msg) {
47 | logger.debug(msg);
48 | }
49 |
50 | @Override
51 | public boolean isEnabled(Logger logger) {
52 | return logger.isDebugEnabled();
53 | }
54 | },
55 | // error
56 | ERROR(4) {
57 | @Override
58 | public void log(Logger logger, String msg) {
59 | logger.error(msg);
60 | }
61 |
62 | @Override
63 | public boolean isEnabled(Logger logger) {
64 | return logger.isErrorEnabled();
65 | }
66 | },
67 | ;
68 |
69 | private final int type;
70 |
71 | LuaLogLevel(int type) {
72 | this.type = type;
73 | }
74 |
75 | @Override
76 | public int getIndex() {
77 | return this.type;
78 | }
79 |
80 | public abstract void log(Logger logger, String msg);
81 |
82 | public abstract boolean isEnabled(Logger logger);
83 |
84 | private static final List values = IndexedEnumUtil.toIndexes(LuaLogLevel.values());
85 |
86 | public static List getValues() {
87 | return values;
88 | }
89 |
90 | public static LuaLogLevel valueOf(int value) {
91 | return EnumUtil.valueOf(values, value);
92 | }
93 | }
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/log/LuaLogTool.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.log;
2 |
3 | import com.hjc.lua.annotation.LuaParam;
4 | import com.hjc.lua.annotation.LuaServerLib;
5 | import com.hjc.lua.annotation.LuaServerLibFunc;
6 | import org.apache.logging.log4j.Level;
7 | import org.luaj.vm2.LuaBoolean;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | import java.io.PrintWriter;
12 | import java.io.StringWriter;
13 |
14 | /**
15 | * @ClassName LuaDebugTool
16 | * @Description
17 | * @Author hejincheng
18 | * @Date 2021/12/16 11:54
19 | * @Version 1.0
20 | **/
21 | @LuaServerLib(fieldName = "logTool", className = "ServerLogTool", fileDir = "lib")
22 | public class LuaLogTool {
23 |
24 | private static Logger log = LoggerFactory.getLogger(LuaLogTool.class);
25 |
26 | /**
27 | * 过滤堆栈中结尾信息
28 | */
29 | private static final String[] FILTER_END_TRACES = new String[]{
30 | "(Unknown Source)",
31 | "(Native Method)",
32 | ".java"
33 | };
34 |
35 | private static final String JAVA_EXT = ".java:";
36 |
37 | @LuaServerLibFunc(comment = "获取堆栈", returnComment = "堆栈信息")
38 | public static String traceback(@LuaParam("log") String msg) {
39 | LuaTracebackException exception = new LuaTracebackException();
40 | return traceback(exception, msg);
41 | }
42 |
43 | /**
44 | * 获取堆栈堆栈
45 | *
46 | * @param msg
47 | * @return
48 | */
49 | public static String traceback(Exception exception, String msg) {
50 | StringWriter stringWriter = new StringWriter();
51 | PrintWriter printWriter = new PrintWriter(stringWriter);
52 | // 获取堆栈信息
53 | StackTraceElement[] stackTraceElements = exception.getStackTrace();
54 | if (stackTraceElements != null) {
55 | printWriter.println(msg);
56 | for (StackTraceElement element : stackTraceElements) {
57 | String traceString = element.toString();
58 | boolean isValid = true;
59 | for (String filterTrace : FILTER_END_TRACES) {
60 | if (traceString.contains(filterTrace)) {
61 | // 过滤掉无用信息
62 | isValid = false;
63 | break;
64 | }
65 | }
66 | if (!isValid) {
67 | continue;
68 | }
69 | if (traceString.contains("(")) {
70 | printWriter.println("\tat " + traceString.substring(traceString.indexOf("(")));
71 | } else {
72 | printWriter.println("\tat " + traceString);
73 | }
74 | }
75 | }
76 | return stringWriter.toString();
77 | }
78 |
79 | /**
80 | * 打印log
81 | *
82 | * @param logLevel log级别
83 | * @param msg log消息
84 | */
85 | @LuaServerLibFunc(comment = "打印log")
86 | public static void log(@LuaParam(value = "logLevel", comment = "log级别") int logLevel,
87 | @LuaParam(value = "msg", comment = "log消息") String msg) {
88 | LuaLogLevel luaLogLevel = LuaLogLevel.valueOf(logLevel);
89 | if (luaLogLevel == null) {
90 | luaLogLevel = LuaLogLevel.INFO;
91 | }
92 | luaLogLevel.log(log, msg);
93 | }
94 |
95 | /**
96 | * log级别是否可用
97 | *
98 | * @param logLevel log级别
99 | * @return log级别是否可用
100 | */
101 | @LuaServerLibFunc(comment = "log级别是否可用", returnComment = "log级别是否可用")
102 | public static LuaBoolean isLogEnabled(@LuaParam(value = "logLevel", comment = "log级别") int logLevel) {
103 | LuaLogLevel luaLogLevel = LuaLogLevel.valueOf(logLevel);
104 | if (luaLogLevel == null) {
105 | return LuaBoolean.valueOf(false);
106 | }
107 | return LuaBoolean.valueOf(luaLogLevel.isEnabled(log));
108 | }
109 |
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/lua/log/LuaTracebackException.java:
--------------------------------------------------------------------------------
1 | package com.hjc.lua.log;
2 |
3 | /**
4 | * lua堆栈
5 | *
6 | * @ClassName LuaTraceback
7 | * @Description
8 | * @Author hejincheng
9 | * @Date 2021/12/16 12:09
10 | * @Version 1.0
11 | **/
12 | public class LuaTracebackException extends Exception {
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/Assert.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util;
2 |
3 | /**
4 | * 断言工具类,用于对方法的传入参数进行校验,如果未通过则
5 | * 抛出IllegalArgumentException
异常
6 | *
7 | * @ClassName Assert
8 | * @Author hejincheng
9 | * @Date 2021/9/18 18:51
10 | * @Version 1.0
11 | **/
12 | public abstract class Assert {
13 | /**
14 | * 断言对象不为空
15 | *
16 | * @param obj
17 | */
18 | public static void notNull(Object obj) {
19 | if (obj == null) {
20 | notNull(obj, null);
21 | }
22 | }
23 |
24 | /**
25 | * 断言对象不为空
26 | *
27 | * @param obj
28 | */
29 | public static void notNull(Object obj, String msg) {
30 | if (obj == null) {
31 | throw new IllegalArgumentException(msg);
32 | }
33 | }
34 |
35 | /**
36 | * 断言表达式为真
37 | *
38 | * @param expression
39 | */
40 | public static void isTrue(boolean expression) {
41 | if (!expression) {
42 | isTrue(expression, null);
43 | }
44 | }
45 |
46 | /**
47 | * 断言表达式为真
48 | *
49 | * @param expression
50 | */
51 | public static void isTrue(boolean expression, String msg) {
52 | if (!expression) {
53 | throw new IllegalArgumentException(msg);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/FileUtil.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util;
2 |
3 | import java.io.*;
4 | import java.net.URL;
5 | import java.util.ArrayList;
6 | import java.util.Collections;
7 | import java.util.List;
8 | import java.util.Objects;
9 |
10 | public class FileUtil {
11 |
12 | public static byte[] getFileBytes(String path) throws Exception {
13 |
14 | File file = new File(path);
15 | if (!file.exists()) {
16 | return null;
17 | }
18 |
19 | byte[] bytes = null;
20 | try (FileInputStream inputStream = new FileInputStream(file);
21 | DataInputStream dataInputStream = new DataInputStream(inputStream);) {
22 | bytes = new byte[dataInputStream.available()];
23 | dataInputStream.read(bytes, 0, bytes.length);
24 | }
25 | return bytes;
26 | }
27 |
28 | public static String getFileContent(String path) throws Exception {
29 | return getFileContent(path, "utf-8");
30 | }
31 |
32 | public static String getFileContent(String path, String encoding) throws Exception {
33 | StringBuilder result = new StringBuilder();
34 | File file = new File(path);
35 | if (!file.exists()) {
36 | return "";
37 | }
38 | try (InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);
39 | BufferedReader bufferedReader = new BufferedReader(read);) {
40 | String s = "";
41 | while ((s = bufferedReader.readLine()) != null) {
42 | result.append(s);
43 | }
44 | }
45 | return result.toString();
46 | }
47 |
48 | /**
49 | * 每行读取文件内容
50 | *
51 | * @param path
52 | * @return
53 | * @throws Exception
54 | */
55 | public static List getFileLinesContent(String path) throws Exception {
56 | List contents = new ArrayList<>();
57 | File file = new File(path);
58 | if (!file.exists()) {
59 | return contents;
60 | }
61 |
62 | try (BufferedReader bufferedReader = new BufferedReader(new FileReader(path))) {
63 | String s = "";
64 | while ((s = bufferedReader.readLine()) != null) {
65 | contents.add(s);
66 | }
67 | }
68 | return contents;
69 | }
70 |
71 | public static List getFileLinesContent(String path, String encoding) throws Exception {
72 | List contents = new ArrayList<>();
73 | File file = new File(path);
74 | if (!file.exists()) {
75 | return contents;
76 | }
77 | InputStreamReader read = new InputStreamReader(new FileInputStream(file), encoding);
78 | BufferedReader bufferedReader = new BufferedReader(read);
79 |
80 | String s = "";
81 | while ((s = bufferedReader.readLine()) != null) {
82 | contents.add(s);
83 | }
84 | bufferedReader.close();
85 | return contents;
86 | }
87 |
88 | public static String writeFileContent(String path, String content) throws Exception {
89 | StringBuilder result = new StringBuilder();
90 | File file = new File(path);
91 | if (!file.exists()) {
92 | file.createNewFile();
93 | } else {
94 | file.delete();
95 | file.createNewFile();
96 | }
97 |
98 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(path))) {
99 | writer.write(content);
100 | writer.flush();
101 | }
102 |
103 | return result.toString();
104 | }
105 |
106 | /**
107 | * 按行向文件中写数据
108 | *
109 | * @param fileName
110 | * @param sqlStr
111 | * @param isAppend 是否追加写
112 | */
113 | public static void writeLineToFile(String fileName, String sqlStr, boolean isAppend) throws IOException {
114 | File file = new File(fileName);
115 | if (!file.exists()) {
116 | file.createNewFile(); // 不存在,创建
117 | }
118 | try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, isAppend));) {
119 | writer.write(sqlStr + "\r\n");
120 | writer.flush();
121 | } catch (IOException e) {
122 | e.printStackTrace();
123 | }
124 | }
125 |
126 | /**
127 | * 获取目录下的所有文件
128 | *
129 | * @param dirPath 目录路径
130 | * @param expectExt 期望后缀,查找全部文件(不要求后缀)可传null
131 | * @param isRecursion 是否递归子目录查找
132 | * @return dirPath下的所有文件
133 | */
134 | public static List getAllFileInDir(String dirPath, String expectExt, boolean isRecursion) {
135 | File dirFile = new File(dirPath);
136 | if (!dirFile.exists()) {
137 | // 根目录就不存在
138 | return Collections.emptyList();
139 | }
140 | if (!dirFile.isDirectory()) {
141 | // 传进来的不是一个目录
142 | return Collections.emptyList();
143 | }
144 | List fileList = new ArrayList<>();
145 | getFileInDir(fileList, dirFile, expectExt, isRecursion);
146 | return fileList;
147 | }
148 |
149 | private static void getFileInDir(List fileList, File dirFile, String expectExt, boolean isRecursion) {
150 | for (File listFile : Objects.requireNonNull(dirFile.listFiles())) {
151 | File file = listFile.getAbsoluteFile();
152 | if (!file.exists()) {
153 | continue;
154 | }
155 | if (file.isFile()) {
156 | // 找到文件
157 | if (!StringUtil.isEmpty(expectExt)) {
158 | String suffix = getFileExt(file);
159 | if (expectExt.equals(suffix)) {
160 | fileList.add(file);
161 | }
162 | } else {
163 | // 不要求后缀
164 | fileList.add(file);
165 | }
166 | continue;
167 | }
168 | if (isRecursion && file.isDirectory()) {
169 | // 是目录:如果需要递归,递归继续查找
170 | getFileInDir(fileList, file, expectExt, isRecursion);
171 | }
172 | }
173 | }
174 |
175 | private static String getFileExt(File file) {
176 | // 要求特定后缀文件
177 | int lastIndexOf = file.getAbsolutePath().lastIndexOf(".");
178 | //获取文件的后缀
179 | return file.getAbsolutePath().substring(lastIndexOf);
180 | }
181 |
182 | /**
183 | * 找到特定文件
184 | *
185 | * @param filePath 文件路径
186 | * @return 特定文件
187 | */
188 | public static File findFile(String filePath) {
189 | return findFile(filePath, null);
190 | }
191 |
192 | /**
193 | * 找到特定文件
194 | *
195 | * @param filePath 文件路径
196 | * @param ext 文件后缀
197 | * @return 特定文件
198 | */
199 | public static File findFile(String filePath, String ext) {
200 | File file = new File(filePath);
201 | if (!file.exists()) {
202 | return null;
203 | }
204 | if (!file.isFile()) {
205 | return null;
206 | }
207 | if (!StringUtil.isEmpty(ext) && !ext.equals(getFileExt(file))) {
208 | return null;
209 | }
210 | return file;
211 | }
212 |
213 | /**
214 | * 获取项目根路径
215 | *
216 | * @return 项目根路径
217 | */
218 | public static String getRootPath() {
219 | URL url = ClassLoader.getSystemClassLoader().getResource("");
220 | if (url == null) {
221 | return StringUtil.EMPTY;
222 | }
223 | return url.getPath();
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/NamedThreadFactory.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util;
2 |
3 | import java.util.concurrent.Executors;
4 | import java.util.concurrent.ThreadFactory;
5 | import java.util.concurrent.atomic.AtomicInteger;
6 |
7 | /**
8 | * 实现一个可以命名的ThreadFactory,以易于定位线程池,参照{@link Executors#defaultThreadFactory()}
9 | *
10 | * @author hejincheng
11 | * @since 2017-1-18
12 | */
13 | public class NamedThreadFactory implements ThreadFactory {
14 |
15 | static final AtomicInteger poolNumber = new AtomicInteger(1);
16 | final ThreadGroup group;
17 | final AtomicInteger threadNumber = new AtomicInteger(1);
18 | final String namePrefix;
19 |
20 | public NamedThreadFactory(Class> clazz) {
21 | // 根据创建类的类名来命名线程池中线程名字的前缀
22 | this(clazz == null ? "pool-" + poolNumber.getAndIncrement() : clazz.getSimpleName());
23 | }
24 |
25 | public NamedThreadFactory(String name) {
26 | SecurityManager s = System.getSecurityManager();
27 | group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
28 | namePrefix = name + "-thread-";
29 | }
30 |
31 | @Override
32 | public Thread newThread(Runnable r) {
33 | Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
34 | if (t.isDaemon()) {
35 | t.setDaemon(false);
36 | }
37 | if (t.getPriority() != Thread.NORM_PRIORITY) {
38 | t.setPriority(Thread.NORM_PRIORITY);
39 | }
40 | return t;
41 | }
42 | }
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/ScanUtil.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util;
2 |
3 | import java.io.File;
4 | import java.io.FileFilter;
5 | import java.io.IOException;
6 | import java.net.JarURLConnection;
7 | import java.net.URL;
8 | import java.net.URLDecoder;
9 | import java.util.Enumeration;
10 | import java.util.LinkedHashSet;
11 | import java.util.Set;
12 | import java.util.jar.JarEntry;
13 | import java.util.jar.JarFile;
14 |
15 | public class ScanUtil {
16 |
17 | public static Set> getClasses(String pack) {
18 | Set> classes = new LinkedHashSet>();
19 | boolean recursive = true;
20 | String packageName = pack;
21 | String packageDirName = packageName.replace('.', '/');
22 | Enumeration dirs;
23 | try {
24 | dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
25 | while (dirs.hasMoreElements()) {
26 | URL url = dirs.nextElement();
27 | String protocol = url.getProtocol();
28 | if ("file".equals(protocol)) {
29 | String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
30 | packageName = pack;
31 | findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
32 | } else if ("jar".equals(protocol)) {
33 | JarFile jar;
34 | try {
35 | jar = ((JarURLConnection) url.openConnection()).getJarFile();
36 | Enumeration entries = jar.entries();
37 | while (entries.hasMoreElements()) {
38 | JarEntry entry = entries.nextElement();
39 | String name = entry.getName();
40 | if (name.charAt(0) == '/') {
41 | name = name.substring(1);
42 | }
43 | if (name.startsWith(packageDirName)) {
44 | int idx = name.lastIndexOf('/');
45 | if (idx != -1) {
46 | packageName = name.substring(0, idx).replace('/', '.');
47 | }
48 | if ((idx != -1) || recursive) {
49 | if (name.endsWith(".class") && !entry.isDirectory()) {
50 | String className = name.substring(packageName.length() + 1, name.length() - 6);
51 | try {
52 | classes.add(Thread.currentThread().getContextClassLoader()
53 | .loadClass(packageName + '.' + className));
54 | } catch (ClassNotFoundException e) {
55 | e.printStackTrace();
56 | }
57 | }
58 | }
59 | }
60 | }
61 | } catch (IOException e) {
62 | e.printStackTrace();
63 | }
64 | }
65 | }
66 | } catch (IOException e) {
67 | e.printStackTrace();
68 | }
69 |
70 | return classes;
71 | }
72 |
73 | public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
74 | Set> classes) {
75 | File dir = new File(packagePath);
76 | if (!dir.exists() || !dir.isDirectory()) {
77 | return;
78 | }
79 | File[] dirfiles = dir.listFiles(new FileFilter() {
80 | public boolean accept(File file) {
81 | return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
82 | }
83 | });
84 | for (File file : dirfiles) {
85 | if (file.isDirectory()) {
86 | findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
87 | classes);
88 | } else {
89 | String className = file.getName().substring(0, file.getName().length() - 6);
90 | try {
91 | classes.add(
92 | Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
93 | } catch (ClassNotFoundException e) {
94 | e.printStackTrace();
95 | }
96 | }
97 | }
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/enumutil/EnumUtil.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util.enumutil;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * 枚举工具
10 | *
11 | * @author guowei
12 | * @since 2010-4-21
13 | */
14 | public class EnumUtil {
15 |
16 | private static Logger logger = LoggerFactory.getLogger(EnumUtil.class);
17 |
18 | /**
19 | * 根据枚举index返回枚举元素,index从0开始
20 | *
21 | * @param 枚举类型
22 | * @param values 枚举元素输注
23 | * @param index 从0开始的index
24 | * @return 枚举元素
25 | */
26 | public static > T valueOf(List values, int index) {
27 | return valueOf(values, index, true);
28 | }
29 |
30 | /**
31 | * 根据枚举index返回枚举元素,index从0开始
32 | *
33 | * @param 枚举类型
34 | * @param values 枚举元素输注
35 | * @param index 从0开始的index
36 | * @param printErrLog 是否打印错误日志
37 | * @return 枚举元素
38 | */
39 | public static > T valueOf(List values, int index, boolean printErrLog) {
40 | T value = null;
41 | try {
42 | value = values.get(index);
43 | } catch (Exception e) {
44 | String typeName = "unknown";
45 | if (values != null) {
46 | for (T enu : values) {
47 | if (enu != null) {
48 | typeName = enu.getClass().getName();
49 | break;
50 | }
51 | }
52 | }
53 | if (printErrLog) {
54 | logger.error(String.format("从枚举中取元素时错误 type=%1$s index=%2$d", typeName, index), e);
55 | }
56 | return null;
57 | }
58 | return value;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/main/java/com/hjc/util/enumutil/IndexedEnum.java:
--------------------------------------------------------------------------------
1 | package com.hjc.util.enumutil;
2 |
3 | import com.hjc.util.Assert;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 |
7 | import java.util.ArrayList;
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | /**
13 | * 从0开始的可索引枚举接口定义,实现此接口的枚举各个元素的index可以不连续,但此接口的实现类多为稀疏数组结构,保持index连续可以节省空间
14 | */
15 | public interface IndexedEnum {
16 |
17 | Logger log = LoggerFactory.getLogger(IndexedEnum.class);
18 |
19 | /**
20 | * 获取该枚举的索引值
21 | *
22 | * @return 返回>=0的索引值
23 | */
24 | public abstract int getIndex();
25 |
26 | public static class IndexedEnumUtil {
27 |
28 | /**
29 | * 索引警戒上限,发现超过此值的索引可能存在较大的空间浪费
30 | */
31 | private static final int WORNNING_MAX_INDEX = 1000;
32 |
33 | /**
34 | * 检测枚举索引
35 | *
36 | * 仅在起服时候用
37 | *
38 | * @return 如果未定义,返回false。
39 | */
40 | public static > boolean checkIndex(List allIndexedEnums, int index) {
41 | return checkIndex(allIndexedEnums, index, true);
42 | }
43 |
44 | /**
45 | * 检测枚举索引
46 | *
47 | * 仅在起服时候用
48 | *
49 | * @return 如果未定义,返回false。
50 | */
51 | public static > boolean checkIndex(List allIndexedEnums, int index, boolean printErrLog) {
52 | try {
53 | if (EnumUtil.valueOf(allIndexedEnums, index, printErrLog) == null) {
54 | return false;
55 | }
56 | } catch (Exception e) {
57 | return false;
58 | }
59 | return true;
60 | }
61 |
62 | /**
63 | * 将枚举中的元素放到一个List中,每个元素在list中的下表即为他的index,如果有不连续的index,则空缺的index用null填充
64 | *
65 | * @param enums
66 | * @param
67 | * @return
68 | */
69 | public static List toIndexIntList(E[] enums) {
70 | int maxIndex = Integer.MIN_VALUE;
71 | int curIdx = 0;
72 | // 找到最大index,此值+1就是结果list的size
73 | for (E enm : enums) {
74 | curIdx = enm.getIndex();
75 | // 索引不能为负
76 | Assert.isTrue(curIdx >= 0, String.format("枚举索引不能为负 index: %1$d type: %2$s ", curIdx, enums.getClass()
77 | .getComponentType().getName()));
78 | if (curIdx > maxIndex) {
79 | maxIndex = curIdx;
80 | }
81 | }
82 | if (maxIndex >= WORNNING_MAX_INDEX) {
83 | log.info(String.format("警告:枚举类%s中有索引超过%d的索引,如果有很多索引空缺,可能会造成空间浪费", enums.getClass().getSimpleName(), WORNNING_MAX_INDEX));
84 | }
85 | List instances = new ArrayList<>(maxIndex + 1);
86 | // 先全用null填充
87 | for (int i = 0; i < maxIndex + 1; i++) {
88 | instances.add(null);
89 | }
90 | for (E enm : enums) {
91 | curIdx = enm.getIndex();
92 | // 索引必须唯一
93 | Assert.isTrue(instances.get(curIdx) == null, String.format("枚举中有重复的index type=%s,index=%s "
94 | , enums.getClass().getComponentType().getName(), curIdx));
95 | instances.set(curIdx, enm.getIndex());
96 | }
97 | return instances;
98 | }
99 |
100 | /**
101 | * 将枚举中的元素放到一个List中,每个元素在list中的下表即为他的index,如果有不连续的index,则空缺的index用null填充
102 | *
103 | * @param
104 | * @param enums
105 | * @return
106 | */
107 | public static List toIndexes(E[] enums) {
108 | int maxIndex = Integer.MIN_VALUE;
109 | int curIdx = 0;
110 | // 找到最大index,此值+1就是结果list的size
111 | for (E enm : enums) {
112 | curIdx = enm.getIndex();
113 | // 索引不能为负
114 | Assert.isTrue(curIdx >= 0, String.format("枚举索引不能为负 index: %1$d type: %2$s ", curIdx, enums.getClass()
115 | .getComponentType().getName()));
116 | if (curIdx > maxIndex) {
117 | maxIndex = curIdx;
118 | }
119 | }
120 | if (maxIndex >= WORNNING_MAX_INDEX) {
121 | log.info(String.format("警告:枚举类%s中有索引超过%d的索引,如果有很多索引空缺,可能会造成空间浪费", enums.getClass().getSimpleName(), WORNNING_MAX_INDEX));
122 | }
123 | List instances = new ArrayList(maxIndex + 1);
124 | // 先全用null填充
125 | for (int i = 0; i < maxIndex + 1; i++) {
126 | instances.add(null);
127 | }
128 | for (E enm : enums) {
129 | curIdx = enm.getIndex();
130 | // 索引必须唯一
131 | Assert.isTrue(instances.get(curIdx) == null, String.format("枚举中有重复的index type=%s,index=%s "
132 | , enums.getClass().getComponentType().getName(), curIdx));
133 | instances.set(curIdx, enm);
134 | }
135 | return instances;
136 | }
137 |
138 | /**
139 | * 将枚举中的元素放到一个List中,每个元素在list中的下表即为他的index,如果有不连续的index,则空缺的index用null填充
140 | *
141 | * @param
142 | * @param enums
143 | * @return
144 | */
145 | public static List toReverseIndexes(E[] enums) {
146 | int maxIndex = Integer.MIN_VALUE;
147 | int curIdx = 0;
148 | // 找到最大index,此值+1就是结果list的size
149 | for (E enm : enums) {
150 | curIdx = enm.getIndex();
151 | // 索引不能为负
152 | Assert.isTrue(curIdx >= 0, String.format("枚举索引不能为负 index: %1$d type: %2$s ", curIdx, enums.getClass()
153 | .getComponentType().getName()));
154 | if (curIdx > maxIndex) {
155 | maxIndex = curIdx;
156 | }
157 | }
158 | if (maxIndex >= WORNNING_MAX_INDEX) {
159 | log.info(String.format("警告:枚举类%s中有索引超过%d的索引,如果有很多索引空缺,可能会造成空间浪费", enums.getClass().getSimpleName(), WORNNING_MAX_INDEX));
160 | }
161 | List instances = new ArrayList(maxIndex + 1);
162 | // 先全用null填充
163 | for (int i = 0; i < maxIndex + 1; i++) {
164 | instances.add(null);
165 | }
166 | int reverseIndex;
167 | for (E enm : enums) {
168 | curIdx = enm.getIndex();
169 | reverseIndex = maxIndex - curIdx;
170 | // 索引必须唯一
171 | Assert.isTrue(instances.get(reverseIndex) == null, "枚举中有重复的index type= "
172 | + enums.getClass().getComponentType().getName());
173 | instances.set(reverseIndex, enm);
174 | }
175 | return instances;
176 | }
177 |
178 | public static HashMap toIndexMap(E[] enums) {
179 | HashMap map = new HashMap<>();
180 | for (E enm : enums) {
181 | map.put(enm.getIndex(), enm);
182 | }
183 | return map;
184 | }
185 |
186 | /**
187 | * 打印枚举所有index
188 | *
189 | * @param allIndexedEnums
190 | * @param
191 | * @return
192 | */
193 | public static String printEnumIndexes(List allIndexedEnums) {
194 | StringBuilder stringBuilder = new StringBuilder();
195 | for (E e : allIndexedEnums) {
196 | if (e == null) {
197 | continue;
198 | }
199 | stringBuilder.append(e.getIndex() + " ");
200 | }
201 | return stringBuilder.length() > 0 ? stringBuilder.substring(0, stringBuilder.length() - 1) : stringBuilder.toString();
202 | }
203 |
204 | //标记
205 | public static Map getDataId(E[] enums) {
206 | Map dataIdMap = new HashMap<>();
207 | for (E e : enums) {
208 | dataIdMap.put(e.getIndex(), e);
209 | }
210 | return dataIdMap;
211 | }
212 | }
213 | }
--------------------------------------------------------------------------------
/src/main/resources/ServerLib.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | --- 服务端Java调用库
9 |
10 | require "Lib/class"
11 |
12 | ---@class ServerLib : table
13 | ${FIELDS_COMMENT}ServerLib = class(nil, 'ServerLib');
14 |
15 | function ServerLib:ctor()
16 | ${SELF_FIELDS}end
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/ServerLibLuaFile.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | ${COMMENT}
9 | require "Lib/class"
10 |
11 | ---@class ${NAME} : ${SUPER_CLASS_COMMENT}
12 | ${NAME} = class(${SUPER_CLASS}, '${NAME}');
13 |
14 | function ${NAME}:ctor()
15 | end
16 |
17 | ${FUNCTIONS}return ${NAME};
--------------------------------------------------------------------------------
/src/main/resources/ServerModelLuaFile.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | ${COMMENT}
9 | require "Lib/class"
10 |
11 | ---@class ${NAME} : ${SUPER_CLASS_COMMENT}
12 | ${FILEDS_COMMENTS}${NAME} = class(${SUPER_CLASS}, '${NAME}');
13 |
14 | ${CTOR_FUNCTION}
15 | return ${NAME};
--------------------------------------------------------------------------------
/src/main/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${date:yyyy-MM-dd}
6 | %d{yyyy-MM-dd HH:mm:ss,SSS} (%F:%L) :%m %n
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 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/ConvertLibLuaFileTest.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert;
2 |
3 | import com.hjc.lua.convertor.LuaServerLibFileConverter;
4 |
5 | /**
6 | * @author hejincheng
7 | * @version 1.0
8 | * @date 2022/8/22 21:58
9 | **/
10 | public class ConvertLibLuaFileTest {
11 |
12 | public static void main(String[] args) {
13 | LuaServerLibFileConverter.main(args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/ConvertModelLuaFileTest.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert;
2 |
3 | import com.hjc.lua.convertor.LuaServerModelFileConverter;
4 |
5 | /**
6 | * @author hejincheng
7 | * @version 1.0
8 | * @date 2022/8/22 21:58
9 | **/
10 | public class ConvertModelLuaFileTest {
11 |
12 | public static void main(String[] args) {
13 | LuaServerModelFileConverter.main(args);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/README.md:
--------------------------------------------------------------------------------
1 | # Java类转换Lua文件工具
2 |
3 | `用于需要互相调用,或者同一份代码,需要两边语言都写的情况`
4 |
5 | > 此工具类基于class.lua(src/test/lua/lib/class.lua)的面向对象模式
6 |
7 | ## 一、Java调用库
8 |
9 | Lua中需要调用java方法的地方,需要java创建调用类,然后在lua中调用,创建对应的lua类,能在lua 代码中更方便的调用。
10 |
11 | 这个过程可以通过工具生成lua文件,并加到Lua统一调用接口ServerLib.lua中 Lua中所有调用Java方法的接口都通过ServerLib调用,如SERVER_LIB.battle:invokeBattleResult(
12 | BATTLE_ROOM:GetBattleId(), unit_player:GetPlayerId(), self.battleResult:GetId())
13 |
14 | ### 使用方法:
15 |
16 | #### 1. 新建Java调用库,增加类注解@LuaServerLib
17 |
18 | |参数名|描述|默认值|
19 | |---|---|---|
20 | |fieldName|ServerLib字段名|Java类名首字母小写|
21 | |className|lua类名|Java类名|
22 | |fileDir|lua文件目录,以luaRootPath为根路径开始|工具类同目录下:~/src/test/java/lua/|
23 | |comment|注释|无|
24 | |addToServerLib|是否添加到ServerLib|是|
25 |
26 | #### 2. 对需要调用的静态方法增加方法注解@LuaServerLibFunc
27 |
28 | |参数名|描述|默认值|
29 | |---|---|---|
30 | |comment|注释|无|
31 | |returnComment|返回注释|无|
32 |
33 | #### 3. 对方法内的参数增加参数注解@LuaParam
34 |
35 | |参数名|描述|默认值|
36 | |---|---|---|
37 | |comment|注释|无|
38 | |value|lua字段名|无|
39 |
40 | > 因为luaj编译后的class的字段名都变成arg0,arg1了,所以不加@LuaParam注解生成的参数名都不认识
41 |
42 | #### 4. 运行工具类:lua.LuaServerLibFileConverter,增加VM参数指定lua路径
43 |
44 | ```properties
45 | -DluaRootPath=lua项目根路径
46 | -DtemplateFilePath=模板文件路径(默认取框架自带模板)
47 | -DjavaScanPackage=要扫描的Java包路径(默认com.hjc)
48 | -DserverLibFilePath=ServerLib文件路径(默认Lib\\Server)
49 | ```
50 |
51 | #### 5. 刷新IDEA:File -> Reload All from Disk
52 |
53 | 参考代码:
54 |
55 | ```java
56 |
57 | /**
58 | * lua战斗核心调用Java类
59 | *
60 | * 这个类里的接口和ServerLuaBattle.lua映射
61 | *
62 | *
63 | * @author hejincheng
64 | * @version 1.0
65 | * @date 2022/2/16 18:55
66 | **/
67 | @LuaServerLib(fieldName = "battle", className = "ServerLuaBattle", fileDir = "Lib/Server/")
68 | public class LuaBattleFunction {
69 |
70 | /**
71 | * Lua脚本调用发送消息
72 | *
73 | * @param uid 玩家id
74 | * @param header 消息号
75 | * @param luaTable 推送参数
76 | */
77 | @LuaServerLibFunc(comment = "Lua脚本调用发送消息")
78 | public static void invokeSendMessageByLua(@LuaParam(value = "uid", comment = "玩家id") int uid,
79 | @LuaParam(value = "header", comment = "消息号") int header,
80 | @LuaParam(value = "luaTable", comment = "消息体") LuaTable luaTable) {
81 | IHumanService humanService = GameServiceManager.getService(IHumanService.class);
82 | if (humanService == null) {
83 | Log.battleLogger.error(String.format("%d.LuaBattle.invokeSendMessageByLua.server.err - header[%d].luaTable[%s]", uid, header, luaTable));
84 | return;
85 | }
86 | Human human = humanService.getHuman(uid);
87 | if (human == null) {
88 | Log.battleLogger.error(String.format("%d.LuaBattle.invokeSendMessageByLua.err.player.null - header[%d].luaTable[%s]", uid, header, luaTable));
89 | return;
90 | }
91 | human.push(header, luaTable);
92 | }
93 | }
94 | ```
95 |
96 | 生成lua文件:
97 |
98 | ```lua
99 | ---
100 | --- Generated by EmmyLua(https://github.com/EmmyLua)
101 | --- Created by Administrator.
102 | --- DateTime: 2022-08-22 22:57:29
103 | ---
104 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
105 | ---
106 |
107 | require "Lib/class"
108 |
109 | ---@class ServerLuaBattle : table
110 | ServerLuaBattle = class(nil, 'ServerLuaBattle');
111 |
112 | function ServerLuaBattle:ctor()
113 | end
114 |
115 | -- 获取战斗核心
116 | ---@param battleId number 战斗id
117 | ---@type function
118 | ---@return any
119 | ---@public
120 | function ServerLuaBattle:getFightCoreLua(battleId)
121 | return
122 | end
123 |
124 | -- Lua脚本调用发送消息
125 | ---@param uid number 玩家id
126 | ---@param header number 消息号
127 | ---@param luaTable table 消息体
128 | ---@type function
129 | ---@return void
130 | ---@public
131 | function ServerLuaBattle:invokeSendMessageByLua(uid, header, luaTable)
132 | return
133 | end
134 |
135 | -- Lua脚本调用广播消息
136 | ---@param raidId number 副本id
137 | ---@param header number 消息号
138 | ---@param luaTable table 推送参数
139 | ---@param includeServer boolean 广播服务端逻辑核
140 | ---@type function
141 | ---@return void
142 | ---@public
143 | function ServerLuaBattle:invokeBroadcastMessageByLua(raidId, header, luaTable, includeServer)
144 | return
145 | end
146 |
147 | return ServerLuaBattle;
148 | ```
149 |
150 | 生成ServerLib.lua文件:
151 |
152 | ```lua
153 | ---
154 | --- Generated by EmmyLua(https://github.com/EmmyLua)
155 | --- Created by Administrator.
156 | --- DateTime: 2022-08-22 22:57:30
157 | ---
158 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
159 | ---
160 | --- 服务端Java调用库
161 |
162 | require "Lib/class"
163 |
164 | ---@class ServerLib : table
165 | ---@field battle ServerLuaBattle
166 | ---@field logTool ServerLogTool
167 | ServerLib = class(nil, 'ServerLib');
168 |
169 | function ServerLib:ctor()
170 | self.battle = luajava.bindClass("com.hjc.demo.convert.lib.LuaBattleFunction")
171 | self.logTool = luajava.bindClass("com.hjc.lua.log.LuaLogTool")
172 | end
173 | ```
174 |
175 | ## 二、Java数据类
176 |
177 | 有的数据,需要服务端全局共享,不能每场战斗都独一份lua数据,这种情况下可以在Java创建共享数据,这样的Model类可以通过工具生成需要的lua文件。
178 |
179 | ### 使用方法:
180 |
181 | #### 1. 新建Java调用库,增加类注解@LuaServerModel
182 |
183 | |参数名|描述|默认值|
184 | |---|---|---|
185 | |className|lua类名|Java类名|
186 | |fileDir|lua文件目录,以~/为根路径开始|工具类同目录下:~/src/test/java/lua/|
187 | |comment|注释|无|
188 |
189 | #### 2. 运行工具类:lua.LuaServerModelFileConverter,增加VM参数指定lua路径
190 |
191 | ```properties
192 | -DluaRootPath=lua项目根路径
193 | -DtemplateFilePath=模板文件路径(默认取框架自带模板)
194 | -DjavaScanPackage=要扫描的Java包路径(默认com.hjc)
195 | ```
196 |
197 | #### 3. 刷新IDEA:File -> Reload All from Disk
198 |
199 | 参考代码: FallDictData.java
200 |
201 | ```java
202 | package com.hjc.helper;
203 |
204 | import com.hjc.annotation.LuaParam;
205 | import com.hjc.annotation.LuaServerModel;
206 | import lombok.Data;
207 |
208 | /**
209 | * @author hejincheng
210 | * @version 1.0
211 | * @date 2022/3/7 17:19
212 | **/
213 | @LuaServerModel(className = "FallDictData", comment = "掉落表数据", fileDir = "Battle/Logic/Room/BattleObject/Fall")
214 | @Data
215 | public class FallDictData {
216 |
217 | @LuaParam(comment = "掉落条件")
218 | private int conditionType;
219 |
220 | @LuaParam(comment = "掉落条件参数")
221 | private float conditionParam;
222 |
223 | @LuaParam(comment = "生效次数")
224 | private int activeTimes;
225 |
226 | @LuaParam(comment = "掉落id")
227 | private int fallObjectId;
228 |
229 | @LuaParam(comment = "掉落数量")
230 | private int fallCount;
231 |
232 | @LuaParam(comment = "冷却时间")
233 | private float cdLimitTime;
234 |
235 | }
236 |
237 | ```
238 |
239 | 生成的lua:
240 |
241 | ```lua
242 | --- 掉落表数据
243 |
244 | require "Lib/class"
245 |
246 | ---@class FallDictData : table
247 | ---@field conditionType number 掉落条件
248 | ---@field conditionParam number 掉落条件参数
249 | ---@field activeTimes number 生效次数
250 | ---@field fallObjectId number 掉落id
251 | ---@field fallCount number 掉落数量
252 | ---@field cdLimitTime number 冷却时间
253 | FallDictData = class(nil, 'FallDictData');
254 |
255 | function FallDictData:ctor(_conditionType, _conditionParam, _activeTimes, _fallObjectId, _fallCount, _cdLimitTime)
256 | self.conditionType = _conditionType
257 | self.conditionParam = _conditionParam
258 | self.activeTimes = _activeTimes
259 | self.fallObjectId = _fallObjectId
260 | self.fallCount = _fallCount
261 | self.cdLimitTime = _cdLimitTime
262 | end
263 |
264 | return FallDictData;
265 | ```
266 |
267 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/fall/FallDictData.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.fall;
2 |
3 | import com.hjc.lua.annotation.LuaParam;
4 | import com.hjc.lua.annotation.LuaServerModel;
5 | import lombok.Data;
6 |
7 | /**
8 | * @author hejincheng
9 | * @version 1.0
10 | * @date 2022/3/7 17:19
11 | **/
12 | @LuaServerModel(className = "FallDictData", comment = "掉落表数据", fileDir = "config/fall")
13 | @Data
14 | public class FallDictData {
15 |
16 | @LuaParam(comment = "index")
17 | private int index;
18 |
19 | @LuaParam(comment = "掉落条件")
20 | private int conditionType;
21 |
22 | @LuaParam(comment = "掉落条件参数")
23 | private double conditionParam;
24 |
25 | @LuaParam(comment = "生效次数")
26 | private int activeTime;
27 |
28 | @LuaParam(comment = "掉落id")
29 | private int fallObjectId;
30 |
31 | @LuaParam(comment = "掉落数量")
32 | private int fallCount;
33 |
34 | @LuaParam(comment = "冷却时间")
35 | private double cdLimitTime;
36 |
37 | @LuaParam(comment = "适应元素")
38 | private boolean adaptElement;
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/lib/LuaBattleFunction.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.lib;
2 |
3 | import com.hjc.demo.service.BattleDemoService;
4 | import com.hjc.lua.annotation.LuaParam;
5 | import com.hjc.lua.annotation.LuaServerLib;
6 | import com.hjc.lua.annotation.LuaServerLibFunc;
7 | import org.luaj.vm2.LuaTable;
8 | import org.luaj.vm2.LuaValue;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 |
12 | /**
13 | * lua战斗核心调用Java类
14 | *
15 | * 这个类里的接口和ServerLuaBattle.lua映射
16 | *
17 | *
18 | * @author hejincheng
19 | * @version 1.0
20 | * @date 2022/2/16 18:55
21 | **/
22 | @LuaServerLib(fieldName = "battle", className = "ServerLuaBattle", fileDir = "lib")
23 | public class LuaBattleFunction {
24 |
25 | private static final Logger logger = LoggerFactory.getLogger(LuaBattleFunction.class);
26 |
27 | /**
28 | * 获取战斗核心
29 | *
30 | * @param battleId 战斗id
31 | */
32 | @LuaServerLibFunc(comment = "获取战斗核心")
33 | public static LuaValue getFightCoreLua(@LuaParam(value = "battleId", comment = "战斗id") int battleId) {
34 | BattleDemoService battleDemoService = BattleDemoService.getInstance();
35 | if (battleDemoService == null) {
36 | logger.error(String.format("LuaBattle.getFightCoreLua.err - battleId[%d].battleDemoService.null", battleId));
37 | return null;
38 | }
39 | return battleDemoService.getFightCore(battleId);
40 | }
41 |
42 | /**
43 | * Lua脚本调用发送消息
44 | *
45 | * @param uid 玩家id
46 | * @param header 消息号
47 | * @param luaTable 推送参数
48 | */
49 | @LuaServerLibFunc(comment = "Lua脚本调用发送消息")
50 | public static void invokeSendMessageByLua(@LuaParam(value = "uid", comment = "玩家id") int uid,
51 | @LuaParam(value = "header", comment = "消息号") int header,
52 | @LuaParam(value = "luaTable", comment = "消息体") LuaTable luaTable) {
53 | // 调用Java的网络层发送网络消息
54 | }
55 |
56 | /**
57 | * Lua脚本调用广播消息
58 | *
59 | * @param raidId 副本id
60 | * @param header 消息号
61 | * @param luaTable 推送参数
62 | * @param includeServer 广播服务端逻辑核
63 | */
64 | @LuaServerLibFunc(comment = "Lua脚本调用广播消息")
65 | public static void invokeBroadcastMessageByLua(@LuaParam(value = "raidId", comment = "副本id") int raidId,
66 | @LuaParam(value = "header", comment = "消息号") int header,
67 | @LuaParam(value = "luaTable", comment = "推送参数") LuaTable luaTable,
68 | @LuaParam(value = "includeServer", comment = "广播服务端逻辑核") boolean includeServer) {
69 | // 调用Java的网络层发送网络消息
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/skill/SkillBaseNode.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.skill;
2 |
3 | import com.hjc.lua.annotation.LuaServerModel;
4 | import lombok.Data;
5 |
6 | import java.util.ArrayList;
7 |
8 | /**
9 | * @author hejincheng
10 | * @version 1.0
11 | * @date 2022/2/15 15:13
12 | **/
13 | @Data
14 | @LuaServerModel(className = "SkillBaseNode", comment = "技能节点基类", fileDir = "config/skill")
15 | public class SkillBaseNode {
16 |
17 | //节点id
18 | public int nodeId;
19 | //节点类型
20 | public int nodeType;
21 | //节点参数
22 | public SkillNodeParam param;
23 | //节点子节点
24 | public ArrayList nextNodes;
25 | //特殊出口
26 | public SkillNodeExitData specialExit;
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/skill/SkillConfigData.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.skill;
2 |
3 | import com.hjc.demo.convert.skill.SkillBaseNode;
4 | import com.hjc.lua.annotation.LuaServerModel;
5 | import lombok.Data;
6 |
7 | import java.util.ArrayList;
8 |
9 | /**
10 | * @author hejincheng
11 | * @version 1.0
12 | * @date 2022/2/15 10:42
13 | **/
14 | @Data
15 | @LuaServerModel(className = "SkillConfigData", comment = "技能配置数据", fileDir = "config/skill")
16 | public class SkillConfigData {
17 |
18 | //技能Id
19 | public int id;
20 | //能力消耗
21 | public String manaCast;
22 | //是否之做检测
23 | public boolean onlyCheck;
24 | //极奏消耗
25 | public String superCast;
26 | //最大积累次数
27 | public String maxTime;
28 | //起始次数
29 | public String startTime;
30 | //开始节点
31 | public int startNodeId;
32 | //打断节点
33 | public int breakNodeId;
34 | //所有技能节点
35 | public ArrayList allNodes;
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/skill/SkillNodeExitData.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.skill;
2 |
3 | import com.hjc.lua.annotation.LuaServerModel;
4 | import lombok.Data;
5 |
6 | import java.util.ArrayList;
7 |
8 | /**
9 | * @author hejincheng
10 | * @version 1.0
11 | * @date 2022/2/16 17:29
12 | **/
13 | @Data
14 | @LuaServerModel(className = "SkillNodeExitData", comment = "技能退出节点", fileDir = "config/skill")
15 | public class SkillNodeExitData {
16 |
17 | //事件类型
18 | public int type;
19 | //事件参数
20 | public String param;
21 | //后续节点
22 | public ArrayList nextNodeID;
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/convert/skill/SkillNodeParam.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.convert.skill;
2 |
3 | import com.hjc.lua.annotation.LuaServerModel;
4 | import lombok.Data;
5 |
6 | import java.util.ArrayList;
7 |
8 | /**
9 | * @author hejincheng
10 | * @version 1.0
11 | * @date 2022/2/25 17:17
12 | **/
13 | @Data
14 | @LuaServerModel(className = "SkillNodeParam", comment = "技能节点参数", fileDir = "config/skill")
15 | public class SkillNodeParam {
16 |
17 | public String time;
18 | public String barrageID;
19 | public String spreadParam;
20 | public String elementID;
21 | public String shootEffect;
22 | public String consume;
23 | public String colliderId;
24 | public ArrayList addBuff;
25 | public ArrayList removeBuff;
26 | public float maxSpeedRatio;
27 | public float currentSpeedRatio;
28 | public int loopId;
29 | public int loopTime;
30 | public double loopDelayTime;
31 | public boolean isForceBreak;
32 | public ArrayList buffGroupIds;
33 | public boolean selfAll;
34 | }
35 |
--------------------------------------------------------------------------------
/src/test/java/com/hjc/demo/service/BattleDemoService.java:
--------------------------------------------------------------------------------
1 | package com.hjc.demo.service;
2 |
3 | import com.hjc.lua.LuaBattleManager;
4 | import com.hjc.lua.LuaInit;
5 | import com.hjc.lua.exception.LuaException;
6 | import com.hjc.util.NamedThreadFactory;
7 | import org.luaj.vm2.LuaFunction;
8 | import org.luaj.vm2.LuaNumber;
9 | import org.luaj.vm2.LuaTable;
10 | import org.luaj.vm2.LuaValue;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import java.util.Map;
15 | import java.util.concurrent.ConcurrentHashMap;
16 | import java.util.concurrent.CountDownLatch;
17 | import java.util.concurrent.ExecutorService;
18 | import java.util.concurrent.Executors;
19 | import java.util.concurrent.atomic.AtomicInteger;
20 |
21 | /**
22 | * lua-java-battle战斗框架使用示例
23 | *
24 | * @author hejincheng
25 | * @version 1.0
26 | * @date 2022/8/18 21:27
27 | **/
28 | public class BattleDemoService {
29 |
30 | private final Logger logger = LoggerFactory.getLogger(BattleDemoService.class);
31 |
32 | private static final BattleDemoService INSTANCE = new BattleDemoService();
33 |
34 | private BattleDemoService() {
35 | }
36 |
37 | public static BattleDemoService getInstance() {
38 | return INSTANCE;
39 | }
40 |
41 | /**
42 | * 战斗id自增
43 | */
44 | AtomicInteger battleIdCounter = new AtomicInteger(0);
45 | /**
46 | * lua战斗管理器
47 | */
48 | LuaBattleManager luaBattleManager = LuaBattleManager.getInstance();
49 | /**
50 | * 战斗核心map
51 | */
52 | private final Map fightCoreMap = new ConcurrentHashMap<>();
53 |
54 | /**
55 | * 是否运行
56 | */
57 | private volatile boolean run;
58 |
59 | public static void main(String[] args) throws LuaException, InterruptedException {
60 | BattleDemoService battleDemoService = new BattleDemoService();
61 | battleDemoService.init();
62 | battleDemoService.start();
63 | }
64 |
65 | private void init() throws LuaException {
66 | LuaInit.LuaInitBuilder luaInit = LuaInit.builder();
67 | luaInit.preScript("print('Hello Lua Battle!!!')");
68 | // 设置lua根路径
69 | luaInit.luaRootPath("F:\\project\\lua-java-battle\\src\\main\\lua\\");
70 | // 加载lua调用接口目录
71 | luaInit.luaLoadDirectories("interface");
72 | // 加载lua主文件
73 | luaInit.luaLoadFiles("FightManager.lua");
74 | // 展示log
75 | luaInit.showLog(true);
76 | // 初始化全局lua环境
77 | luaBattleManager.init(luaInit.build());
78 |
79 | // lua主函数,可执行初始化等操作
80 | LuaFunction mainFunction = this.initFunction("Main");
81 | // 执行主函数,初始lua环境
82 | mainFunction.invoke();
83 |
84 | // 初始所有需要用到的方法
85 | this.updateFunction = this.initFunction("LOOPER.ServerUpdate");
86 | this.receiveMsgFunction = this.initFunction("COMMAND.ReceiveMsgTable");
87 | this.createFightCoreFunction = this.initFunction("CreateFightCore");
88 | this.initFightCoreFunction = this.initFunction("InitFightCore");
89 | this.closeFunction = this.initFunction("Close");
90 |
91 | // 创建10根线程,模拟游戏业务的线程模型
92 | for (int i = 0; i < 10; i++) {
93 | this.es[i] = Executors.newSingleThreadExecutor(new NamedThreadFactory("Battle-Looper-" + i));
94 | }
95 | // 单线程模拟网络消息的接收
96 | this.netExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("Net-IO"));
97 |
98 | this.run = true;
99 | logger.info("luaBattleManager init");
100 | }
101 |
102 | // lua每帧驱动方法
103 | private LuaFunction updateFunction;
104 | // lua接收消息方法
105 | private LuaFunction receiveMsgFunction;
106 | // lua创建战斗核心方法
107 | private LuaFunction createFightCoreFunction;
108 | // lua初始战斗核心方法
109 | private LuaFunction initFightCoreFunction;
110 | // lua关闭战斗核心方法
111 | private LuaFunction closeFunction;
112 |
113 |
114 | // 创建10根线程,模拟游戏业务的线程模型
115 | private final ExecutorService[] es = new ExecutorService[10];
116 | // 单线程模拟网络消息的接收
117 | private ExecutorService netExecutor;
118 |
119 | private LuaFunction initFunction(String functionName) throws LuaException {
120 | LuaFunction function = luaBattleManager.initExecuteFunction(functionName);
121 | if (function == null || function.isnil()) {
122 | throw new LuaException(String.format("init.function.err - [%s].not.found", functionName));
123 | }
124 | logger.info(String.format("init.function.[%s]", functionName));
125 | return function;
126 | }
127 |
128 | /**
129 | * 创建战斗
130 | */
131 | public void createFightCore() {
132 | int battleId = battleIdCounter.getAndIncrement();
133 | // 创建战斗核心
134 | LuaTable fightCore = (LuaTable) this.createFightCoreFunction.invoke(LuaNumber.valueOf(battleId));
135 | this.fightCoreMap.put(battleId, fightCore);
136 | // 初始化战斗核心
137 | this.initFightCoreFunction.invoke(fightCore);
138 | }
139 |
140 | public void start() throws InterruptedException {
141 | // 模拟战斗场数
142 | int battleNum = 1000;
143 | // 模拟执行update次数
144 | int updateNum = 100;
145 |
146 | System.out.println(String.format("start.create.fightCore.for.%d.count", battleNum));
147 |
148 | for (int i = 0; i < battleNum; i++) {
149 | this.createFightCore();
150 | }
151 |
152 | // 接收网络消息
153 | receiveNetMsg();
154 | // 执行update
155 | executeUpdate(updateNum);
156 |
157 | // 退出程序
158 | System.exit(0);
159 | }
160 |
161 | /**
162 | * 模拟接收网络消息
163 | */
164 | private void receiveNetMsg() {
165 | this.netExecutor.submit(() -> {
166 | while (this.run) {
167 | for (Map.Entry entry : this.fightCoreMap.entrySet()) {
168 | // 模拟消息体和id号
169 | int battleId = entry.getKey();
170 | LuaTable fightCore = entry.getValue();
171 | LuaTable luaTable = new LuaTable();
172 | luaTable.set("testKey", "testValue");
173 | this.receiveMsgFunction.invoke(new LuaValue[]{fightCore, LuaNumber.valueOf(battleId), LuaNumber.valueOf(battleId), luaTable});
174 | }
175 | // 每1s收到一条消息并调用lua进行接收
176 | try {
177 | Thread.sleep(100);
178 | } catch (InterruptedException e) {
179 | }
180 | }
181 | });
182 | }
183 |
184 | /**
185 | * 模拟执行每帧update
186 | *
187 | * @param updateNum
188 | * @throws InterruptedException
189 | */
190 | private void executeUpdate(int updateNum) throws InterruptedException {
191 | // 计数器,让每一场战斗执行num次update
192 | CountDownLatch countDownLatch = new CountDownLatch(updateNum * this.fightCoreMap.size());
193 |
194 | long start = System.currentTimeMillis();
195 | System.out.println(String.format("start.update.for.%d.count", updateNum));
196 |
197 | for (int i = 0; i < updateNum; i++) {
198 | for (Map.Entry entry : this.fightCoreMap.entrySet()) {
199 | int battleId = entry.getKey();
200 | LuaTable fightCore = entry.getValue();
201 |
202 | int index = battleId % this.es.length;
203 | this.es[index].submit(() -> {
204 | // 执行update方法
205 | this.updateFunction.invoke(fightCore);
206 |
207 | // 可以模拟每帧运行时间
208 | /*
209 | try {
210 | Thread.sleep(33);
211 | } catch (InterruptedException e) {
212 | }
213 | */
214 |
215 | countDownLatch.countDown();
216 | });
217 | }
218 | }
219 |
220 | System.out.println("start.await");
221 | countDownLatch.await();
222 |
223 | // 关闭
224 | System.out.println("start.close.invoke");
225 | for (LuaTable fightCore : this.fightCoreMap.values()) {
226 | this.closeFunction.invoke(fightCore);
227 | }
228 |
229 | // 战斗关闭后,从全局map移除
230 | this.fightCoreMap.clear();
231 |
232 | // 关闭
233 | this.run = false;
234 |
235 | for (ExecutorService e : this.es) {
236 | e.shutdownNow();
237 | }
238 | this.netExecutor.shutdownNow();
239 |
240 | long end = System.currentTimeMillis();
241 | System.out.println("time : " + (end - start) + "ms");
242 | }
243 |
244 | /**
245 | * 获取战斗核心
246 | *
247 | * @param battleId 战斗id
248 | * @return 战斗核心
249 | */
250 | public LuaTable getFightCore(int battleId) {
251 | return this.fightCoreMap.get(battleId);
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/test/lua/FightCoreLua.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by wth.
4 | --- DateTime: 2021/12/4 17:07
5 | --- lua战斗逻辑外壳
6 |
7 | require "logic/BattleRoom"
8 | require "lib/class"
9 |
10 | ---@class FightCoreLua:table lua战斗逻辑外壳
11 | FightCoreLua = class(nil, 'FightCoreLua')
12 |
13 | function FightCoreLua:ctor(_battleId)
14 | --print("FightCoreLua ctor")
15 | ---@type BattleRoom
16 | self.battleRoom = BattleRoom.New()
17 | end
18 |
19 | function FightCoreLua:Init()
20 | --print("FightCoreLua Init")
21 | self.battleRoom:Init()
22 | end
23 |
24 | --- 战斗逻辑帧更新
25 | function FightCoreLua:Update(_dt, _frameId, _dtMs)
26 | --print("FightCoreLua Update")
27 | end
28 |
29 | --- 服务器接收消息接口
30 | ---@public
31 | ---@param _uid number 发送协议的玩家id,如果是战斗房间消息该值为nil
32 | ---@param _id number 协议id
33 | ---@param _msgTable table 协议
34 | function FightCoreLua:ReceiveMsg(_uid, _id, _msgTable)
35 | --print("FightCoreLua ReceiveMsg: uid:" .. tostring(_uid) .. ", id:" .. tostring(_id) .. ", msgTable:" .. tostring(_msgTable) .. "")
36 | end
37 |
38 | --- 关闭核心
39 | ---@public
40 | function FightCoreLua:Close()
41 | --print("FightCoreLua Close")
42 | end
43 |
44 | return FightCoreLua
--------------------------------------------------------------------------------
/src/test/lua/FightManager.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022/8/17 22:49
5 | ---
6 | -- 外层赋值确定客户端启动logic_core
7 | ---@return boolean 客户端启动logic core
8 | function IsClient()
9 | return isClient
10 | end
11 |
12 | -- 外层赋值确定服务器启动logic core
13 | ---@return boolean 服务器启动logic core
14 | function IsServer()
15 | return isServer
16 | end
17 |
18 | require "lib/class"
19 | require "FightCoreLua"
20 |
21 | if IsServer() then
22 | -- 执行服务器初始化逻辑
23 | -- 服务端Java调用库
24 | require "Lib/Server/ServerLib"
25 | require 'org.luaj.vm2.lib.DebugLib'
26 |
27 | -- 服务端Java调用库
28 | ---@type ServerLib
29 | SERVER_LIB = ServerLib.New()
30 | end
31 |
32 | --外层调用启动logic_core
33 | function Main()
34 | Init()
35 | end
36 |
37 | function Init()
38 | -- 初始化全局的内容
39 | print("FightManager Init")
40 | end
41 |
42 | --- 创建战斗核心
43 | ---@return FightCoreLua 战斗核心
44 | function CreateFightCore(_battleId)
45 | ---@type FightCoreLua
46 | local fightCoreLua = FightCoreLua.New(_battleId)
47 | return fightCoreLua
48 | end
49 |
50 | --- 初始化战斗核心(服务端把这一步拆开做,需要等创建完扔进Raid对象管理,再做初始化)
51 | ---@param _fightCoreLua FightCoreLua
52 | function InitFightCore(_fightCoreLua)
53 | if _fightCoreLua == nil then
54 | return
55 | end
56 | -- 初始化
57 | _fightCoreLua:Init()
58 | end
59 |
60 | ---@type function
61 | ---@param _fightCoreLua FightCoreLua
62 | function Close(_fightCoreLua)
63 | if _fightCoreLua then
64 | _fightCoreLua:Close()
65 | end
66 | end
67 |
68 | --- 获取战斗核心
69 | ---@param _battleId number
70 | function GetFightCore(_battleId)
71 | if IsClient() then
72 | -- 客户端使用全局唯一战斗核心
73 | return CLIENT_FIGHT_CORE
74 | else
75 | -- 服务端从Java内存获取战斗核心
76 | return SERVER_LIB.battle:getFightCoreLua(_battleId)
77 | end
78 | end
79 |
80 | --- 获取战斗房间
81 | ---@param _battleId number
82 | ---@return BattleRoom
83 | function GetBattleRoom(_battleId)
84 | local fightCoreLua = GetFightCore(_battleId)
85 | if not fightCoreLua then
86 | return
87 | end
88 | return fightCoreLua.battleRoom
89 | end
90 |
91 |
--------------------------------------------------------------------------------
/src/test/lua/config/fall/FallDictData.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:39:28
5 | ---
6 | --- 掉落表数据
7 |
8 | require "Lib/class"
9 |
10 | ---@class FallDictData : table
11 | ---@field index number index
12 | ---@field conditionType number 掉落条件
13 | ---@field conditionParam number 掉落条件参数
14 | ---@field activeTime number 生效次数
15 | ---@field fallObjectId number 掉落id
16 | ---@field fallCount number 掉落数量
17 | ---@field cdLimitTime number 冷却时间
18 | ---@field adaptElement boolean 适应元素
19 | FallDictData = class(nil, 'FallDictData');
20 |
21 | function FallDictData:ctor(_index, _conditionType, _conditionParam, _activeTime, _fallObjectId, _fallCount, _cdLimitTime, _adaptElement)
22 | self.index = _index
23 | self.conditionType = _conditionType
24 | self.conditionParam = _conditionParam
25 | self.activeTime = _activeTime
26 | self.fallObjectId = _fallObjectId
27 | self.fallCount = _fallCount
28 | self.cdLimitTime = _cdLimitTime
29 | self.adaptElement = _adaptElement
30 | end
31 |
32 | return FallDictData;
33 |
--------------------------------------------------------------------------------
/src/test/lua/config/skill/SkillBaseNode.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:39:28
5 | ---
6 | --- 技能节点基类
7 |
8 | require "Lib/class"
9 |
10 | ---@class SkillBaseNode : table
11 | ---@field nodeId number
12 | ---@field nodeType number
13 | ---@field param SkillNodeParam
14 | ---@field nextNodes SkillNodeExitData[]
15 | ---@field specialExit SkillNodeExitData
16 | SkillBaseNode = class(nil, 'SkillBaseNode');
17 |
18 | function SkillBaseNode:ctor(_nodeId, _nodeType, _param, _nextNodes, _specialExit)
19 | self.nodeId = _nodeId
20 | self.nodeType = _nodeType
21 | self.param = _param
22 | self.nextNodes = _nextNodes
23 | self.specialExit = _specialExit
24 | end
25 |
26 | return SkillBaseNode;
27 |
--------------------------------------------------------------------------------
/src/test/lua/config/skill/SkillConfigData.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:39:28
5 | ---
6 | --- 技能配置数据
7 |
8 | require "Lib/class"
9 |
10 | ---@class SkillConfigData : table
11 | ---@field id number
12 | ---@field manaCast string
13 | ---@field onlyCheck boolean
14 | ---@field superCast string
15 | ---@field maxTime string
16 | ---@field startTime string
17 | ---@field startNodeId number
18 | ---@field breakNodeId number
19 | ---@field allNodes SkillBaseNode[]
20 | SkillConfigData = class(nil, 'SkillConfigData');
21 |
22 | function SkillConfigData:ctor(_id, _manaCast, _onlyCheck, _superCast, _maxTime, _startTime, _startNodeId, _breakNodeId, _allNodes)
23 | self.id = _id
24 | self.manaCast = _manaCast
25 | self.onlyCheck = _onlyCheck
26 | self.superCast = _superCast
27 | self.maxTime = _maxTime
28 | self.startTime = _startTime
29 | self.startNodeId = _startNodeId
30 | self.breakNodeId = _breakNodeId
31 | self.allNodes = _allNodes
32 | end
33 |
34 | return SkillConfigData;
35 |
--------------------------------------------------------------------------------
/src/test/lua/config/skill/SkillNodeExitData.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:39:28
5 | ---
6 | --- 技能退出节点
7 |
8 | require "Lib/class"
9 |
10 | ---@class SkillNodeExitData : table
11 | ---@field type number
12 | ---@field param string
13 | ---@field nextNodeID number[]
14 | SkillNodeExitData = class(nil, 'SkillNodeExitData');
15 |
16 | function SkillNodeExitData:ctor(_type, _param, _nextNodeID)
17 | self.type = _type
18 | self.param = _param
19 | self.nextNodeID = _nextNodeID
20 | end
21 |
22 | return SkillNodeExitData;
23 |
--------------------------------------------------------------------------------
/src/test/lua/config/skill/SkillNodeParam.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:39:28
5 | ---
6 | --- 技能节点参数
7 |
8 | require "Lib/class"
9 |
10 | ---@class SkillNodeParam : table
11 | ---@field time string
12 | ---@field barrageID string
13 | ---@field spreadParam string
14 | ---@field elementID string
15 | ---@field shootEffect string
16 | ---@field consume string
17 | ---@field colliderId string
18 | ---@field addBuff string[]
19 | ---@field removeBuff string[]
20 | ---@field maxSpeedRatio number
21 | ---@field currentSpeedRatio number
22 | ---@field loopId number
23 | ---@field loopTime number
24 | ---@field loopDelayTime number
25 | ---@field isForceBreak boolean
26 | ---@field buffGroupIds string[]
27 | ---@field selfAll boolean
28 | SkillNodeParam = class(nil, 'SkillNodeParam');
29 |
30 | function SkillNodeParam:ctor(_time, _barrageID, _spreadParam, _elementID, _shootEffect, _consume, _colliderId, _addBuff, _removeBuff, _maxSpeedRatio, _currentSpeedRatio, _loopId, _loopTime, _loopDelayTime, _isForceBreak, _buffGroupIds, _selfAll)
31 | self.time = _time
32 | self.barrageID = _barrageID
33 | self.spreadParam = _spreadParam
34 | self.elementID = _elementID
35 | self.shootEffect = _shootEffect
36 | self.consume = _consume
37 | self.colliderId = _colliderId
38 | self.addBuff = _addBuff
39 | self.removeBuff = _removeBuff
40 | self.maxSpeedRatio = _maxSpeedRatio
41 | self.currentSpeedRatio = _currentSpeedRatio
42 | self.loopId = _loopId
43 | self.loopTime = _loopTime
44 | self.loopDelayTime = _loopDelayTime
45 | self.isForceBreak = _isForceBreak
46 | self.buffGroupIds = _buffGroupIds
47 | self.selfAll = _selfAll
48 | end
49 |
50 | return SkillNodeParam;
51 |
--------------------------------------------------------------------------------
/src/test/lua/interface/Command.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by hejincheng.
4 | --- DateTime: 2021/12/4 12:05
5 | --- 消息接收器
6 |
7 | COMMAND = {};
8 |
9 | --外部调用收到msg table(服务器用)
10 | ---@param fightCoreLua FightCoreLua 战斗
11 | ---@param uid number 发送协议的玩家id,如果是战斗房间消息该值为nil
12 | ---@param id number 协议id
13 | ---@param table table 协议
14 | function COMMAND.ReceiveMsgTable(fightCoreLua, uid, id, table)
15 | if fightCoreLua == nil then
16 | print("ReceiveMsgBytes id[" .. tostring(id) .. "] fightCoreLua nil !!!")
17 | return
18 | end
19 | fightCoreLua:ReceiveMsg(uid, id, table)
20 | end
21 |
22 | --向客户端广播消息 服务器端逻辑核心调用
23 | ---@param battleId number 广播的战斗id
24 | ---@param id number 协议id
25 | ---@param table table 协议
26 | ---@param includeServer boolean 是否广播给服务器逻辑核
27 | function COMMAND.BroadcastCommandToClientTable(battleId, id, table, includeServer)
28 | ---@type boolean
29 | local _includeServer = false
30 | if includeServer then
31 | _includeServer = includeServer
32 | end
33 |
34 | -- 服务端调用服务端库发送
35 | SERVER_LIB.battle:invokeBroadcastMessageByLua(battleId, id, table, _includeServer);
36 | end
37 |
38 |
--------------------------------------------------------------------------------
/src/test/lua/interface/Looper.lua:
--------------------------------------------------------------------------------
1 | -- 外层tick更新
2 | LOOPER = {}
3 |
4 | -- tick间隔
5 | local dt = 0.033
6 | -- 当前帧号
7 | local frameId = 0
8 |
9 | -- 更新
10 | ---@param _fightCoreLua FightCoreLua
11 | function LOOPER.ServerUpdate(_fightCoreLua)
12 | frameId = frameId + 1
13 | if _fightCoreLua ~= nil then
14 | _fightCoreLua:Update(dt, frameId, 33)
15 | end
16 | end
--------------------------------------------------------------------------------
/src/test/lua/lib/ServerLib.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:57:30
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | --- 服务端Java调用库
9 |
10 | require "Lib/class"
11 |
12 | ---@class ServerLib : table
13 | ---@field battle ServerLuaBattle
14 | ---@field logTool ServerLogTool
15 | ServerLib = class(nil, 'ServerLib');
16 |
17 | function ServerLib:ctor()
18 | self.battle = luajava.bindClass("com.hjc.demo.convert.lib.LuaBattleFunction")
19 | self.logTool = luajava.bindClass("com.hjc.lua.log.LuaLogTool")
20 | end
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/test/lua/lib/ServerLogTool.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:57:30
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 |
9 | require "Lib/class"
10 |
11 | ---@class ServerLogTool : table
12 | ServerLogTool = class(nil, 'ServerLogTool');
13 |
14 | function ServerLogTool:ctor()
15 | end
16 |
17 | -- 打印log
18 | ---@param logLevel number log级别
19 | ---@param msg string log消息
20 | ---@type function
21 | ---@return void
22 | ---@public
23 | function ServerLogTool:log(logLevel, msg)
24 | return
25 | end
26 |
27 | -- 获取堆栈
28 | ---@param log string
29 | ---@type function
30 | ---@return string
31 | ---@public
32 | function ServerLogTool:traceback(log)
33 | return
34 | end
35 |
36 | -- log级别是否可用
37 | ---@param logLevel number log级别
38 | ---@type function
39 | ---@return boolean
40 | ---@public
41 | function ServerLogTool:isLogEnabled(logLevel)
42 | return
43 | end
44 |
45 | return ServerLogTool;
46 |
--------------------------------------------------------------------------------
/src/test/lua/lib/ServerLuaBattle.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by Administrator.
4 | --- DateTime: 2022-08-22 22:57:29
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 |
9 | require "Lib/class"
10 |
11 | ---@class ServerLuaBattle : table
12 | ServerLuaBattle = class(nil, 'ServerLuaBattle');
13 |
14 | function ServerLuaBattle:ctor()
15 | end
16 |
17 | -- 获取战斗核心
18 | ---@param battleId number 战斗id
19 | ---@type function
20 | ---@return any
21 | ---@public
22 | function ServerLuaBattle:getFightCoreLua(battleId)
23 | return
24 | end
25 |
26 | -- Lua脚本调用发送消息
27 | ---@param uid number 玩家id
28 | ---@param header number 消息号
29 | ---@param luaTable table 消息体
30 | ---@type function
31 | ---@return void
32 | ---@public
33 | function ServerLuaBattle:invokeSendMessageByLua(uid, header, luaTable)
34 | return
35 | end
36 |
37 | -- Lua脚本调用广播消息
38 | ---@param raidId number 副本id
39 | ---@param header number 消息号
40 | ---@param luaTable table 推送参数
41 | ---@param includeServer boolean 广播服务端逻辑核
42 | ---@type function
43 | ---@return void
44 | ---@public
45 | function ServerLuaBattle:invokeBroadcastMessageByLua(raidId, header, luaTable, includeServer)
46 | return
47 | end
48 |
49 | return ServerLuaBattle;
50 |
--------------------------------------------------------------------------------
/src/test/lua/lib/class.lua:
--------------------------------------------------------------------------------
1 |
2 | function clone(object)
3 | local lookup_table = {}
4 | local function _copy(object)
5 | if type(object) ~= "table" then
6 | return object
7 | elseif lookup_table[object] then
8 | return lookup_table[object]
9 | end
10 | local new_table = {}
11 | lookup_table[object] = new_table
12 | for key, value in pairs(object) do
13 | new_table[_copy(key)] = _copy(value)
14 | end
15 | return setmetatable(new_table, getmetatable(object))
16 | end
17 | return _copy(object)
18 | end
19 |
20 | --Create an class.
21 | function class(super, classname)
22 | local superType = type(super)
23 | local cls
24 |
25 | if superType ~= "function" and superType ~= "table" then
26 | superType = nil
27 | super = nil
28 | end
29 |
30 | if superType == "function" or (super and super.__ctype == 1) then
31 | -- inherited from native C++ Object
32 | cls = {}
33 |
34 | if superType == "table" then
35 | -- copy fields from super
36 | for k,v in pairs(super) do cls[k] = v end
37 | cls.__create = super.__create
38 | cls.super = super
39 | else
40 | cls.__create = super
41 | end
42 |
43 | cls.ctor = function() end
44 | cls.reset = function() end
45 | cls.__cname = classname
46 | cls.__ctype = 1
47 |
48 | function cls.New(...)
49 | local instance = cls.__create(...)
50 | -- copy fields from class to native object
51 | for k,v in pairs(cls) do instance[k] = v end
52 | instance.class = cls
53 | instance:ctor(...)
54 | return instance
55 | end
56 |
57 | else
58 | -- inherited from Lua Object
59 | if super then
60 | cls = clone(super)
61 | cls.super = super
62 | else
63 | cls = {ctor = function() end,reset = function() end}
64 | end
65 |
66 | cls.__cname = classname
67 | cls.__ctype = 2 -- lua
68 | cls.__index = cls
69 |
70 | function cls.New(...)
71 | local instance = setmetatable({}, cls)
72 | instance.class = cls
73 | instance:ctor(...)
74 | return instance
75 | end
76 | end
77 |
78 | return cls
79 | end
80 |
81 |
82 | --exports.class = class
83 | --exports.clone = clone
--------------------------------------------------------------------------------
/src/test/lua/logic/BattleRoom.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by hejincheng.
4 | --- DateTime: 2022/8/8 15:33
5 |
6 | require "lib/class"
7 | require "logic/UnitManager"
8 |
9 | ---@class BattleRoom : table
10 | BattleRoom = class(nil, 'BattleRoom');
11 |
12 | function BattleRoom:ctor(_battleId)
13 | self.battleId = _battleId
14 | self.a = 0
15 | --print("ctor:" .. tostring(self.battleId))
16 | end
17 |
18 | function BattleRoom:Init()
19 | self.unitManager = UnitManager.New()
20 | --print("Init:" .. tostring(self.battleId))
21 | end
22 |
23 | function BattleRoom:Run()
24 | --print("Run:" .. tostring(self.battleId))
25 | --if self.unitManager then
26 | -- self.unitManager:Update()
27 | --end
28 | self.a = self.a + 1
29 | --print(a)
30 | end
31 |
32 | function BattleRoom:Close()
33 | if self.unitManager then
34 | self.unitManager:Close()
35 | end
36 |
37 | print(tostring(self.battleId) .. " : " .. tostring(self.a))
38 | end
39 |
40 | return BattleRoom;
--------------------------------------------------------------------------------
/src/test/lua/logic/UnitManager.lua:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by hejincheng.
4 | --- DateTime: 2022/8/8 15:46
5 |
6 | require "lib/class"
7 |
8 | ---@class UnitManager : table
9 | UnitManager = class(nil, 'UnitManager');
10 |
11 | function UnitManager:ctor()
12 |
13 | end
14 |
15 | function UnitManager:Update()
16 |
17 | end
18 |
19 | function UnitManager:Close()
20 |
21 | end
22 |
23 | return UnitManager;
--------------------------------------------------------------------------------
/src/test/resources/ServerLib.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | --- 服务端Java调用库
9 |
10 | require "Lib/class"
11 |
12 | ---@class ServerLib : table
13 | ${FIELDS_COMMENT}ServerLib = class(nil, 'ServerLib');
14 |
15 | function ServerLib:ctor()
16 | ${SELF_FIELDS}end
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/test/resources/ServerLibLuaFile.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | ${COMMENT}
9 | require "Lib/class"
10 |
11 | ---@class ${NAME} : ${SUPER_CLASS_COMMENT}
12 | ${NAME} = class(${SUPER_CLASS}, '${NAME}');
13 |
14 | function ${NAME}:ctor()
15 | end
16 |
17 | ${FUNCTIONS}return ${NAME};
--------------------------------------------------------------------------------
/src/test/resources/ServerModelLuaFile.template:
--------------------------------------------------------------------------------
1 | ---
2 | --- Generated by EmmyLua(https://github.com/EmmyLua)
3 | --- Created by ${USER}.
4 | --- DateTime: ${DATE} ${TIME}
5 | ---
6 | --- 通过Java工具类自动生成,请勿修改,重新生成会被覆盖
7 | ---
8 | ${COMMENT}
9 | require "Lib/class"
10 |
11 | ---@class ${NAME} : ${SUPER_CLASS_COMMENT}
12 | ${FILEDS_COMMENTS}${NAME} = class(${SUPER_CLASS}, '${NAME}');
13 |
14 | ${CTOR_FUNCTION}
15 | return ${NAME};
--------------------------------------------------------------------------------
/src/test/resources/log4j2.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${date:yyyy-MM-dd}
6 | %d{yyyy-MM-dd HH:mm:ss,SSS} (%F:%L) :%m %n
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 |
--------------------------------------------------------------------------------