shrexish Posted April 21, 2020 Posted April 21, 2020 This is my first ever mod. Im trying to do something fairly simple. I created a custom saddle for horses. The idea is that when you place the saddle in the horse's inventory, you can ride the horse with your friend. The saddle is called "Saddle For Two". I need to make the horse accept my saddle as normal saddle that would be able to be placed in its inventory. Later on I will begin to work on the functionality where two players can ride one horse. For now i just need to be able to place my custom saddle in the horse's inventory. Is it even possible to mess around with that? Quote
imacatlolol Posted April 21, 2020 Posted April 21, 2020 Hmm, I've never tried this, but it seems doable. I think you'd have to listen for the horse's container to open on the server with PlayerContainerEvent.Open and on the client with GuiOpenEvent, then replace the saddle slot with your custom slot that also accepts your new saddle. Quote I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.
shrexish Posted April 21, 2020 Author Posted April 21, 2020 Where is all of this server and client thing? Im guessing you dont mean something like writing this code somewhere, right? Quote
imacatlolol Posted April 21, 2020 Posted April 21, 2020 The game runs a server and a client, the server does most of the logical work while the client mainly handles input and rendering. You can read up on the server/client a bit here. The events I mentioned (PlayerContainerEvent.Open and GuiOpenEvent) are things you can subscribe to and run some code when the events occur. Read this to see how events work. Both of those events offer a way of accessing an instance of HorseInventoryContainer, which is what controls what items are allowed into a horse's inventory. By changing the saddle slot in this container, you will be able to allow your saddle to be placed in it. Quote I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.
shrexish Posted April 21, 2020 Author Posted April 21, 2020 Hey! After trying to understand what this PlayerContainerEvent.Open does, I did this: @SubscribeEvent public void containerOpen(PlayerContainerEvent.Open event) { System.out.println("Opened...!"); } To see if it would print on opening either my inventory or the horse's inventory. Sadly, nothing printed. Im very confused... Quote
imacatlolol Posted April 21, 2020 Posted April 21, 2020 Are you using @Mod.EventBusSubscriber? If so, your event methods need to be static. Also, instead of printing directly to the console, it's generally better to print with a logger instance via LogManager.getLogger(). Using your IDE's debugger is also great for testing whether or not code is being run. Quote I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.
shrexish Posted April 22, 2020 Author Posted April 22, 2020 I'm just using @SubscribeEvent. I will switch to Logger. Quote
shrexish Posted April 22, 2020 Author Posted April 22, 2020 Switching to Logger didn't give out any info. Any other recommendation? Quote
imacatlolol Posted April 22, 2020 Posted April 22, 2020 Just using @SubscribeEvent isn't enough, you need to tell it to search the class or object for that annotation. There are multiple ways of doing this, but the simplest way is to annotate the class with @Mod.EventBusSubscriber and make all methods that use @SubscribeEvent static. You can alternatively call MinecraftForge.EVENT_BUS.register() and pass in the object or class you want it to scan. Quote I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.
shrexish Posted April 22, 2020 Author Posted April 22, 2020 Im trying the first way, hope it works! Quote
shrexish Posted April 22, 2020 Author Posted April 22, 2020 (edited) Still nothing printing out. Edited April 22, 2020 by shrexish Quote
imacatlolol Posted April 22, 2020 Posted April 22, 2020 Show your full class code Quote I'm eager to learn and am prone to mistakes. Don't hesitate to tell me how I can improve.
shrexish Posted April 22, 2020 Author Posted April 22, 2020 Ok: package com.shrexish.horsemod; import org.apache.logging.log4j.LogManager; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber public class EventHandler { @SubscribeEvent public static void containerOpen(PlayerContainerEvent.Open event) { LogManager.getLogger(); } } This is my event handler. This is my main class: package com.shrexish.horsemod; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @Mod(Reference.MOD_ID) public class HorseMod { public HorseMod() { MinecraftForge.EVENT_BUS.register(this); FMLJavaModLoadingContext.get().getModEventBus().register(this); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); } private void onClientSetup(FMLClientSetupEvent event) { //PROXY.setupClient(); MinecraftForge.EVENT_BUS.register(new EventHandler()); } } This the ModItems class: package com.shrexish.horsemod.init; import com.shrexish.horsemod.Reference; import com.shrexish.horsemod.item.ItemSaddleForTwo; import net.minecraft.item.Item; import net.minecraft.item.ItemGroup; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.ObjectHolder; @ObjectHolder(Reference.MOD_ID) @Mod.EventBusSubscriber(modid = Reference.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD) public class ModItems { public static final Item SADDLE_FOR_TWO = null; @SubscribeEvent @SuppressWarnings("unused") public static void register(final RegistryEvent.Register<Item> event) { event.getRegistry().register(new ItemSaddleForTwo(new Item.Properties().maxStackSize(1).group(ItemGroup.TRANSPORTATION))); } } This is the custom saddle class: package com.shrexish.horsemod.item; import com.shrexish.horsemod.Reference; import net.minecraft.item.Item; import net.minecraft.util.ResourceLocation; public class ItemSaddleForTwo extends Item { public ItemSaddleForTwo(Properties properties) { super(properties); this.setRegistryName(new ResourceLocation(Reference.MOD_ID, "saddle_for_two")); } } This was practically everything. Quote
shrexish Posted April 27, 2020 Author Posted April 27, 2020 Still can't find a solution to this. Do you have any more recommendations? Quote
Boy132 Posted April 27, 2020 Posted April 27, 2020 (edited) On 4/22/2020 at 1:26 PM, shrexish said: [...] package com.shrexish.horsemod; import org.apache.logging.log4j.LogManager; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber public class EventHandler { @SubscribeEvent public static void containerOpen(PlayerContainerEvent.Open event) { LogManager.getLogger(); } } [...] Expand You need to actually call the Logger#log function. (e.g. LogManager.getLogger().log(Level.INFO, "debug info");) On 4/22/2020 at 1:26 PM, shrexish said: [...] package com.shrexish.horsemod; import org.apache.logging.log4j.LogManager; import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod; @Mod.EventBusSubscriber public class EventHandler { @SubscribeEvent public static void containerOpen(PlayerContainerEvent.Open event) { LogManager.getLogger(); } } This is my event handler. This is my main class: package com.shrexish.horsemod; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; @Mod(Reference.MOD_ID) public class HorseMod { public HorseMod() { MinecraftForge.EVENT_BUS.register(this); FMLJavaModLoadingContext.get().getModEventBus().register(this); FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onClientSetup); } private void onClientSetup(FMLClientSetupEvent event) { //PROXY.setupClient(); MinecraftForge.EVENT_BUS.register(new EventHandler()); } } [...] Expand Also you should not use both methods that @imacatlolol mentioned above but rather choose one. If this works then you can access the container of the event (event.getContainer()) and can check if it's the HorseInventoryContainer (instanceof HorseInventoryContainer). And after that point I have no idea how to continue. Maybe adding a new Slot at the position of the old one? (maybe not the best idea ?) Edited April 27, 2020 by Boy132 Quote
ZDoctor Posted April 27, 2020 Posted April 27, 2020 (edited) So looking into it, as I understand it, you'd need to do the following: 1) You would need to subscribe to the open container event, check if the container is an instance of HorseInventoryContainer and override the openContainer of the player (by casting to ServerPlayerEntity) with your own custom HorseInventoryContainer then add the listener. 1.5) Since the parameters horse and inventoryIn are not passed along with the event, and are private with no accessors, you'd probably have to use reflection to get those values and pass them to your new instance or make a constructor that does that for you. Look at ObfuscationReflectionHelper. 2) Subscribe to the GuiOpenEvent and wait for the ClientPlayNetHandler to open the HorseInventoryScreen(through call handleOpenHorseWindow) and in the event you'd want to set the gui to the new instance of the HorseInventoryScreen with your custom horsecontainer. And that should do it. 2.5) like 1.5 you'd need some parameters not passed by the event, so you'd need reflection again to get them from the old container. Edited April 27, 2020 by ZDoctor Quote
ZDoctor Posted April 27, 2020 Posted April 27, 2020 (edited) And I went ahead and did it to prove I could. First the custom HorseInventoryContainer: public class TestHorseInventory extends HorseInventoryContainer { public TestHorseInventory(int id, PlayerInventory pInv, IInventory iinv, final AbstractHorseEntity horse) { super(id, pInv, iinv, horse); inventorySlots.set(0, new Slot(iinv, 0, 8, 18) { /** * Check if the stack is allowed to be placed in this slot, used for armor slots as well as furnace fuel. */ public boolean isItemValid(ItemStack stack) { return (stack.getItem() == Items.SADDLE || stack.getItem() == Items.DIAMOND) && !this.getHasStack() && horse.canBeSaddled(); } /** * Actualy only call when we want to render the white square effect over the slots. Return always True, except * for the armor slot of the Donkey/Mule (we can't interact with the Undead and Skeleton horses) */ @OnlyIn(Dist.CLIENT) public boolean isEnabled() { return horse.canBeSaddled(); } }); } } The overriding the server container: @Mod.EventBusSubscriber public static class CommonEvents { @SubscribeEvent public static void containerOpen(PlayerContainerEvent.Open event) { if (event.getContainer() instanceof HorseInventoryContainer) { LOGGER.info("Open Inventory"); ServerPlayerEntity player = (ServerPlayerEntity) event.getPlayer(); HorseInventoryContainer oldContainer = (HorseInventoryContainer) event.getContainer(); AbstractHorseEntity horse = ObfuscationReflectionHelper.getPrivateValue(HorseInventoryContainer.class, oldContainer, "horse"); IInventory horseInventory = ObfuscationReflectionHelper.getPrivateValue(HorseInventoryContainer.class, oldContainer, "horseInventory"); if (player != null && horse != null && horseInventory != null) { player.openContainer = new TestHorseInventory(player.currentWindowId, player.inventory, horseInventory, horse); player.openContainer.addListener(player); } } } } Then the HorseInventoryScreen: @Mod.EventBusSubscriber public static class TestClientEvents { @SubscribeEvent public static void openGui(GuiOpenEvent event) { if (event.getGui() instanceof HorseInventoryScreen) { ClientPlayerEntity player = Minecraft.getInstance().player; if (player != null) { HorseInventoryScreen oldGui = (HorseInventoryScreen) event.getGui(); HorseInventoryContainer oldContainer = oldGui.getContainer(); AbstractHorseEntity horse = ObfuscationReflectionHelper.getPrivateValue(HorseInventoryContainer.class, oldContainer, "horse"); IInventory horseInventory = ObfuscationReflectionHelper.getPrivateValue(HorseInventoryContainer.class, oldContainer, "horseInventory"); if (horse != null && horseInventory != null) { HorseInventoryContainer newContainer = new TestHorseInventory(oldContainer.windowId, player.inventory, horseInventory, horse); player.openContainer = newContainer; HorseInventoryScreen newGui = new HorseInventoryScreen(newContainer, player.inventory, horse); event.setGui(newGui); } } } } } The finished product: Edited April 27, 2020 by ZDoctor Spoiler broke code. Quote
shrexish Posted April 27, 2020 Author Posted April 27, 2020 Once I just do LogManager.getLogger().log(Level.INFO, "HorseInventory Opened!"); It prints that when I open my inventory when im on the horse. Off the horse and I open my inventory it doesnt print. So now i can know when the player opens the horse inventory. Now i just need to add my saddle to be able to be placed on horses. Hard part. Quote
shrexish Posted April 27, 2020 Author Posted April 27, 2020 Hey ZDoctor! Im reading your comment now. Thanks! Quote
ZDoctor Posted April 27, 2020 Posted April 27, 2020 Just realized, but I did it in version 1.15.2. Think that needed to be said. Quote
shrexish Posted April 27, 2020 Author Posted April 27, 2020 Thanks so much! I can now use my custom saddle on my horse. Now I will research how I can add to player functionality on the saddle. I think this doesn't have to do with the saddle, but with the horse itself not accepting two players. I'm really learning a lot right now, thanks! Quote
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.