Dijkstra Posted September 21, 2015 Posted September 21, 2015 I am making a mod that allows the user to change there skin game, it far from complete, but bubbled it down to just setting a skin me and sister to test on my server for performance and we both ended up with are skin set on minecraft.net, but in my dev environment my skin does change to the one i set the set of code i am test is package dijkstra.wardrobe.main; public class testReflections{ public static ResourceLocation oldSkin; public static AbstractClientPlayer player = Minecraft.getMinecraft().thePlayer; public static void load(){ Class Skin = AbstractClientPlayer.class; try { Field locationSkin = AbstractClientPlayer.class.getDeclaredField("locationSkin"); locationSkin.setAccessible(true); locationSkin.set(Minecraft.getMinecraft().thePlayer, setSkin(Players.valueOf(player.getDisplayName()))); } catch (Exception e){ e.printStackTrace(); } } private static ResourceLocation setSkin(Players player){ switch(player){ case Dijkstra: return new ResourceLocation("textures/players/Dijkstra.png"); case Pegasus: return new ResourceLocation("textures/players/Pegasus.png"); default: return null; } } private enum Players{ Dijkstra, Pegasus } } events package dijkstra.wardrobe.main; public class eventHandler{ public static boolean changed = true; @SubscribeEvent public void renderTick(TickEvent.RenderTickEvent event) { Minecraft mc = Minecraft.getMinecraft(); if(mc.theWorld != null) { if(event.phase == TickEvent.Phase.START) { if(changed){ testReflections.load(); changed = false; } } } } @SubscribeEvent public void PlayerLoggedInEvent(PlayerEvent.PlayerLoggedInEvent event){ changed = true; } } main file @EventHandler public static void PreLoad(FMLPreInitializationEvent PreEvent){ FMLCommonHandler.instance().bus().register(new eventHandler()); proxy.registerRenderInfo(); } Think that should be all you need if need more of the mod just ask but it is getting quite large Quote
Choonster Posted September 21, 2015 Posted September 21, 2015 In the obfuscated (non-development) environment, there's no field called locationSkin ; instead it has the SRG name field_110312_d . FML's ObfuscationReflectionHelper#setPrivateValue allows you to pass both the deobfuscated (MCP) and obfuscated (SRG) names of the field to set the value of. Either use it directly or adapt the logic to your class. Make sure you only reference client-only classes like Minecraft and AbstractClientPlayer from client-side code (i.e. only register your event handler in your client proxy). Edit: The SRG name of AbstractClientPlayer#locationSkin in 1.7.10 is actually field_110312_d . field_178865_e is the SRG name of NetworkPlayerInfo#locationSkin field in 1.8. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Dijkstra Posted September 23, 2015 Author Posted September 23, 2015 Ok so done use fml method still works fine in the dev environment but now crashes out of the dev environment crash report out of dev cpw.mods.fml.relauncher.ReflectionHelper$UnableToAccessFieldException: cpw.mods.fml.relauncher.ReflectionHelper$UnableToFindFieldException: java.lang.NoSuchFieldException: locationSkin at cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(ReflectionHelper.java:147) at cpw.mods.fml.common.ObfuscationReflectionHelper.setPrivateValue(ObfuscationReflectionHelper.java:91) at dijkstra.wardrobe.main.testReflections.load(testReflections.java:15) at dijkstra.wardrobe.main.eventHandler.renderTick(eventHandler.java:23) at cpw.mods.fml.common.eventhandler.ASMEventHandler_7_eventHandler_renderTick_RenderTickEvent.invoke(.dynamic) at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:138) at cpw.mods.fml.common.FMLCommonHandler.onRenderTickStart(FMLCommonHandler.java:335) at net.minecraft.client.Minecraft.func_71411_J(Minecraft.java:999) at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:898) at net.minecraft.client.main.Main.main(SourceFile:148) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) at net.minecraft.launchwrapper.Launch.main(Launch.java:28) Caused by: cpw.mods.fml.relauncher.ReflectionHelper$UnableToFindFieldException: java.lang.NoSuchFieldException: locationSkin at cpw.mods.fml.relauncher.ReflectionHelper.findField(ReflectionHelper.java:94) at cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(ReflectionHelper.java:143) ... 16 more Caused by: java.lang.NoSuchFieldException: locationSkin at java.lang.Class.getDeclaredField(Class.java:2062) at cpw.mods.fml.relauncher.ReflectionHelper.findField(ReflectionHelper.java:85) ... 17 more Quote
Choonster Posted September 23, 2015 Posted September 23, 2015 To answer the question in your previous post, only use reflection when you can't use a normal method call/field access. Examples of this include accessing non-public methods/fields or methods/fields with a dynamic name/argument list and instantiating a class that's only known at runtime (i.e. you have a Class object you want to create an instance of). Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Choonster Posted September 23, 2015 Posted September 23, 2015 Ok so done use fml method still works fine in the dev environment but now crashes out of the dev environment crash report out of dev cpw.mods.fml.relauncher.ReflectionHelper$UnableToAccessFieldException: cpw.mods.fml.relauncher.ReflectionHelper$UnableToFindFieldException: java.lang.NoSuchFieldException: locationSkin at cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(ReflectionHelper.java:147) at cpw.mods.fml.common.ObfuscationReflectionHelper.setPrivateValue(ObfuscationReflectionHelper.java:91) at dijkstra.wardrobe.main.testReflections.load(testReflections.java:15) at dijkstra.wardrobe.main.eventHandler.renderTick(eventHandler.java:23) at cpw.mods.fml.common.eventhandler.ASMEventHandler_7_eventHandler_renderTick_RenderTickEvent.invoke(.dynamic) at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:54) at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:138) at cpw.mods.fml.common.FMLCommonHandler.onRenderTickStart(FMLCommonHandler.java:335) at net.minecraft.client.Minecraft.func_71411_J(Minecraft.java:999) at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:898) at net.minecraft.client.main.Main.main(SourceFile:148) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:483) at net.minecraft.launchwrapper.Launch.launch(Launch.java:135) at net.minecraft.launchwrapper.Launch.main(Launch.java:28) Caused by: cpw.mods.fml.relauncher.ReflectionHelper$UnableToFindFieldException: java.lang.NoSuchFieldException: locationSkin at cpw.mods.fml.relauncher.ReflectionHelper.findField(ReflectionHelper.java:94) at cpw.mods.fml.relauncher.ReflectionHelper.setPrivateValue(ReflectionHelper.java:143) ... 16 more Caused by: java.lang.NoSuchFieldException: locationSkin at java.lang.Class.getDeclaredField(Class.java:2062) at cpw.mods.fml.relauncher.ReflectionHelper.findField(ReflectionHelper.java:85) ... 17 more This is the same error as before: you're only using the deobfucated (MCP) name, not the obfuscated (SRG) name. You need to pass both names to ObfuscationReflectionHelper.getPrivateValue (and other methods in the same class that take a String array/vararg of field names). You can find the SRG name of a method/field in the MCP mapping CSVs, which can be found in your Gradle cache. For recent 1.7.10 versions of Forge, the CSVs are in ~/.gradle/caches/minecraft/net/minecraftforge/forge/<forge_version>/unpacked/conf (replace ~ with %USERPROFILE% on Windows). You can also download the mappings from the MCPBot Website. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Dijkstra Posted September 23, 2015 Author Posted September 23, 2015 public static void load(){ AbstractClientPlayer player = Minecraft.getMinecraft().thePlayer; ResourceLocation skin = new ResourceLocation("textures/players/"+player.getDisplayName()+".png"); ObfuscationReflectionHelper.setPrivateValue(AbstractClientPlayer.class, player, skin, new String[]{"locationSkin","field_178865_e"}); } this is code i am using, clearly i am doing something but can not work it out i have passed in both the dev name and real, still works fine in dev but not out of dev Quote
Choonster Posted September 23, 2015 Posted September 23, 2015 Ah, I gave you the SRG name of 1.8's NetworkPlayerInfo#locationSkin field instead of 1.7.10's AbstractClientPlayer#locationSkin field. The SRG name you want is actually field_110312_d . In future you should include the Minecraft version in the title so people know which version you're working with. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Dijkstra Posted September 25, 2015 Author Posted September 25, 2015 It worked, out dev!!!! for a second then my skin i set on minecraft.net came back does this mean that i need to run my code each tick or do i need to get to be set a bit latter? Quote
Choonster Posted September 25, 2015 Posted September 25, 2015 Skins are downloaded and applied to players asynchronously, so you can't immediately replace the player's skin with your custom skin. When an AbstractClientPlayer is instantiated, it calls SkinManager#func_152790_a . This downloads the the name of the player's skin and cape textures from Mojang's session servers and calls SkinManager#func_152789_a for each one. This downloads the texture in a separate thread (if it hasn't already been downloaded) and then calls SkinManager.SkinAvailableCallback#func_152121_a (implemented by AbstractClientPlayer ) on the next tick to store the ResourceLocation of the texture in the appropriate field of AbstractClientPlayer . I'd recommend subscribing to EntityJoinWorldEvent (only on the client side) and checking if the entity is an AbstractClientPlayer . If it is, call SkinManager#func_152790_a with your own implementation of SkinManager.SkinAvailableCallback#func_152121_a that that the Type is Type.SKIN and then sets the player's locationSkin field. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Dijkstra Posted October 21, 2015 Author Posted October 21, 2015 Sorry i have not got back sooner, but I have be busy with more important stuff. I used to get the skin manager instance from the Minecraft class SkinManager sm = Minecraft.getMinecraft().func_152342_ad(); so that i can call SkinCallback sc = new SkinCallback(); // my class that implements SkinAvailableCallback sm.func_152790_a(GameProfile, sc, false); but i am sure where to get the game profile class or the what to set boolean to Quote
Choonster Posted October 21, 2015 Posted October 21, 2015 Use EntityPlayer#getGameProfile to get a player's GameProfile . The boolean is whether the downloaded texture names need to be securely signed. AbstractClientPlayer passes true , you probably should too. Quote Please don't PM me to ask for help. Asking your question in a public thread preserves it for people who are having the same problem in the future.
Dijkstra Posted October 21, 2015 Author Posted October 21, 2015 thanks i had literally just found the getter for the game profile by following the method in Abstract Client Player class back to entity player class, and thanks for clarification of the boolean Quote
Dijkstra Posted October 26, 2015 Author Posted October 26, 2015 I have subscribed to both PlayerLoggedInEvent and the EntityJoinWorldEvent, but i have a problem the EntityJoinWorldEvent is not trigger by a player and the PlayerLoggedInEvent gives me an EntityPlayer and not an abstract client player and i can not cast the EntityPlayer to an abstract client player Quote
EverythingGames Posted October 28, 2015 Posted October 28, 2015 I have subscribed to both PlayerLoggedInEvent and the EntityJoinWorldEvent, but i have a problem the EntityJoinWorldEvent is not trigger by a player and the PlayerLoggedInEvent gives me an EntityPlayer and not an abstract client player and i can not cast the EntityPlayer to an abstract client player Wrong. EntityJoinWorldEvent is triggered by all entities joining the world, hence the name. Subscribe to the EntityJoinWorldEvent, check the instance of the AbstractClientPlayer against the event.entity, and then cast and do whatever you need. Remember, changing the skin with reflection during the EntityJoinWorldEvent will result in a NullPointerException (if you are changing NetworkPlayerInfo.locationSkin), so you will need to create a delayed runnable and schedule it to run on the main thread. Be sure to use ObfuscationReflectionHelper with SRG names. Quote Development of Plugins [2012 - 2014] Development of Mods [2012 - Current]
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.