Jump to content

[1.12] How to get stuff from server when doing the client GUI


Samaritans

Recommended Posts

Hey all, I have a container for a tile entity that would render different things depends on a player UUID appended to the item that is required to open the gui. In the container, it calls a method to find player by UUID and determine what the render based on the entityPlayer.

 

However, when the getClientGuiElement is called, findPlayer returns null since its called on the client side, causing my GUI to render nothing. Is there a way to remedy this?

 

Code here:

public ContainerTarotTable(UUID uuid) {
   this.player = Util.findPlayer(uuid);
   if (player != null) {
      // do stuff with player rng and other stuff in a custom registry.
   }
}

Here's how EntityPlayer is obtained from uuid:

public static EntityPlayer findPlayer(UUID uuid) {
   for (WorldServer ws : DimensionManager.getWorlds()) {
      EntityPlayer player = ws.getPlayerEntityByUUID(uuid);
      if (player != null) return player;
   }
   return null;
}

 

Edit: I know that it sounds like something to handle using Packets, however I'm confused by how to implement such a thing. i can send a packet to server and use server to get the entityPlayer, but how would i pass it back to the client when creating this new ContainerTarotTable.

Edited by Samaritans
Link to comment
Share on other sites

29 minutes ago, diesieben07 said:

You can send a packet and then check if Minecraft#player.openContainer is your container. If so, put whatever data you received into that container.

So that'd be two packets right? One from client to server sending UUID over. and another from from server to client sending the EntityPlayer and on receiving the packet at client I check something like (sorry not at pc rn, psudo code here:)

if(Minecraft.getMinecraft().player.openContainer instance of ContainerTarotTable) {
	
	Minecraft.getMinecraft.player.openContainer.player = message.player;

}

but then what would I do? The container is already constructed my entire guiHandler here:

public class GuiHandler implements IGuiHandler {

	@Override
	public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
		TileEntity tile = world.getTileEntity(new BlockPos(x, y, z));
		if (tile instanceof TileEntityWitchesOven) return new ContainerWitchesOven(player.inventory, (TileEntityWitchesOven) tile);
		if (tile instanceof TileEntityDistillery) return new ContainerDistillery(player.inventory, (TileEntityDistillery) tile);
		if (tile instanceof TileEntitySpinningWheel) return new ContainerSpinningWheel(player.inventory, (TileEntitySpinningWheel) tile);
		if (tile instanceof TileEntityTarotTable) {
			ItemStack stack = player.getHeldItemMainhand();
			if (stack.getItem() instanceof ItemTarotCards && stack.hasTagCompound() && stack.getTagCompound().hasKey("readId")) {
				return new ContainerTarotTable(UUID.fromString(stack.getTagCompound().getString("readId")));
			}
		}
		if (tile instanceof TileEntityJuniperChest) return new ContainerJuniperChest(player.inventory, (TileEntityJuniperChest) tile);
		return null;
	}
	
	@Override
	public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
		TileEntity tile = world.getTileEntity(new BlockPos(x, y, z));
		if (tile instanceof TileEntityWitchesOven) return new GuiWitchesOven((ContainerWitchesOven) getServerGuiElement(ModGui.OVEN.ordinal(), player, world, x, y, z), player.inventory);
		if (tile instanceof TileEntityDistillery) return new GuiDistillery((ContainerDistillery) getServerGuiElement(ModGui.DISTILLERY.ordinal(), player, world, x, y, z));
		if (tile instanceof TileEntitySpinningWheel) return new GuiSpinningWheel((ContainerSpinningWheel) getServerGuiElement(ModGui.SPINNING_WHEEL.ordinal(), player, world, x, y, z));
		if (tile instanceof TileEntityTarotTable) return new GuiTarotTable((ContainerTarotTable) getServerGuiElement(ModGui.TAROT_TABLE.ordinal(), player, world, x, y, z));
		if (tile instanceof TileEntityJuniperChest) return new GuiJuniperChest((ContainerJuniperChest) getServerGuiElement(ModGui.JUNIPER_CHEST.ordinal(), player, world, x, y, z), player.inventory);
		return null;
	}
	
	public enum ModGui {
		OVEN, DISTILLERY, SPINNING_WHEEL, TAROT_TABLE, JUNIPER_CHEST
	}
}

 

Or do i just simple make a new Constructor for the Container and pass in EntityPlayer. And on client reciveiving message just set player.openGui = new ContainerTarotTable(entityPlayer); ?

 

Edited by Samaritans
Link to comment
Share on other sites

I'm still really lost tbh, can you help me more here @diesieben07, would really appreciate it? Here's my messages: 

 

Server -> Client:

public class TarotPlayerMessage implements IMessage {
   private EntityPlayer player;

   public TarotPlayerMessage() { }

   public TarotPlayerMessage(EntityPlayer player) {
      this.player = player;
   }

   @Override
   public void fromBytes(ByteBuf byteBuf) {
      this.player.deserializeNBT(ByteBufUtils.readTag(byteBuf));
   }

   @Override
   public void toBytes(ByteBuf byteBuf) {
      ByteBufUtils.writeTag(byteBuf, player.serializeNBT());
   }

   public static class Handler implements IMessageHandler<TarotPlayerMessage, IMessage> {
      @Override
      public IMessage onMessage(TarotPlayerMessage message, MessageContext ctx) {
         if (ctx.side.isClient()) {
            Minecraft.getMinecraft().addScheduledTask(() -> Minecraft.getMinecraft().player.openContainer = new ContainerTarotTable(message.player));
         }
         return null;
      }
   }
}

 

Client -> Server:

public class TarotMessage implements IMessage {
   private String uuid;

   public TarotMessage() { }

   public TarotMessage(String uuid) {
      this.uuid = uuid;
   }

   @Override
   public void fromBytes(ByteBuf byteBuf) {
      uuid = ByteBufUtils.readUTF8String(byteBuf);
   }

   @Override
   public void toBytes(ByteBuf byteBuf) {
      ByteBufUtils.writeUTF8String(byteBuf, uuid);
   }

   public static class Handler implements IMessageHandler<TarotMessage, IMessage> {
      @Override
      public IMessage onMessage(TarotMessage message, MessageContext ctx) {
         if (ctx.side.isServer()) {
            EntityPlayer player = Util.findPlayer(message.uuid);
            if (player != null) return new TarotPlayerMessage(player);
         }
         return null;
      }
   }
}

 

Don't think it works, also its got a null string not allowed error. 

Link to comment
Share on other sites

36 minutes ago, Samaritans said:

also its got a null string not allowed error. 

Then you are passing it a null string via the constructor.

 

But the real problem here is that you dont know what you are doing. What information are you trying to get on the client from the server?

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

5 minutes ago, Animefan8888 said:

Then you are passing it a null string via the constructor.

 

But the real problem here is that you dont know what you are doing. What information are you trying to get on the client from the server?

@Animefan8888 You're right, I'm not sure what I'm doing. I'm trying to get the EntityPlayer from Server so when the client calls opengui, it would make a new container using the EntityPlayer that was passed in.

Link to comment
Share on other sites

9 minutes ago, Samaritans said:

I'm trying to get the EntityPlayer from Server so when the client calls opengui, it would make a new container using the EntityPlayer that was passed in.

Is the player from the server the one that opened the container/gui?

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

51 minutes ago, Animefan8888 said:

Is the player from the server the one that opened the container/gui?

@Animefan8888 No, its an entityplayer obtained by using findPlayerbyUUID function and the UUID comes from an item in the player's hand when opening the GUI. Thus if i don't do packets, the findPlayerbyUUID returns null on client side and i get an empty GUI.

Link to comment
Share on other sites

12 minutes ago, Samaritans said:

Thus if i don't do packets, the findPlayerbyUUID returns null on client side and i get an empty GUI.

What do you need about the player? I assure that you cannot always obtain the EntityPlayer of that player on the client. However you can have specific values. 

 

Also you only need one packet server to client.

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

@Animefan8888 well here's thefull constructor for the container:

public ContainerTarotTable(EntityPlayer player) {
   this.player = player;
   if (player != null) {
      List<Tarot> valid = GameRegistry.findRegistry(Tarot.class).getValuesCollection().stream().filter(f -> f.isCounted(this.player)).collect(Collectors.toList());
      if (!valid.isEmpty()) {
         while (!valid.isEmpty() && toRead.size() < 4) {
            int i = player.getRNG().nextInt(valid.size());
            toRead.add(valid.get(i));
            valid.remove(i);
         }
      }
   }
}

 

as you can see, i need the playerRNG and also using the player to filter stuff from my registry.

Link to comment
Share on other sites

37 minutes ago, Samaritans said:

 as you can see, i need the playerRNG and also using the player to filter stuff from my registry.

Well you dont need the rng on the client...you need the results of the rng. So you need two constructors one that takes a player(server side) and an empty one to use for when you create the gui. Then a packet needs to get sent with the rng information you've got on the server to the client.

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

1 hour ago, Animefan8888 said:

Well you dont need the rng on the client...you need the results of the rng. So you need two constructors one that takes a player(server side) and an empty one to use for when you create the gui. Then a packet needs to get sent with the rng information you've got on the server to the client.

@Animefan8888 Honestly still kinda confused but lemme try to word this:

1. Keep the constructor that uses entityplayer like i posted above.

2. Another Container constructor? (still not sure for what?)

3. Send packet from server to client with the rng info, but 

    #1. what do i do with my registry filter part that also needs my player?

    #2. sure i can process the stuff on the server, but how when would i even send that packet and where do i let the server do the work? so confused.

    #3. Where would i send that packet, like what would be in the handler?

 

I really tried reading the forge docs and still don't quite understand how to utilize the packets to open gui. All i can see by debug mode and breaks are:

1. When i right click to open the gui, the server calls getServerGuiElement and successfully gets the gui.

2. but when the client calls getClientGuiElement and i return a new GuiTarotTable(new ContainerTarotTable(...)) it renders nothing because findPlayer returns null.

 

I thought I need to send a packet at getClientGuiElement from client to server with the UUID and get entityPlayer inside the Message Handler. And then, another packet need to be sent back with the entityPlayer or at least the RNG result and the filtered List<Tarot>, but what would i do with the message is what I'm mostly confused with.

 

Sorry for the word wall, I've had this bug for days and kinda frustrating that I have no clue how to fix it :(

 

really appreciate your help! 

 

Edited by Samaritans
Link to comment
Share on other sites

13 minutes ago, Samaritans said:

Another Container constructor? (still not sure for what?)

For the client.

 

16 minutes ago, Samaritans said:

1. what do i do with my registry filter part that also needs my player?

The client shouldn't care.

 

16 minutes ago, Samaritans said:

when would i even send that packet

Once you get it.

 

16 minutes ago, Samaritans said:

and where do i let the server do the work?

I'm not sure what you mean by this. When do you get the information? You can do it in the constructor that takes a player.

18 minutes ago, Samaritans said:

Where would i send that packet

To the client.

 

19 minutes ago, Samaritans said:

like what would be in the handler?

You'd schedule it via Minecraft#scheduleTask and then you would get the openContainer field from the client player Minecraft#player. If the openContainer is an instanceof your Container. Then take the data out of the message and put it in the openContainer.

 

25 minutes ago, Samaritans said:

I thought I need to send a packet at getClientGuiElement from client to server with the UUID and get entityPlayer inside the Message Handler. And then, another packet need to be sent back with the entityPlayer or at least the RNG result and the filtered List<Tarot>, but what would i do with the message is what I'm mostly confused with.

This would just be a waste of packets. The server knows that a gui is opening and the uuid of the player in question and therefore is fully capable of sending the packet it needs.

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

@Animefan8888

 

Why shouldn't the client care, the List<Tarot> toRead is the list of stuff that's gonna be rendered on the Gui. Or do you mean I shall find entityPlayer and also the List<Tarot> in handler and then just send the toRead List back to the client? that kinda make sense.

 

But where in code would I call my network.sentTo(new ThisMessage(...)) to send this packet though? The constructor doesn't make sense because it's also called on the client side, do i send the packet to client when the getServerGuiElement in my GuiHandler?

 

Link to comment
Share on other sites

1 minute ago, Samaritans said:

The constructor doesn't make sense because it's also called on the client side

Thus the two constructors, though you could send it in getServerGuiElement.

 

1 minute ago, Samaritans said:

Or do you mean I shall find entityPlayer and also the List<Tarot> in handler and then just send the toRead List back to the client? that kinda make sense.

You can find them there or in the Container's constructor that takes a player. Then send the packet.

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

@Animefan8888

I think it's really important that I can somehow pass the EntityPlayer in my packet because its not only used in the container to get the List<Tarot> but also when rendering GUI to determine how to render my tarots. Here's guiRendering stuff:

Spoiler

@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
   mc.getTextureManager().bindTexture(TEX);
   int x = (width - xSize) / 2;
   int y = (height - ySize) / 2;
   drawTexturedModalRect(x, y, 0, 0, xSize, ySize);
   for (int i = 0; i < container.toRead.size(); i++) {
      GlStateManager.pushMatrix();
      GlStateManager.color(1, 1, 1);
      Tarot tarot = container.toRead.get(i);
      mc.getTextureManager().bindTexture(tarot.texture);
      int cx = x + (i % 2 == 0 ? 48 : 126);
      int cy = y + (i / 2 == 0 ? 44 : 122);
      GlStateManager.pushMatrix();
      if (tarot.isReversed(container.player)) {
         GlStateManager.rotate(180, 0, 0, 1);
         drawCard(-cx, -cy, 48, 64);
      }
      else drawCard(cx, cy, 48, 64);
      GlStateManager.popMatrix();
      mc.getTextureManager().bindTexture(tarot.getNumber(container.player) < 0 ? TEX_FRAME : TEX_FRAME_NUMBER);
      drawCard(cx, cy, 50, 66);
      GlStateManager.popMatrix();
      if (tarot.getNumber(container.player) > -1) {
         int num = tarot.getNumber(container.player);
         String number = num > 99 ? "99+" : "" + num;
         drawCenteredString(mc.fontRenderer, number, cx, cy + 24, 0x7f7f7f);
      }
   }
}

As you can see the player is still used to determine what number to render the cards and if they should be rendered in reverse. (the tarot.getNumber or isReversed function checks a capability attached to the player by the mod). 

 

When i tried to send the packet with an Entityplayer by Serializing and then Deserializing, it gives me null string exception.

 

Then I just tried to send the List<tarot> and forget about the other rendering stuff for now, but this gives me a NullPointer Error Executing Task ``at net.minecraft.network.play.server.SPacketEntityHeadLook.getEntity(SPacketEntityHeadLook.java:56) ~[SPacketEntityHeadLook.class:?]``

 

Here's what i got in the packet:

Spoiler

public class TarotMessage implements IMessage {
   private List<Tarot> toRead = new ArrayList<>();

   public TarotMessage() { }

   public TarotMessage(String uuid) {
      EntityPlayer player = Util.findPlayer(uuid);
      List<Tarot> valid = GameRegistry.findRegistry(Tarot.class).getValuesCollection().stream().filter(f -> f.isCounted(player)).collect(Collectors.toList());
      if (!valid.isEmpty()) {
         while (!valid.isEmpty() && toRead.size() < 4) {
            int i = player.getRNG().nextInt(valid.size());
            toRead.add(valid.get(i));
            valid.remove(i);
         }
      }
   }

   @Override
   public void fromBytes(ByteBuf byteBuf) {
      this.toRead = ByteBufUtils.readRegistryEntries(byteBuf, GameRegistry.findRegistry(Tarot.class));
   }

   @Override
   public void toBytes(ByteBuf byteBuf) {
      ByteBufUtils.writeRegistryEntries(byteBuf, toRead);
   }

   public static class Handler implements IMessageHandler<TarotMessage, IMessage> {
      @Override
      public IMessage onMessage(TarotMessage message, MessageContext ctx) {
         if (ctx.side.isClient()) {
            Minecraft.getMinecraft().addScheduledTask(() -> {
               if(Minecraft.getMinecraft().player.openContainer instanceof ContainerTarotTable) {
                  ((ContainerTarotTable) Minecraft.getMinecraft().player.openContainer).toRead = message.toRead;
               }
            });
         }
         return null;
      }
   }
}

 The packet is sent in the GuiHandler under getServerGuiElement like this:

if(!world.isRemote) Bewitchment.network.sendTo(new TarotMessage(stack.getTagCompound().getString("readId")), (EntityPlayerMP) player);

 

Have I just done something fundamentally wrong and not achievable through packet and modify how my entire tarot system and GUI works?

 

Link to comment
Share on other sites

5 hours ago, Samaritans said:

Have I just done something fundamentally wrong and not achievable through packet and modify how my entire tarot system and GUI works?

No, because you can send the Capability data you need to render in the packet. As diesieben and I have both said it is impossible to always have the player on the client side. Therefore you can only send the data.

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

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.

Announcements



×
×
  • Create New...

Important Information

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