Fabric API

Fabric API

106M Downloads

How to remap the reflect params? Help Wanted

FeelingBored opened this issue · 56 comments

commented

everything was remaped but the reflect params.

I have search on the fabric wiki(zh-cn) but got little message about that.

            //反射
            Field packetHandlers = NetworkState.class.getDeclaredField("packetHandlers");
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField("HANDLER_STATE_MAP");
            Class<?> PacketHandler = Class.forName("net.minecraft.network.NetworkState$PacketHandler");
            Method register = PacketHandler.getMethod("register", Class.class, Supplier.class);

I want to make it remaped, but dont know how to do.
I know I can got the remaped name on the "yarn", but I guess that if I remap it by myself, it would not work in the idea's "runClient".
But I dont remap it, it cannot work but in the idea's "runClient" and "runServer".

Sorry for my poor English, Can anyone help me, please?
Thanks. :)

commented
            //反射
            Field packetHandlers = NetworkState.class.getDeclaredField("field_20595");
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField("field_11687");
            Class<?> PacketHandler = Class.forName("net.minecraft.class_2539$class_4532");
            Method register = PacketHandler.getMethod("method_22313", Class.class, Supplier.class);

The result excepted like this.
https://github.com/FabricMC/yarn/blob/20w28a/mappings/net/minecraft/network/NetworkState.mapping

commented

I assume this is for custom packets? Would a CustomPayloadS2CPacket or CustomPayloadC2SPacket suffice? The custom payload packets do have the advantage of not breaking vanilla clients (clients ignore packets they don't recognize).

Otherwise you could use an access widener (sadly we don't have a translation of this yet) to make the package-private PacketHandler class visible in dev and runtime. Then use mixin to access the map (HANDLER_STATE_MAP).

commented

If you need to do reflection, I'd take a look at loader's mappings resolver to remap the names during runtime. (most likely inputting intermediary as namespace. And then intermediary names)

commented

If you need to do reflection, I'd take a look at loader's mappings resolver to remap the names during runtime.

OK, Thanks you very much!!!
I'm gonna to research the CustomPayloadS2CPacket and CustomPayloadC2SPacket that you told me.
And try to read the "access widener"
Thanks for your patient!: D

commented

I did assign liach to this issue since he knows a bit more Chinese than I do to see if he can help. Hopefully he responds soon.

commented

Which loom version are you using? (You can find it at the top of your build.gradle)

I found where the problem is.
"Accessible class net/minecraft/network/NetworkState$NetworkHandler"
should be
"Accessible class net/minecraft/network/NetworkState$PacketHandler"

SORRY FOR WASTING YOU MUCH TIME!!!!
And thanks for your patient very much! ;-;

commented

Which loom version are you using? (You can find it at the top of your build.gradle)

g
don't know what happen...suddenly.
it cause failed to export the remapjar.

but after comment the "Accessible class net/minecraft/network/NetworkState$PacketHandler" and refresh project and gensources and uncomment the "Accessible class net/minecraft/network/NetworkState$PacketHandler" and refresh project and gensources.and remove the access code about "NetworkState.PacketHandler" and remapJar and readd the access code and remapJar.it works again(can export remapJar).
It's very difficult to explain in my poor English.

Finally, I test it in local server, and it successfully work!
And then, I try to move it to my remote server for testting.

Thanks you two very much! :) (would maybe meet some bugs.

commented

I did assign liach to this issue since he knows a bit more Chinese than I do to see if he can help. Hopefully he responds soon.

Hard to understand "access widener".
What's the "namespace should match the mappings namespace of your mod's source code"?does it mean the main class?

Hard to understand"The file must start with the following header, namespace should match the mappings namespace of your mod's source code, this is usually named. Loom will remap the access widener file for you into intermediary along with your mod. If you use a custom RemapJarTask, set remapAccessWidener property on it to true to ensure this happens."

hope for some examples. x(

The following I got it, but still dont know how it works.
examples excepted!plz!

commented

And I try to replace the reflect param by myself and export the remapjar.
and put it in the mods of server.

But it told me that
"[14:38:38] [main/INFO]: [STDERR]: java.lang.IllegalAccessException: Class net.miya.autologin.packet.PacketRegister can not access a member of class net.minecraft.class_2539$class_4532 with modifiers "public""
which make me confused and sad. :(
it works in the idea's task(runClient and runServer)

commented

Did you remember to put the access widener in your fabric.mod.json too?

commented

Did you remember to put the access widener in your fabric.mod.json too?

it works in the idea, but dont work without idea.
If I need to make it both work with putting a "access widener" as AccessWideners

I cannot understand
The file must start with the following header, namespace should match the mappings namespace of your mod's source code, this is usually named. Loom will remap the access widener file for you into intermediary along with your mod. If you use a custom RemapJarTask, set remapAccessWidener property on it to true to ensure this happens.
What does the "namespace should match the mappings namespace of your mod's source code" mean?
and "custom RemapJarTask" and "set remapAccessWidener property on it to true"

I want some examples. :(
Thanks.

commented

QQ截图20200715155844
I dont know the "namespace" should write "net" or "net.miya.autologin" or else.

commented

"custom RemapJarTask" I dont know what its"custom"mean.
and I dont know where to set the "remapAccessWidener property" to true.I cannot see anything in the Edit 'autologin[remapJar]'

commented

The namespace refers to the mappings namespace. You would likely put named in that field.

commented

a
b
c

commented

Change net to named

commented

The namespace refers to the mappings namespace. You would likely put named in that field.

does it mean "named" or the mod's id in "fabric.mod.json"?

commented

Change net to named

d
like this?

commented

Yes.

Also, your class names should use slashes (/) instead of dots (.). So your other line should read:

Accessible class net/minecraft/network/NetworkState$NetworkHandler
commented

Yes.

Also, your class names should use slashes (/) instead of dots (.). So your other line should read:

Accessible class net/minecraft/network/NetworkState$NetworkHandler

OMG, sorry for my misunderstanding of the article.
I think it means "replace / with . and replace $ with /" before.
sorry.

Class names are separated with a / and not .
For inner classes, you should use $ instead of /
commented

Yes.

Also, your class names should use slashes (/) instead of dots (.). So your other line should read:

Accessible class net/minecraft/network/NetworkState$NetworkHandler

actually, I dont know what "access widener" does.
I try to remap the reflect params by myself like this.

            //反射
            Field packetHandlers = NetworkState.class.getDeclaredField("field_20595");
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField("field_11687");
            Class<?> PacketHandler = Class.forName("net.minecraft.class_2539$class_4532");
            Method register = PacketHandler.getMethod("method_22313", Class.class, Supplier.class);

And I aslo put the access widener in the fabric.mod.json...
But how to solve this problem?…
a
But it works on idea(runServer)
b

which made me confused. X(

commented

The access widener makes the class public, so you shouldn't need to use reflection. Instead, once you've finished editing the access widener, refresh the gradle project in intellij, and run genSources again, and you should be able to access the class normally from your code.

commented

The access widener makes the class public, so you shouldn't need to use reflection. Instead, once you've finished editing the access widener, refresh the gradle project in intellij, and run genSources again, and you should be able to access the class normally from your code.

refresh the gradle project in intellij?do you mean restart the idea or execute the command "gradlew idea"?

commented

image

image

commented

a
b
c
d
e

I have refresh the project, and run task"genSources".
But I dont know why can I access it...
Is there anything wrong on me?

commented

Which loom version are you using? (You can find it at the top of your build.gradle)

commented

Which loom version are you using? (You can find it at the top of your build.gradle)

id 'fabric-loom' version '0.4-SNAPSHOT'
f

commented

a
为什么Entity可以,PlayerEntity可以,而NetworkState不行?

commented

a
Hard to understand why.Cast it to Object and Cast the Object to Accessor.

commented

一般如果是final class的话不能直接cast成accessor interface,不是final class的话可以直接cast
如果mixin是class别cast,运行时会炸

commented

一般如果是final class的话不能直接cast成accessor interface,不是final class的话可以直接cast
如果mixin是class别cast,运行时会炸

那个 MixinNetworkStateAccess 是Interface。
那个NetworkState是public enum NetworkState,但我大概知道为什么了,既然告诉我final不可以直接cast的话。
那将它cast为Object之后,然后再将它cast为MixinNetworkStateAccess这样是可以的吗?(至少成功了)
a
相关代码

另外我很模糊地明白了为什么那个HANDLER_STATE_MAP为什么是null了,这应该涉及到java自身的加载机制吧?因为我在调用那个register method的时候,他从register method暂时中断,然后跳到NetworkState那边进行初始化它的enum(如PLAY),然后再回来继续执行 register method。

总之很感谢你耐心的回答!:)
我还得继续研究下去。

我在想为什么我在它的构造方法里写获取HANDLER_STATE_MAP不起作用的问题,想了想,大概可能是因为自己调用自己内部的东西,所以才不会触发静态代码块的加载?我是这么想的(上网查了一下)

commented

a
b
本来是想在PacketRegister那边弄个静态代码块来加载的,网上说了在静态成员被调用的时候就会调用静态代码块,但很遗憾虽然这块代码运行了,但是NetworkState注入的静态代码块不认账,仍然是null
然后我将他挪到了我的Mod入口方法,总算是成功了(成功拿到了Instance),虽然不理解到底为什么。

commented

你下次考虑代码发github gist吧,别截图了……你能发图就能发github gist,githubusercontent国内不是已经墙了 😓
mixin把mixin class里面的static block东西加到原来class的<clinit>的结尾,你发完整代码才能分析

commented

你下次考虑代码发github gist吧,别截图了……你能发图就能发github gist,githubusercontent国内不是已经墙了 😓
mixin把mixin class里面的static block东西加到原来class的<clinit>的结尾,你发完整代码才能分析

好的,我还是第一次知道有这样的服务。
https://gist.github.com/MikuScarlet/2607e1e95f9f9a995834688601f3ced8
另外之前用Inject注入感觉直接写static block好像没区别…(指在当时都不执行)
另外我是全程开着魔法的,不然上github超慢,上fabric wiki超慢,mod连download asset都做不到。

我先将 static block重新改为inject注入看看(得到了一样的结果,具体细节在上面的gist地址里,果然还是需要在入口那边进行获取,只是把一样的代码移个位置效果就不一样了)

commented

哦,你这个循环呼叫了
加载NetworkState的时候呼叫了你的PacketRegister,而你的PacketRegister以为NetworkState已经跑完<clinit>了。你只要在两个地方中间的一个把NetworkState的map放到你PacketRegister里面应该就行了

commented

哦,你这个循环呼叫了
加载NetworkState的时候呼叫了你的PacketRegister,而你的PacketRegister以为NetworkState已经跑完<clinit>了。你只要在两个地方中间的一个把NetworkState的map放到你PacketRegister里面应该就行了

你的意思是指只保留一个“把NetworkState的map放到PacketRegister里面”的部分吗?
大概了解你的意思了,确实这样搞的话编译器也很烦恼要怎么做。

成功了!真的是太感谢你了!
按照着上面你们三位指导我的做法,我将注入NetworkState部分的代码全删了,用Accessor代替获取Handlers。
成功的代码
总感觉对这方面的理解稍为更进一步了,真的十分感谢你们!:) (学到和认识了好多新东西,Accessor,access widener,以及Ctrl + Shift + F全局搜索原来要下载源码文件才能搜到…之前不会注入的时候,就靠这个搜例子…但因为没有下载源码的原因,几乎搜不到几个)

commented

I assume this is for custom packets? Would a CustomPayloadS2CPacket or CustomPayloadC2SPacket suffice? The custom payload packets do have the advantage of not breaking vanilla clients (clients ignore packets they don't recognize).

Otherwise you could use an access widener (sadly we don't have a translation of this yet) to make the package-private PacketHandler class visible in dev and runtime. Then use mixin to access the map (HANDLER_STATE_MAP).

the HANDLER_STATE_MAP in NetworkState doesn't inited.

`@Mixin(NetworkState.class)
public class MixinNetworkState {
@shadow
public static Map<Class>, NetworkState> HANDLER_STATE_MAP;

@Shadow
private Map<NetworkSide, ? extends NetworkState.PacketHandler<?>> packetHandlers;


@Inject(at = @At("TAIL"), method = "<init>")
private void init(String a, int b, int id, NetworkState.PacketHandlerInitializer packetHandlerInitializer, CallbackInfo info){
    PacketRegister.HANDLER_STATE_MAP = HANDLER_STATE_MAP;
    PacketRegister.handlers_map.put(this, packetHandlers);
}

}`

it print "null". And I try to inject the method "<clinit>" which I search in the Module and Package Names
but it dont work(no printing)
I dont how to do now....

commented

a
b
c
related code imgs.

commented
            //反射
            Field packetHandlers = NetworkState.class.getDeclaredField("packetHandlers");
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField("HANDLER_STATE_MAP");
            Class<?> PacketHandler = Class.forName("net.minecraft.network.NetworkState$PacketHandler");
            Method register = PacketHandler.getMethod("register", Class.class, Supplier.class);

            //反射
            String namespace = "intermediary"; // 别的不可靠
            MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
            Field packetHandlers = NetworkState.class.getDeclaredField(resolver.mapFieldName(namespace, "net/minecraft/class_2539", "field_20595", "Ljava/util/Map;"));
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField(resolver.mapFieldName(namespace, "net/minecraft/class_2539", "field_11687", "Ljava/util/Map;"));
            Class<?> PacketHandler = Class.forName(resolver.mapClassName(namespace, "net/minecraft/class_2539$class_4532"));
            Method register = PacketHandler.getMethod(resolver.mapClassName(namespace, "net/minecraft/class_2539$class_4532", "method_22313", "(Ljava/lang/Class;Ljava/util/function/Supplier;)Lnet/minecraft/class_2539$class_4532;"), Class.class, Supplier.class);

✔️


你编mod的时候看到的“源码”实际上并不是真正运行的东西,真正运行的只有bytecode。yarn本质类似于文档,没有它你可以写mod但是会超级麻烦而已。yarn里面的东西到运行的时候就没了,只有intermediary可靠。

别急着拿去用呢

你用这种方法黑包强烈不推荐,完全破坏和原版网络系统的兼容性。还有如果你要呼叫东西现在都用mixin,reflection就呵呵吧

commented
            //反射
            Field packetHandlers = NetworkState.class.getDeclaredField("packetHandlers");
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField("HANDLER_STATE_MAP");
            Class<?> PacketHandler = Class.forName("net.minecraft.network.NetworkState$PacketHandler");
            Method register = PacketHandler.getMethod("register", Class.class, Supplier.class);

            //反射
            String namespace = "intermediary"; // 别的不可靠
            MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
            Field packetHandlers = NetworkState.class.getDeclaredField(resolver.mapFieldName(namespace, "net/minecraft/class_2539", "field_20595", "Ljava/util/Map;"));
            Field HANDLER_STATE_MAP = NetworkState.class.getDeclaredField(resolver.mapFieldName(namespace, "net/minecraft/class_2539", "field_11687", "Ljava/util/Map;"));
            Class<?> PacketHandler = Class.forName(resolver.mapClassName(namespace, "net/minecraft/class_2539$class_4532"));
            Method register = PacketHandler.getMethod(resolver.mapClassName(namespace, "net/minecraft/class_2539$class_4532", "method_22313", "(Ljava/lang/Class;Ljava/util/function/Supplier;)Lnet/minecraft/class_2539$class_4532;"), Class.class, Supplier.class);

✔️

你编mod的时候看到的“源码”实际上并不是真正运行的东西,真正运行的只有bytecode。yarn本质类似于文档,没有它你可以写mod但是会超级麻烦而已。yarn里面的东西到运行的时候就没了,只有intermediary可靠。

别急着拿去用呢

你用这种方法黑包强烈不推荐,完全破坏和原版网络系统的兼容性。还有如果你要呼叫东西现在都用mixin,reflection就呵呵吧

关于第一段话,我在写的过程中已经慢慢理解了,大概就是反混淆过来的代码然后让你写没有混淆过的代码,然后编译的时候自动帮你混淆回去吧?我大概了解了…但当时只是没有想到原来不会混淆反射的参数。

另外确实,这个破坏和原版网络系统的兼容性,因为只要有一方没有这个数据包的类,就会直接中断连接。(但实际上我在尝试制作自动登录mod,作用原理大概就是进入游戏的时候读取目录下的密码,发包给服务器,服务器接收到包之后对其进行密码核对,如果不对就踢出,超过两秒没有发密码包的也会被踢出)

我现在已经意识到不能使用reflection(反射)了,正在尝试用其他方式替代,比如使用mixin(但我对这个不是很熟,尝试过成功的注入只有"HEAD"和"TAIL"还有"FILED"。但是"INVOKE_ASSGIN"不明白为什么无法使用,明明也按报错提示要求填写好参数了,没有任何头绪。)

现在我已经按照上面两位的指导,去研究相关的wiki了。
但是我刚刚发的使用mixin获取里面的属性失败的情况让我很头疼…不知如何是好了…如果是reflection应该很容易就做到吧,但这似乎是不被推荐做的。

commented

你这个的话我个人推荐发这种包,可以在玩家进服务器之前验证 https://wiki.vg/Protocol#Login_Plugin_Request
你用mixin是加注代码,而Mixin有accessor可以让你写个interface然后呼叫interface,mixin运行时打补丁

实际上反射可以用,但是你就要用mappingresolver而已,虽然你90%的反射用途可以拿别的更高效安全的方法取代

commented

你这个的话我个人推荐发这种包,可以在玩家进服务器之前验证 https://wiki.vg/Protocol#Login_Plugin_Request
你用mixin是加注代码,而Mixin有accessor可以让你写个interface然后呼叫interface,mixin运行时打补丁

实际上反射可以用,但是你就要用mappingresolver而已,虽然你90%的反射用途可以拿别的更高效安全的方法取代

好的,感谢你的帮助!以后会考虑去这么做的,现在的我还没这个能力去做这个事情(刚开始接触这方面的东西)。
如果可以的话,可以帮我看看半个小时前发的Comment吗?

commented

你那个应该直接传递原来的packetHandlers map,这样保证是同一个object兼容性更好
如果那个map是immutable map你就mixin里面写个static block,比如

static {
  packetHandlers = new HashMap<>(packetHandlers);
}
commented

I assume this is for custom packets? Would a CustomPayloadS2CPacket or CustomPayloadC2SPacket suffice? The custom payload packets do have the advantage of not breaking vanilla clients (clients ignore packets they don't recognize).
Otherwise you could use an access widener (sadly we don't have a translation of this yet) to make the package-private PacketHandler class visible in dev and runtime. Then use mixin to access the map (HANDLER_STATE_MAP).

the HANDLER_STATE_MAP in NetworkState doesn't inited.

`@Mixin(NetworkState.class)
public class MixinNetworkState {
@shadow
public static Map, NetworkState> HANDLER_STATE_MAP;

@Shadow
private Map<NetworkSide, ? extends NetworkState.PacketHandler<?>> packetHandlers;


@Inject(at = @At("TAIL"), method = "<init>")
private void init(String a, int b, int id, NetworkState.PacketHandlerInitializer packetHandlerInitializer, CallbackInfo info){
    PacketRegister.HANDLER_STATE_MAP = HANDLER_STATE_MAP;
    PacketRegister.handlers_map.put(this, packetHandlers);
}

}`

it print "null". And I try to inject the method "" which I search in the Module and Package Names
but it dont work(no printing)
I dont how to do now....

原文翻译

在类NetworkState中的HANDLER_STATE_MAP没有被加载

我尝试过在注入方法里打印它,但是它打印出来是"null",并且在查阅了java的那个文档之后,我发现了clinit这个关键字,
并打算注入他,并在里面只写上打印代码,然后将外面导致异常的代码注释了,但从头到尾都没有发生任何作用(打印HANDLER_STATE_MAP)

我不知道该怎么做了

commented

你那个应该直接传递原来的packetHandlers map,这样保证是同一个object兼容性更好
如果那个map是immutable map你就mixin里面写个static block,比如

static {
  packetHandlers = new HashMap<>(packetHandlers);
}

好的,但是它并没有发生任何问题,发生问题的是HANDLER_STATE_MAP(null)。

commented

你那个应该直接传递原来的packetHandlers map,这样保证是同一个object兼容性更好
如果那个map是immutable map你就mixin里面写个static block,比如

static {
  packetHandlers = new HashMap<>(packetHandlers);
}

好的,但是它并没有发生任何问题,发生问题的是HANDLER_STATE_MAP(null)。

我是通过一个Map,去储存它们的packetHandlers,Key是NetworkState,Value是Map<NetworkSide, ? extends NetworkState.PacketHandler<?>>,也就是PacketHandlers的变量类型

commented

d
b
a

他是不会打印的。

commented

accessor

我似乎明白你说的ACCESSOR是什么了,我这就去尝试一下。

commented

你这个的话我个人推荐发这种包,可以在玩家进服务器之前验证 https://wiki.vg/Protocol#Login_Plugin_Request
你用mixin是加注代码,而Mixin有accessor可以让你写个interface然后呼叫interface,mixin运行时打补丁

实际上反射可以用,但是你就要用mappingresolver而已,虽然你90%的反射用途可以拿别的更高效安全的方法取代

请问可以在哪里找到关于这方面(Accessor)的使用示例吗?

commented

a

我好像在API里面找到了,但之前我使用Ctrl + Shift + F搜索全局根本就搜不出来,奇怪……
今天去把API里的mixin都看一遍吧

a

commented

哦,你这个循环呼叫了
加载NetworkState的时候呼叫了你的PacketRegister,而你的PacketRegister以为NetworkState已经跑完<clinit>了。你只要在两个地方中间的一个把NetworkState的map放到你PacketRegister里面应该就行了

你好,我想向你请教一个问题(粗体部分是提问),我register了一个entity之后,并为他添加了sound,soundevent,还有model和renderer。一切运作正常。

并且我想尝试在服务端上安装,看看效果,并用没有安装这个mod的客户端进入这个服务器。但是奇怪的是,竟然能进去服务器,没有弹任何关于“你需要这个mod-version才能进入服务器”之类的信息。

并且我尝试使用summon指令spawn这个new entity type。令人伤心的是,出来的是一个小猪。

请问有什么方法可以要求玩家必须安装某个mod才能进入服务器吗?

我在中文wiki上并没有找到相关的内容,并且在英文wiki上面大概找了一下。
并且在issues上进行搜索keyword "install" "mod",但内容太多了…我不知道这是否存在…

commented

fabric不检查的,api只有个registry sync,客户端检测到不兼容主动离开服务器而已

commented

fabric不检查的,api只有个registry sync,客户端检测到不兼容主动离开服务器而已

这个过程是fabric自动完成的吗?我发现Registry有Item,以及EntityType

		RegistryAttributeHolder.get(Registry.ENTITY_TYPE)
				.addAttribute(RegistryAttribute.SYNCED);
		RegistryAttributeHolder.get(Registry.ITEM)
				.addAttribute(RegistryAttribute.SYNCED);

我的意思是,如果我注册了我的Item(比如一个食物)和EntityType(比如一只怪物)。并且只把他安装在 real server上,不安装在client那边。client能检测到不兼容吗?(事实上并没有检测,没有安装这个mod的客户端仍然能进入安装了mod的服务端)还是说需要做点其他的事情才能让client明白自己不兼容这个服务器然后主动离开??

现在我的方案就是服务端向客户端发送一个自定义包,自定义包包括这个mod的版本和它的名字。如果版本和名字对不上的话,就会自动断开服务器,如果不安装这个mod的话,就没有这个自定义包的定义,进入服务器的时候就会因为不认识这个自定义包而断开连接了。(但是感觉不是一个好方法)

commented

没有安装这个mod的客户端

你客户端要有fabric api的registry sync啊,原版客户端或者装fabric loader但是没有registry sync的客户端不会主动退出啊。

现在我的方案就是服务端向客户端发送一个自定义包,自定义包包括这个mod的版本和它的名字。如果版本和名字对不上的话,就会自动断开服务器,如果不安装这个mod的话,就没有这个自定义包的定义,进入服务器的时候就会因为不认识这个自定义包而断开连接了。

这个想法太差了
你应该让客户端回包,如果一定时间内没收到回复包就踢客户端,反正没有你的mod的客户端没法回包

commented

还有聊天请到 https://discord.gg/v6v4pMv ,github不适合聊天

commented

没有安装这个mod的客户端

你客户端要有fabric api的registry sync啊,原版客户端或者装fabric loader但是没有registry sync的客户端不会主动退出啊。

现在我的方案就是服务端向客户端发送一个自定义包,自定义包包括这个mod的版本和它的名字。如果版本和名字对不上的话,就会自动断开服务器,如果不安装这个mod的话,就没有这个自定义包的定义,进入服务器的时候就会因为不认识这个自定义包而断开连接了。

这个想法太差了
你应该让客户端回包,如果一定时间内没收到回复包就踢客户端,反正没有你的mod的客户端没法回包

其实不管是服务端还是客户端上我都安装了fabric api,因为客户端我还在使用之前的我自己制作的登录mod,它使用到了fabric api。我用的不是原版纯净的客户端。

还有聊天请到 https://discord.gg/v6v4pMv ,github不适合聊天

好的,我明白了…只是觉得这些也算是提问,并不是闲聊,所以就发在这里了。我会注意的。

最后感谢您提供的思路! :)

2020-7-18-14:45
另外,不知道是不是api版本不一样的原因,服务端上安装的API是553KB的,客户端上安装的API是596KB的,这会影响吗?(也无所谓了…已经使用另一种方式去尝试解决了。)