Jump to content

[1.15.2] Problem displaying capability information on player's inventory GUI


PenWallet

Recommended Posts

Hello, I'm quite new to modding, really sorry if making beginners' mistakes.

 

So, I have a capability for each PlayerEntity, which is just an integer acting as a currency. To do this, I copied the InventoryScreen code to change the GUI's texture to my own (which adds in a place for the currency) and to show a text with said amount of currency. The problem comes getting the information from the user's capability. Since it's server-only information, I need to make packet networking, so I did, but I have no idea how to rely that information to my CustomInventoryScreen. Since I think I can't show Mojang's proprietary code, I'll only show what I changed from InventoryScreen:

 

CustomInventoryScreen

@OnlyIn(Dist.CLIENT)
public class CustomInventoryScreen extends DisplayEffectsScreen<PlayerContainer> implements IRecipeShownListener {
    private static final ResourceLocation CUSTOM_INVENTORY_TEXTURE = new ResourceLocation(Constants.MODID, "textures/gui/container/custominventory.png");
  
  	/**
     * Draws the background layer of this container (behind the items).
     */
    protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
        	//Call to get the server to send the user's currency information
        	MyMod.CHANNEL.sendToServer(new C2SCurrencyPacket());
  		//This changes the background to mine (with the place for the currency added to the texture)
        	this.minecraft.getTextureManager().bindTexture(CUSTOM_INVENTORY_TEXTURE);
 	 	//This draws the currency at the right place. THIS is where I would need that information, but I don't know how to get it
 		this.font.drawString("10002g", this.guiLeft + 110, this.height / 2 - 13, 0xffae00);
	}
}

 

C2SCurrencyPacket

public class C2SCurrencyPacket {

    public C2SCurrencyPacket() {

    }

    public static void handle(final C2SCurrencyPacket msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            ServerPlayerEntity sender = ctx.get().getSender();
            ICurrency currency = sender.getCapability(CurrencyCapability.CURRENCY_CAPABILITY).orElseThrow(IllegalStateException::new);
          
            //I send the currency amount information to the client with another packet
            MyMod.CHANNEL.send(PacketDistributor.ALL.noArg(), new S2CCurrencyPacket(currency.getAmount()));
          
            //Just testing if the currency is accurate by showing a message to the user, and it is
            sender.sendMessage(new StringTextComponent("Money: "+currency.getAmount()), ChatType.SYSTEM);
        });
        ctx.get().setPacketHandled(true);
    }

    public static void encode(final C2SCurrencyPacket msg, final PacketBuffer packetBuffer) {

    }

    public static C2SCurrencyPacket decode(final PacketBuffer packetBuffer) {
        return new C2SCurrencyPacket();
    }
}

 

S2CCurrencyPacket

public class S2CCurrencyPacket {
    private int amount;

    public S2CCurrencyPacket(int amount) {
        this.amount = amount;
    }

    public static void handle(final S2CCurrencyPacket msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            // Here I would need to give the currency information to the menu, but I don't know how
        });
        ctx.get().setPacketHandled(true);
    }

    public static void encode(final S2CCurrencyPacket msg, final PacketBuffer packetBuffer) {
        packetBuffer.writeInt(msg.amount);
    }

    public static S2CCurrencyPacket decode(final PacketBuffer packetBuffer) {
        return new S2CCurrencyPacket(packetBuffer.readInt());
    }
}

 

Here's how I attach my CustomInventoryScreen to the game:

@Mod(Constants.MODID)
public class MyMod {
    @SubscribeEvent
    public void changeInventoryGUI(GuiOpenEvent event)
    {
        if(event.getGui() instanceof InventoryScreen)
            event.setGui(new CustomInventoryScreen(Minecraft.getInstance().player));
    }
}

 

So, to sum it up, my problem is that I don't know how to access my CustomInventoryScreen instance to show the information or if that's the way I should do it. I don't know if what I have done is correct or if I'm killing bunnies with every line of code I type. Please help me out and thank you for your time

 

EDIT:

I managed to solve my problem by changing the CustomInventoryScreen to this

@OnlyIn(Dist.CLIENT)
public class CustomInventoryScreen extends DisplayEffectsScreen<PlayerContainer> implements IRecipeShownListener {
    private static final ResourceLocation CUSTOM_INVENTORY_TEXTURE = new ResourceLocation(Constants.MODID, "textures/gui/container/custominventory.png");
  	private int money = 0;
  
  	public CustomInventoryScreen(PlayerEntity player) {
		//Call to get the server to send the user's currency information
		StardewMod.CHANNEL.sendToServer(new C2SCurrencyPacket());
	}
  
	//Sets the variable to the right amount of money
	public void setMoney(int amount)
	{
		this.money = amount;
	}
  
	/**
	* Draws the background layer of this container (behind the items).
	*/
	protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
		//Call to get the server to send the user's currency information
		MyMod.CHANNEL.sendToServer(new C2SCurrencyPacket());
  		//This changes the background to mine (with the place for the currency added to the texture)
		this.minecraft.getTextureManager().bindTexture(CUSTOM_INVENTORY_TEXTURE);
 	 	//This draws the currency at the right place. THIS is where I would need that information, but I don't know how to get it
 		this.font.drawString(money+"g", this.guiLeft + 110, this.height / 2 - 13, 0xffae00);
	}
}

 

And the S2CCurrencyPacket to this:

public class S2CCurrencyPacket {
    private int amount;

    public S2CCurrencyPacket(int amount) {
        this.amount = amount;
    }

    public static void handle(final S2CCurrencyPacket msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            ServerPlayerEntity sender = ctx.get().getSender(); // the client that sent this packet
            //Set the amount in the screen
            ((CustomInventoryScreen)Minecraft.getInstance().currentScreen).setMoney(msg.amount);
        });
        ctx.get().setPacketHandled(true);
    }

    public static void encode(final S2CCurrencyPacket msg, final PacketBuffer packetBuffer) {
        packetBuffer.writeInt(msg.amount);
    }

    public static S2CCurrencyPacket decode(final PacketBuffer packetBuffer) {
        return new S2CCurrencyPacket(packetBuffer.readInt());
    }
}

 

This does indeed solve my problem, but I don't know why it smells like stinky code. Am I on the right here or is this just a shitty way to do it?

Edited by PenWallet
Link to comment
Share on other sites

6 hours ago, PenWallet said:

This does indeed solve my problem, but I don't know why it smells like stinky code. Am I on the right here or is this just a shitty way to do it?

You send a pack to the server every frame the Screen is called...

 

Also when handling the packet you reach across logical sides by using Minecraft.getInstance() You cannot do this Minecraft.class is client only.

 

Instead whenever the currency is changed send it to the player aka the client. Then in your Screen you can just do Minecraft.getInstance().player.getCapability(...) to get your data from the capability. Your handle method for your packet should store the currency in the capability.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

3 hours ago, Animefan8888 said:

You send a pack to the server every frame the Screen is called...

 

Also when handling the packet you reach across logical sides by using Minecraft.getInstance() You cannot do this Minecraft.class is client only.

 

Instead whenever the currency is changed send it to the player aka the client. Then in your Screen you can just do Minecraft.getInstance().player.getCapability(...) to get your data from the capability. Your handle method for your packet should store the currency in the capability.

And how would I go about giving the player the information when he first joins? Right now, when he first joins, it shows 0 on the player's inventory GUI because Minecraft.getInstance().player doesn't have the information yet. When I update the information, I did what you told me and send the packet, and then it does show correctly on the GUI, but I don't know exactly where I would first update that information.

 

Also, aside from that I understand I can only ask for the information (that is, sending the C2SCurrencyPacket) from the client, so I should first check if world.isRemote, right?

Link to comment
Share on other sites

4 hours ago, diesieben07 said:

Okay. First things first: You should not make an entire new inventory screen, it will prevent any other mods from doing modifications to it. There are a plethora of events being fired for screens (see the subclasses of GuiScreenEvent), which you can and should use to modify screens.

 

Now as for your packet: To have the data always up to date on the client you need to send it from PlayerLoggedInEventPlayerRespawnEventPlayerChangedDimensionEvent and  whenever the data changes.

So, I want to do 3 things in my custom inventory:

- Changing the background to this one, which adds a space for the currency

custominventory.png.d9e362f694d8774d3c2327c95f13e938.png

- Move the book icon to the top right corner, above the crafting result slot, so that it doesn't interfere with the currency slot

- Adding a text to said currency slot.

 

From that stuff, I have achieved:

- Tried to change background with code:

private static final ResourceLocation CUSTOM_INVENTORY = new ResourceLocation(Constants.MODID, "textures/gui/container/custominventory.png");

@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public void guiBackground(GuiScreenEvent.BackgroundDrawnEvent event)
{
	if(!(event.getGui() instanceof InventoryScreen)) return;
	
  	InventoryScreen screen = (InventoryScreen)event.getGui();
	screen.getMinecraft().getTextureManager().bindTexture(CUSTOM_INVENTORY);
}

It didn't work, so I replaced minecraft's texture with mine in my mod's assets, and it does work, but I'm guessing that's not the way to do it.

 

- I managed to move the crafting recipe book like this:

@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public void guiScreenEvent(GuiScreenEvent.DrawScreenEvent.Post event)
{
	if(!(event.getGui() instanceof InventoryScreen)) return;

	InventoryScreen screen = (InventoryScreen)event.getGui();
	int guiLeft = (screen.width - screen.getXSize()) / 2;
	for(Object object : screen.children())
	{
		if(object instanceof RecipeBookGui)
		{
			guiLeft = ((RecipeBookGui)object).updateScreenPosition(screen.width < 379, guiLeft + 290, screen.height / 2 - 79);
		}
		else if(object instanceof ImageButton)
		{
			((ImageButton)object).setPosition(guiLeft + 290, screen.height / 2 - 79);
		}
	}
}

And it does move it, but only if it's fullscreen, if it isn't fullscreen, it goes somewhere else, like this:

image.thumb.png.57bf3b61de0c09d697dfb5d7bae7a519.png

 

- The string over the currency slot I have no idea how to implement. When I used CustomInventoryScreen, I did it like this:

protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
	//This draws the currency at the right place. THIS is where I would need that information, but I don't know how to get it
	this.font.drawString(money+"g", this.guiLeft + 110, this.height / 2 - 13, 0xffae00);
}

But now that I'm using events like @diesieben07 said, I don't even know how to add it

 

A main problem, I think, is that most stuff from InventoryScreen and its super class are protected or private, so I have very little stuff to touch. I don't know, I feel quite lost right now, any help is highly appreaciated, thanks

Link to comment
Share on other sites

19 minutes ago, PenWallet said:

and its super class are protected or private, so I have very little stuff to touch.

Have you looked for getters?

 

Also in the case of Screen::font just use Minecraft.getInstance().fontRenderer or use Reflection in order to get it.

23 minutes ago, PenWallet said:

And it does move it, but only if it's fullscreen, if it isn't fullscreen, it goes somewhere else, like this:

Doing raw additions probably isn't the best idea. Things should be based on the Screen's width and height or the window's width and height.

 

26 minutes ago, PenWallet said:

It didn't work, so I replaced minecraft's texture with mine in my mod's assets, and it does work, but I'm guessing that's not the way to do it.

You are correct it is not. I would instead just draw the portion you changed onto the screen not the whole gui.

28 minutes ago, PenWallet said:

BackgroundDrawnEvent

This event is called after the tinting effect is called not when the actual screen is called.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

16 minutes ago, Animefan8888 said:

Have you looked for getters?

Well I'm stupid, not sure why I didn't look for a getGuiLeft method, and there it was. That solved my recipe book moving all over the place.

 

18 minutes ago, Animefan8888 said:

Also in the case of Screen::font just use Minecraft.getInstance().fontRenderer or use Reflection in order to get it.

Yup, that worked, I did this:

//Event to add the string to the screen
@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public void guiScreenEvent(GuiScreenEvent.DrawScreenEvent.Post event)
{
	if(!(event.getGui() instanceof InventoryScreen)) return;

	InventoryScreen screen = (InventoryScreen)event.getGui();
	screen.getMinecraft().fontRenderer.drawString(CurrencyCapability.getAmountFromPlayer(Minecraft.getInstance().player)+"g", screen.getGuiLeft() + 110, screen.height / 2 - 13, 0xffae00);

}

(The very same InventoryScreen does use raw additions so I'll guess that's doable?)

 

And for the thing you said, about drawing the portion, do you know the method which I would have to use or how I'd do it? Do I need a .png with just that part of the background?

Link to comment
Share on other sites

A bit of bump, I'm still stuck trying to draw this (currencyslot.png.3dc2412e98aecdb316590f74e8e0dad1.png) to every player's inventory. I did it by overriding minecraft's inventory texture, but as I was told, this is not a good idea, but I don't know what method to use or in which event of GuiScreenEvent to do so.

Link to comment
Share on other sites

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.