FabricMC / fabric

Essential hooks for modding with Fabric.
Apache License 2.0
2.36k stars 416 forks source link

How to remap the reflect params? Help Wanted #891

Closed FeelingBored closed 4 years ago

FeelingBored commented 4 years ago

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. :)

FeelingBored commented 4 years ago
            //反射
            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

i509VCB commented 4 years ago

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).

i509VCB commented 4 years ago

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)

FeelingBored commented 4 years ago

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

i509VCB commented 4 years ago

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.

FeelingBored commented 4 years ago

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!

FeelingBored commented 4 years ago

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)

Earthcomputer commented 4 years ago

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

FeelingBored commented 4 years ago

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.

FeelingBored commented 4 years ago

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

FeelingBored commented 4 years ago

"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]'

i509VCB commented 4 years ago

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

FeelingBored commented 4 years ago

a b c

Earthcomputer commented 4 years ago

Change net to named

FeelingBored commented 4 years ago

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"?

FeelingBored commented 4 years ago

Change net to named

d like this?

Earthcomputer commented 4 years ago

Yes.

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

Accessible class net/minecraft/network/NetworkState$NetworkHandler
FeelingBored commented 4 years ago

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 /
FeelingBored commented 4 years ago

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(

Earthcomputer commented 4 years ago

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.

FeelingBored commented 4 years ago

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"?

Earthcomputer commented 4 years ago

image

image

FeelingBored commented 4 years ago

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?

Earthcomputer commented 4 years ago

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

FeelingBored commented 4 years ago

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

FeelingBored commented 4 years ago

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! ;-;

FeelingBored commented 4 years ago

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.

FeelingBored commented 4 years ago

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<? extends Packet<?>>, 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....

FeelingBored commented 4 years ago

a b c related code imgs.

liach commented 4 years ago
//反射
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);

:x:

            //反射
            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);

:heavy_check_mark:


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

别急着拿去用呢

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

FeelingBored commented 4 years ago
            //反射
            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应该很容易就做到吧,但这似乎是不被推荐做的。

liach commented 4 years ago

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

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

FeelingBored commented 4 years ago

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

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

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

liach commented 4 years ago

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

static {
  packetHandlers = new HashMap<>(packetHandlers);
}
FeelingBored commented 4 years ago

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)

我不知道该怎么做了

FeelingBored commented 4 years ago

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

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

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

FeelingBored commented 4 years ago

你那个应该直接传递原来的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的变量类型

FeelingBored commented 4 years ago

d b a

他是不会打印的。

FeelingBored commented 4 years ago

accessor

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

FeelingBored commented 4 years ago

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

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

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

FeelingBored commented 4 years ago

a

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

a

FeelingBored commented 4 years ago

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

FeelingBored commented 4 years ago

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

liach commented 4 years ago

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

FeelingBored commented 4 years ago

一般如果是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不起作用的问题,想了想,大概可能是因为自己调用自己内部的东西,所以才不会触发静态代码块的加载?我是这么想的(上网查了一下)

FeelingBored commented 4 years ago

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

liach commented 4 years ago

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

FeelingBored commented 4 years ago

你下次考虑代码发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地址里,果然还是需要在入口那边进行获取,只是把一样的代码移个位置效果就不一样了)

liach commented 4 years ago

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

FeelingBored commented 4 years ago

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

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

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