Posted June 13, 201411 yr I've been trying to replicate the function of TrazLander's CommandBlockSigns+ MCEdit filter within modded Minecraft. I came up with a system that would work, but I don't know if it is possible to execute within Forge as it is in the latest release. TL;DR : is there a way to hook into TileEntitySign's completion so that I can do things with the text of a sign when placed? (The easy way to do this would just be to make a new block as extension of BlockSign (i.e. CommandBlockSign) but I would like to keep convertibility to vanilla for exporting.) Longer version : The way this mod would work is any time a sign is placed and the player is finished writing to it, the mod would grab it, check if it is a valid sign for the system, and if so, log it into a .json file (the json part works wonderfully, though I spent an evening figuring out how to do it - I'm noobish with Java I/O). When a sign is destroyed the mod would check for a log for the sign and if there was one, remove it. (This is an easy hook.) When executing commands the mod would intercept the command, find tokens (as defined by the filter) and replace them as appropriate, then forward the command (and I believe I have found the token for this). The problem with this system, however, is that I have been unable to find a @SubscribeEvent that I could subscribe to to allow me to do this logging of signs. Any help is appreciated. http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr Hi The editing appears to be done in GuiEditSign and completion of the sign is in actionPerformed; I don't see any suitable hooks there. I have two suggestions then- 1) you could use ASM + Reflection to modify GuiEditSign.actionPerformed() to call your own code 2) You could write your own MyGuiEditSign extends GuiEditSign, override actionPerformed, and use the GuiOpenEvent to intercept the vanilla GuiEditSign and replace it with your MyGuiEditSign instead (replace event.gui with your own) Never tried those, but they should both work with a bit of tweaking. -TGG
June 13, 201411 yr Author At the very least this got me to update Forge I was on 10.12.1.1060 which was the Recommended when I installed Forge on this machine in my enviroment, but now the Recommended build has been updated to 10.12.2.1121, literally last night Aaaaaanyway: in 1060, the only GUI event was GuiOpenEvent. In 1069, however, "bspkrs: New GuiScreen events" was pushed. I'm updating now to have a look. (In other notes, in looking through "all" of the events to find one for this I had only looked in subpackages of net.minecraftforge.event and not net.minecraftforge.client.event. You learn something new every day!) http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr Author Well, I'm able to grab the GUI with the new GUI hooks! @SubscribeEvent public void on(ActionPerformedEvent.Pre event) { if (event.gui instanceof GuiEditSign) { GuiEditSign ges = (GuiEditSign) event.gui; } } The one problem is this line in GuiEditSign: /** Reference to the sign object. */ private TileEntitySign tileSign; Looks like I'll be overwriting the GuiEditSign after all. Oh well, at least I got a Forge update! http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr Hi I suggest to use the ASM Access transformer to turn the private to public http://www.minecraftforge.net/wiki/Using_Access_Transformers Relatively simple compared to modifying the class. -TGG
June 13, 201411 yr Author I've not messed with Java Reflection before (mainly because I haven't had to and it's such an advanced thing), so I'm probably missing something here, but this is the code that I wrote after doing a bit of research on the javadocs for reflection: @SubscribeEvent public void onSignDone(ActionPerformedEvent.Pre event) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException // yes I know this throws chain is messy { if (event.gui instanceof GuiEditSign) { GuiEditSign ges = (GuiEditSign) event.gui; TileEntitySign tileSign = (TileEntitySign) ges.getClass().getField("tileSign") .get(null); System.out.println(Arrays.asList(tileSign.signText)); } } However, upon hitting the "Done" on a sign, my Minecraft immediately crashes with this error: net.minecraft.util.ReportedException: Updating screen events at net.minecraft.client.Minecraft.runTick(Minecraft.java:1701) ~[Minecraft.class:?] at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:996) ~[Minecraft.class:?] at net.minecraft.client.Minecraft.run(Minecraft.java:912) [Minecraft.class:?] at net.minecraft.client.main.Main.main(Main.java:112) [Main.class:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.7.0_55] at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_55] at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.7.0_55] at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.7.0_55] at net.minecraft.launchwrapper.Launch.launch(Launch.java:134) [launchwrapper-1.9.jar:?] at net.minecraft.launchwrapper.Launch.main(Launch.java:28) [launchwrapper-1.9.jar:?] Caused by: java.lang.IllegalAccessException: Class cad97.commandblocksigns.CommandBlockSignsListener can not access a member of class net.minecraft.client.gui.inventory.GuiEditSign with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Unknown Source) ~[?:1.7.0_55] at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(Unknown Source) ~[?:1.7.0_55] at java.lang.reflect.AccessibleObject.checkAccess(Unknown Source) ~[?:1.7.0_55] at java.lang.reflect.Field.get(Unknown Source) ~[?:1.7.0_55] at cad97.commandblocksigns.CommandBlockSignsListener.onSignDone(CommandBlockSignsListener.java:20) ~[CommandBlockSignsListener.class:?] at cpw.mods.fml.common.eventhandler.ASMEventHandler_4_CommandBlockSignsListener_onSignDone_Pre.invoke(.dynamic) ~[?:?] at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:51) ~[ASMEventHandler.class:?] at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:122) ~[EventBus.class:?] at net.minecraft.client.gui.GuiScreen.mouseClicked(GuiScreen.java:250) ~[GuiScreen.class:?] at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:351) ~[GuiScreen.class:?] at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:315) ~[GuiScreen.class:?] at net.minecraft.client.Minecraft.runTick(Minecraft.java:1687) ~[Minecraft.class:?] ... 9 more (fun side note, the minecraft crash comment was "// Why is it breaking " - exactly what I'm thinking right now ) http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr Author Ok... this is really weird. With the same code I am getting a different error now ?!!!?? package cad97.commandblocksigns; import java.util.Arrays; import net.minecraft.client.gui.inventory.GuiEditSign; import net.minecraft.tileentity.TileEntitySign; import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; public class CommandBlockSignsListener { @SubscribeEvent public void onSignDone(ActionPerformedEvent.Pre event) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException // yes I know this throws chain is messy { if (event.gui instanceof GuiEditSign) { GuiEditSign ges = (GuiEditSign) event.gui; TileEntitySign tileSign = (TileEntitySign) ges.getClass().getField("tileSign") .get(null); System.out.println(Arrays.asList(tileSign.signText)); } } } java.lang.NoSuchFieldException: tileSign at java.lang.Class.getField(Unknown Source) at cad97.commandblocksigns.CommandBlockSignsListener.onSignDone(CommandBlockSignsListener.java:19) at cpw.mods.fml.common.eventhandler.ASMEventHandler_4_CommandBlockSignsListener_onSignDone_Pre.invoke(.dynamic) at cpw.mods.fml.common.eventhandler.ASMEventHandler.invoke(ASMEventHandler.java:51) at cpw.mods.fml.common.eventhandler.EventBus.post(EventBus.java:122) at net.minecraft.client.gui.GuiScreen.mouseClicked(GuiScreen.java:250) at net.minecraft.client.gui.GuiScreen.handleMouseInput(GuiScreen.java:351) at net.minecraft.client.gui.GuiScreen.handleInput(GuiScreen.java:315) at net.minecraft.client.Minecraft.runTick(Minecraft.java:1687) at net.minecraft.client.Minecraft.runGameLoop(Minecraft.java:996) at net.minecraft.client.Minecraft.run(Minecraft.java:912) at net.minecraft.client.main.Main.main(Main.java:112) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at net.minecraft.launchwrapper.Launch.launch(Launch.java:134) at net.minecraft.launchwrapper.Launch.main(Launch.java:28) I assume that what you are talking about for caching the getField would be something like this if it weren't giving me the NoSuchFieldException: package cad97.commandblocksigns; import java.lang.reflect.Field; import java.util.Arrays; import net.minecraft.client.gui.inventory.GuiEditSign; import net.minecraft.tileentity.TileEntitySign; import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; public class CommandBlockSignsListener { private Field f; @SubscribeEvent public void onSignDone(ActionPerformedEvent.Pre event) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException // yes I know this throws chain is undesirable I'll add a catch later { if (event.gui instanceof GuiEditSign) { if (f == null) { GuiEditSign ges = (GuiEditSign) event.gui; f = ges.getClass().getField("tileSign"); f.setAccessible(true); } TileEntitySign tileSign = (TileEntitySign) f.get(null); System.out.println(Arrays.asList(tileSign.signText)); } } } http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr Good job Now I haven't messed around with reflection much, but you came up with same solution I would have. And following my logic... It should work. We all stuff up sometimes... But I seem to be at the bottom of that pot.
June 13, 201411 yr Author Well I fixed it! Can you spot the difference? @SubscribeEvent public void onSignDone(ActionPerformedEvent.Pre event) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException // yes I know this throws chain is undesirable I'll add a catch later { if (event.gui instanceof GuiEditSign) { GuiEditSign ges = (GuiEditSign) event.gui; if (f == null) { f = ges.getClass().getDeclaredField("tileSign"); f.setAccessible(true); } TileEntitySign tileSign = (TileEntitySign) f.get(ges); System.out.println(Arrays.asList(tileSign.signText)); } } [spoiler=differences] It's two lines. Looking back at my code after a break, I realized that before I was using .getDeclaredField("tileSign") but switched somewhere to .getField("tileSign"). Looking at the documentation both should work, but something about the way they work only grabs it properly with .getDeclaredField("tileSign"). After I changed this, I got a NullPointer on the line with .get(null). [[GAH THAT THUNDER SCARED ME]] [[OK, breathe. in. out. in. out. Ok I'm fine.]] Reading the javadoc for this made me realize just why you can instantiate the field without a specific object reference and use the same one - you pass the specific reference to .get(). So, now the code works. I'm actually going to move the Field instantiate to a static constructor to simplify the actual hook. It'll work, though, so no need for me to put it here A great big THANK YOU (as in the button AND an internet hug (assuming you aren't a hug-hater or anything) #hugamodder (this is not a thing to my knowledge)) to the people who helped me. I couldn't have figured this out on my own. I love doing this and I love learning - THANK YOU for helping me http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
June 13, 201411 yr * claps enthusiastically * Thank you for teaching me a little bit o' reflection We all stuff up sometimes... But I seem to be at the bottom of that pot.
June 14, 201411 yr Author If you're interested, here is my final code. package cad97.commandblocksigns; import java.lang.reflect.Field; import net.minecraft.client.gui.inventory.GuiEditSign; import net.minecraft.tileentity.TileEntitySign; import net.minecraftforge.client.event.GuiScreenEvent.ActionPerformedEvent; import cpw.mods.fml.common.eventhandler.SubscribeEvent; public class CommandBlockSignsListener { static { try { f = GuiEditSign.class.getDeclaredField("tileSign"); } catch (NoSuchFieldException | SecurityException e) { e.printStackTrace(); } } private static Field f; @SubscribeEvent public void onSignDone(ActionPerformedEvent.Pre event) { if (event.gui instanceof GuiEditSign) { if (f != null) { try { f.setAccessible(true); GuiEditSign ges = (GuiEditSign) event.gui; TileEntitySign tileSign; tileSign = (TileEntitySign) f.get(ges); if (WaypointSign.isValidWaypointSign(tileSign)) { WaypointSign.register(tileSign.getWorldObj(), new WaypointSign(tileSign.signText)); } } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } } } } } I unfortunately can't do f.setAccessible(true); in the static constructor; Eclipse tells me "Cannot reference a field before it is defined." Oh well, re-setting one boolean every time is no real cost http://i1279.photobucket.com/albums/y523/textcraft/Jun%202014%20-%202/a77dd69ddfa9e622422c5e5cd7e377b14d5cdedec1b7a8e19dde68c9e22be6dfbf81219d3893f419da39a3ee5e6b4b0d3255bfef95601890afd8070929aa338b0dfc68d48355_zps0c847cf3.png[/img] I have a reputation for text walls. If you ask me a question I will most likely explain it in the most wordy way possible. -0 characters left
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.