Skip to content

Commit 6bf67e8

Browse files
committed
update
1 parent f4d85c6 commit 6bf67e8

11 files changed

+266
-4
lines changed

docs/.vitepress/sidebar.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ export const sidebar : DefaultTheme.Sidebar = {
6868
{ text: "库的导出", link: "/zh/quickstart/09library/02export-library" }
6969
]
7070
},
71-
{
71+
{
7272
text: "MNI框架",
7373
items: [
7474
{ text: "介绍", link: "/zh/quickstart/10mni/01mni-framework" },
75-
{ text: "使用MNI框架", link: "/zh/quickstart/10mni/02use-mni-framework" },
76-
{ text: "MNI框架的实现", link: "/zh/quickstart/10mni/03mni-framework-implementation" }
75+
{ text: "MNI框架的实现", link: "/zh/quickstart/10mni/02mni-framework-implementation" }
76+
{ text: "直接使用java变量和函数", link: "/zh/quickstart/10mni/03javavar" },
7777
]
7878
},
7979
{

docs/zh/quickstart/07generic/03generic_class.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ClassName<type T>{
3333
}
3434
3535
func main(){
36-
ClassName<int> c = ClassName(5);
36+
ClassName<int> c = ClassName(5); //创建泛型类的对象
3737
print(c.getValue()); //输出5
3838
}
3939
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
TODO
2+
3+
但是第十章是写了的!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
lastUpdate: true
3+
---
4+
5+
# MNI框架
6+
7+
MNI框架(Minecraft Native Implement Framework)是MCFPP提供的一个在编译过程中与JVM虚拟机交互的逻辑。MNI允许开发者在mcfpp中调用Java方法,或者使用Java实现MCFPP函数的功能。MNI和Java中的JNI较为类似,但是比JNI更加简洁易用。
8+
9+
在MCFPP中,有大量的函数都是通过MNI实现的,例如`print`函数。下面是一个例子,展示`print`函数利用MNI的实现过程。
10+
11+
```mcfpp
12+
//定义print函数以及它的重载
13+
func print(int i) = top.mcfpp.lang.System.print;
14+
func print(any a) = top.mcfpp.lang.System.print;
15+
```
16+
17+
```java
18+
//print函数在java中的具体实现
19+
package top.mcfpp.lang;
20+
21+
//略去import函数
22+
23+
public class System extends MNIMethodContainer {
24+
25+
@NotNull
26+
@Override
27+
public Function4<Var<?>[], Var<?>[], CanSelectMember, ValueWrapper<Var<?>>, java.lang.Void> getMNIMethod(@NotNull String name) {
28+
return methods.get(name);
29+
}
30+
31+
static HashMap<String, Function4<Var<?>[], Var<?>[], CanSelectMember, ValueWrapper<Var<?>>, java.lang.Void>> methods;
32+
33+
static {
34+
methods = new HashMap<>();
35+
//实现print函数
36+
methods.put("print", (vars, vars2, canSelectMember, varValueWrapper) -> {
37+
//只会有一个参数哦
38+
var value = vars2[0];
39+
if (value instanceof MCInt) print((MCInt) value);
40+
else print(value);
41+
return null;
42+
});
43+
}
44+
45+
@InsertCommand
46+
public static void print(@NotNull MCInt var) {
47+
if (var instanceof MCIntConcrete varC) {
48+
//是确定的,直接输出数值
49+
Function.Companion.addCommand("tellraw @a " + varC.getValue());
50+
}else {
51+
Function.Companion.addCommand("tellraw @a " + new JsonTextNumber(var).toJson());
52+
}
53+
}
54+
55+
@InsertCommand
56+
public static void print(@NotNull Var<?> var){
57+
Function.Companion.addCommand("tellraw @a " + "\"" +var + "\"");
58+
}
59+
}
60+
```
61+
62+
在接下来的章节中,我们将会详细介绍MNI框架的使用方法。
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
lastUpdate: true
3+
---
4+
5+
# MNI函数
6+
7+
## 声明
8+
9+
如果我们想要声明一个函数的具体实现逻辑是由Java代码实现的,我们就需要将它声明为**Native函数**。Native函数的声明格式如下:
10+
11+
```mcfpp
12+
func 函数名(参数列表) = Java类名.函数名;
13+
```
14+
15+
例如我们刚刚例子中提到的`print`函数,它的声明是:
16+
17+
```mcfpp
18+
func print(int i) = top.mcfpp.lang.System.print;
19+
```
20+
21+
它表示,print函数拥有一个int类型的参数,同时它的逻辑交给了`top.mcfpp.lang.System`类的`print`函数来实现。注意这里的类名需要是完全限定名,需要包含包名,否则编译器将无法找到这个类。
22+
23+
## 实现
24+
25+
`System`类被称为**MNI实现类**`print`函数的具体逻辑就是在这个类中实现的。MNI的实现类必须继承`MNIMethodContainer`类:
26+
27+
```kotlin
28+
package top.mcfpp.model.function
29+
30+
typealias MNIMethod = (Array<Var<*>?>, Array<Var<*>?>, CanSelectMember?, ValueWrapper<Var<*>>) -> Void
31+
32+
abstract class MNIMethodContainer{
33+
34+
abstract fun getMNIMethod(name: String): MNIMethod
35+
36+
}
37+
```
38+
39+
其中,定义了一个名为`getMNIMethod`的抽象方法,它的参数是一个函数名,即在之前声明native方法的时候,在等号后面的部分,类名.函数名中的函数名。方法需要根据传入的函数名,返回一个`MNIMethod`函数。
40+
41+
`MNIMethod`是一个lambda函数类型,它接受四个参数:
42+
43+
* `Array<Var<*>?> readonlyArgs`:传入函数的泛型参数数组。参数的顺序和定义一致。
44+
45+
* `Array<Var<*>?> normalArgs`:传入函数的普通参数数组。参数的顺序和定义一致。
46+
47+
* `CanSelectMember? caller`:这个函数的调用者。比如对于`a.test()`,变量`a`就是调用者。如果调用者不存在,比如对于全局函数,那么这个参数就是`null`
48+
49+
* `ValueWrapper<Var<*>> returnVar`:这个参数即为函数的返回值。它的成员变量`value`即为函数的返回值对应的变量,可以通过修改value达到返回某个值的目的。如果函数没有返回值,可以不用管这个参数。
50+
51+
在MNI调用执行Native函数的过程中,将会自动调用`getMNIMethod`方法,根据函数名获取到对应的函数,传入上述四个参数,然后执行这个函数。
52+
53+
一般来说,我们会在MNI实现类中定义一个静态的`HashMap`,用于存储所有的MNI函数。这个`HashMap`的key是函数名,value是对应的`MNIMethod`函数。在`getMNIMethod`方法中,我们只需要根据传入的函数名,从`HashMap`中获取到对应的函数即可,例如对于`System`类,我们的实现是这样的:
54+
55+
```java
56+
package top.mcfpp.lang;
57+
58+
//略去import函数
59+
60+
public class System extends MNIMethodContainer {
61+
62+
@NotNull
63+
@Override
64+
public Function4<Var<?>[], Var<?>[], CanSelectMember, ValueWrapper<Var<?>>, java.lang.Void> getMNIMethod(@NotNull String name) {
65+
return methods.get(name);
66+
}
67+
68+
static HashMap<String, Function4<Var<?>[], Var<?>[], CanSelectMember, ValueWrapper<Var<?>>, java.lang.Void>> methods;
69+
70+
static {
71+
methods = new HashMap<>();
72+
//实现print函数
73+
methods.put("print", (vars, vars2, canSelectMember, varValueWrapper) -> {
74+
//具体逻辑在这里实现
75+
});
76+
}
77+
}
78+
```
79+
80+
其中,我们在`System`类中定义了一个`HashMap`,并在静态代码块中初始化了`print`函数的实现。现在,我们来具体实现这个函数。
81+
82+
```java
83+
//...
84+
85+
static {
86+
//...
87+
//实现print函数
88+
methods.put("print", (vars, vars2, canSelectMember, varValueWrapper) -> {
89+
//只会有一个参数哦
90+
var value = vars2[0];
91+
if (value instanceof MCInt) print((MCInt) value);
92+
else print(value);
93+
return null;
94+
});
95+
}
96+
97+
@InsertCommand
98+
public static void print(@NotNull MCInt var) {
99+
if (var instanceof MCIntConcrete varC) {
100+
//是确定的,直接输出数值
101+
Function.Companion.addCommand("tellraw @a " + varC.getValue());
102+
}else {
103+
Function.Companion.addCommand("tellraw @a " + new JsonTextNumber(var).toJson());
104+
}
105+
}
106+
107+
@InsertCommand
108+
public static void print(@NotNull Var<?> var){
109+
Function.Companion.addCommand("tellraw @a " + "\"" +var + "\"");
110+
}
111+
112+
//...
113+
```
114+
115+
首先,我们获取到了传入`print`的参数。由于`print`函数只有一个普通参数,因此我们直接使用`var2[0]`就可以获取到这个传入的参数了。此后,我们使用`instanceof`关键字判断这个参数的类型,如果是`MCInt`类型,那么我们就调用`print(MCInt var)`函数。在不同类型对应的函数中,我们再进行具体的实现。
116+
117+
:::tip
118+
`Function.addCommand`函数用于向当前正在编译的mcf函数的末尾添加一条命令
119+
:::
120+
121+
## 调用
122+
123+
在MCFPP中,我们可以直接调用MNI函数,就像调用普通函数一样。例如:
124+
125+
```mcfpp
126+
print(5);
127+
```
128+
129+
但是MNI函数和普通函数一个非常重要的区别就是,MNI是在编译期执行的。在编译到此函数的时候,将会执行这个函数。也就是说,MNI函数的执行和逻辑语句无关,MNI函数也不会出现在数据包中。
130+
131+
例如:
132+
133+
```mcfpp
134+
if(b){
135+
print(7);
136+
}else{
137+
print(8);
138+
}
139+
```
140+
141+
编译器编译的时候是从上往下编译的,并且编译过程中不会在意逻辑语句。因此,无论`b`的值是什么,编译器始终都会先后遇到`print(7)``print(8)`,并且执行这两个函数。因此,数据包中始终会有`tellraw @a 7``tellraw @a 8`这两条命令。
142+
143+
当然,由于if的两个分支会对应两个不同的函数,因此`tellraw @a 7``tellraw @a 8`会分别出现在两个函数中。但是若调用的MNI函数的实现不同,那么可能会出现更多意想不到的情况。因此在使用MNI函数的时候,应当额外注意这一点。

docs/zh/quickstart/10mni/02use-mni-framework.md

Whitespace-only changes.

docs/zh/quickstart/10mni/03javavar.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
lastUpdate: true
3+
---
4+
5+
# JavaVar
6+
7+
在MCFPP中有一个相当特殊的变量类型,即`JavaVar`。它只能被MNI函数返回,不能被直接创建。`JavaVar`也只存在于编译过程中,它表示了编译过程中的一个Java变量,同样也属于MNI框架的一部分。
8+
9+
`JavaVar`变量仅可通过MNI的返回值获取。例如我们有MNI函数实现如下:
10+
11+
```mcfpp
12+
func getJavaVar() = top.mcfpp.lang.JavaVar.getJavaVar;
13+
```
14+
15+
```java
16+
import java.util.Random;
17+
18+
//...
19+
method.add("getJavaVar", (readonlyArgs, normalArgs, caller, returnVar) -> {
20+
returnVar.value = new JavaVar(new Random()); //返回一个随机数生成器
21+
return null;
22+
});
23+
//...
24+
```
25+
26+
这个MNI函数将会返回一个`JavaVar`变量,它的值是`Random`类的一个实例。接下来,我们可以在mcfpp中调用这个函数:
27+
28+
```mcfpp
29+
var random = getJavaVar();
30+
```
31+
32+
现在,random是一个JavaVar类型的变量,它的值是`Random`类的一个实例。接下来,我们可以去调用它的各种方法:
33+
34+
```mcfpp
35+
//调用方法
36+
var nextInt = random.nextInt(100);
37+
```
38+
39+
调用`JavaVar`类型变量的成员方法得到的变量仍然是`JavaVar`类型的变量。得益于MCFPP和Java/Kotlin语法的极大相似,我们可以几乎无缝在MCFPP中调用Java/Kotlin的方法。
40+
41+
同样,在适当的情况下,编译器会完成部分类型的MCFPP变量和Java变量之间的自动转换。
42+
43+
```mcfpp
44+
int qwq = 100; //MCFPP变量
45+
46+
var nextInt = random.nextInt(qwq); //MCFPP变量自动转换为Java变量,并传入Java方法中
47+
48+
int nextMCFPPInt = nextInt; //Java变量自动转换为MCFPP变量
49+
```
50+
51+
这样的转换有一个前提,也就是,MCFPP变量必须是已知的,才能被自动转换为对应的Java变量,否则将会转换为为`Var`类,即所有MCFPP变量在编译器中的基类。当然,如果Java方法默认需要一个`Var`类型的参数,编译器就不会执行自动转换。

0 commit comments

Comments
 (0)