#Sending NBT int value to Client HUD

148 messages · Page 1 of 1 (latest)

severe tapir
#

(1.21) Hey! I am a bit confused on how to network a nbt value "projectaot.energy" from the server side to the client. I created a payload class:

#
    public static final CustomPayload.Id<EnergyPayload> ID = new CustomPayload.Id<>(NetworkingConstants.ENERGY_PACKET_ID);
    public static final PacketCodec<RegistryByteBuf, EnergyPayload> CODEC = PacketCodec.tuple(PacketCodecs.INTEGER, EnergyPayload::value, EnergyPayload::new);


    @Override
    public Id<? extends CustomPayload> getId() {
        return null;
    }

    public static int getValue(EnergyPayload payload) {
        return payload.value;
    }
}``` and a EnergyHUD class: ```public class EnergyHUDOverlay implements HudRenderCallback {
    public static final Identifier FILLED_ENERGY = Identifier.of(ProjectAOT.MOD_ID, "textures/energy/filled_energy.png");
    public static final Identifier EMPTY_ENERGY = Identifier.of(ProjectAOT.MOD_ID, "textures/energy/empty_energy.png");
    @Override
    public void onHudRender(DrawContext drawContext, RenderTickCounter renderTickCounter) {
        int x = 100;
        int y = 100;
        MinecraftClient client = MinecraftClient.getInstance();
        if(client != null) {
            int width = client.getWindow().getScaledWidth();
            int height = client.getWindow().getScaledHeight();

            x = width / 2;
            y = height;

        }
        if(client.player != null) {
            IEntityDataSaver p = (IEntityDataSaver) client.player;
            if(p.getPersistentData() != null) {

                int energy = p.getPersistentData().getInt("projtectaot.energy");
                drawContext.drawText(client.textRenderer, energy + "/100", x+10, y, 0xFFFFFFFF, true);

                if(energy > 0) {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, FILLED_ENERGY, x, y, 0,0,16,16, 16,16);

                } else {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, EMPTY_ENERGY, x, y, 0,0,16,16, 16,16);

                }
            }```
#

How can I send this payload to the client? And how should I grab the information to send on the HUD?

#

The Idea is to display the nbt value onto the screen.

#

from my understand, the nbt data is strictly on the server side and I need the client to recieve the data.

alpine goblet
severe tapir
#

thank you! Yeah tbh idk what im doing so I didn't even notice im returning null lol

#

fixed it to @Override public Id<? extends CustomPayload> getId() { return ID; }

alpine goblet
#

Something like that. Doesn't need to be in a separate class or have the onPlayReady hook ofc

severe tapir
#

interesting. So for my example. Would I send the packet in my HUD class? And should I make the packet in the HUD class as well?

#

ex: int energy = p.getPersistentData().getInt("projtectaot.energy"); EnergyPayload payload = new EnergyPayload(energy);

#

probably not lol...?

alpine goblet
#

If that's on the server side, I don't see why not

#

It's a logical place to put it - send it when they open the hud

severe tapir
#

Don't believe it is

alpine goblet
#

Oh well then no xd

severe tapir
#

hahah

alpine goblet
#

Does the server know when the client wants it (as of now)?

#

If not, you'll need a way to tell the server to send it, which is likely another (empty) packet

severe tapir
#

I don't believe so. My code is very primitive, frankly because I just started. The classes that deal with my nbt value are the 2 the are above and ```public class PlayerTickHandler implements ServerTickEvents.StartTick{

@Override
public void onStartTick(MinecraftServer minecraftServer) {
   if(minecraftServer.getTicks() % 60 == 0) { //every 3 seconds
        for(ServerPlayerEntity player : minecraftServer.getPlayerManager().getPlayerList()) {
            IEntityDataSaver dataSaver = (IEntityDataSaver) player;
            EnergyData.addEnergy(dataSaver, 1);

        }
    }
}

}```

#

maybe I can use this class to create a packet?

#

this is the class that updates the nbt value every 3 seconds

alpine goblet
#

Sure, but do you really need to send it that often? If you do that's fine.

#

I don't know the context in which this "energy" is used

severe tapir
#

let me explain it a bit more

#

(this system is inspired by a stamina energy-like mechanic system) the nbt integer "projectaot.energy" is an integer that is stored on the player. I have a event that increases a player's "energy" every 3 seconds. The idea is to display the player's current "energy" value on their screen. As of now, my current issue is that these values are not being display on screen (I know my HUD works since I tested it with displaying a constant variable). A suggestion by a fellow discord user said they I am not sending this "energy" nbt value to the client, thus the value im displaying on screen doesn't exist.

#

Currently, i'm curious on whether it's because of the client (networking issue) or my HUD code in general (which im slightly confident in)

#

This energy will eventually be used by as a value cost to execute an ability later on... but for now just the HUD

alpine goblet
#

Almost certainly the networking. I think fabric API has code for syncing data attachments if you really wanted, but sending packets is just fine. I'd just put a // TODO estimate values on client and call it good.

severe tapir
#

if this doesn't answer your question feel free to ask me more about it! I appreciate your help!

alpine goblet
severe tapir
#

Excellent. So yeah, from my understanding there are a few ways to send the nbt value to the client. From what I understand the payload class I made is just fine (besides that minor "null" return as you pointed out).

#

As of now, I guess im a bit confused on how to utilize this payload.

#

@alpine goblet ill also take a further look at your github example

alpine goblet
#

You've got it created, now you need to register it on the right sides and create a receiver on the client side.

#

And yeah just yoink from my GitHub that's what I'd do lol

severe tapir
#

haha bet

#
    public static final Identifier ENERGY_PACKET_ID = Identifier.of("projectaot", "energy_id");


    private static CustomPayloadS2CPacket makeEnergyValuePacket(MinecraftServer server, int value) {
        return new CustomPayloadS2CPacket(new EnergyPayload(value));
    }

}``` I created a class like this
#

hopefully a good starting point?

alpine goblet
#

Yep!

severe tapir
#
        CustomPayloadS2CPacket packet = makeEnergyValuePacket(server,value);
        player.networkHandler.sendPacket(packet);
    }``` made this, thinking I use this function in my HUD class?
#

actually no

#

that seems stupid

#

lol

#

now im confused again haha

alpine goblet
#

Are you using split sources?

#

You'd have a main folder and a client folder set up for you if you are

severe tapir
#

yes I am

#

oh wait

#

no sorry

#

just a client folder.

alpine goblet
#

🤨

severe tapir
#

I assume this is what you're talking about:

#

unless im mistaken..

alpine goblet
#

Can you search your build.gradle for "split" as in split sources?

#

splitEnvironmentSourceSets

alpine goblet
#

Ok, not using split sources then. Good to know.

severe tapir
#

I may have something interesting.... let me know what you think about this

alpine goblet
severe tapir
#
    public static final Identifier FILLED_ENERGY = Identifier.of(ProjectAOT.MOD_ID, "textures/energy/filled_energy.png");
    public static final Identifier EMPTY_ENERGY = Identifier.of(ProjectAOT.MOD_ID, "textures/energy/empty_energy.png");
    @Override
    public void onHudRender(DrawContext drawContext, RenderTickCounter renderTickCounter) {
        int x = 100;
        int y = 100;
        MinecraftClient client = MinecraftClient.getInstance();
        if(client != null) {
            int width = client.getWindow().getScaledWidth();
            int height = client.getWindow().getScaledHeight();

            x = width / 2;
            y = height;

        }
        if(client.player != null) {
            IEntityDataSaver p = (IEntityDataSaver) client.player;
            if(p.getPersistentData() != null) {


                int energy = p.getPersistentData().getInt("projtectaot.energy");
                EnergyPayload payload = new EnergyPayload(energy);
                ProjectAOTNetworking.sendEnergyValuePacket(client.player.getServer(), client.player, energy);

                drawContext.drawText(client.textRenderer, energy + "/100", x+10, y, 0xFFFFFFFF, true);

                if(energy > 0) {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, FILLED_ENERGY, x, y, 0,0,16,16, 16,16);

                } else {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, EMPTY_ENERGY, x, y, 0,0,16,16, 16,16);

                }
            }
        }
    }
}``` This is the new HUD class, only difference is making the packet. But as you said I probably dont want to make it constantly.
#

sending network class hold up

#
    public static final Identifier ENERGY_PACKET_ID = Identifier.of("projectaot", "energy_id");


    private static CustomPayloadS2CPacket makeEnergyValuePacket(MinecraftServer server, int value) {
        return new CustomPayloadS2CPacket(new EnergyPayload(value));
    }

    public static void sendEnergyValuePacket(MinecraftServer server, ClientPlayerEntity player, int value) {
        CustomPayloadS2CPacket packet = makeEnergyValuePacket(server,value);
        player.networkHandler.sendPacket(packet);
    }

}``` make it clientplayerentity instead (I have a feeling this is completely wrong)
#

maybe instead I put this in my every 3 second tick class?

#

since it uses ServerPlayerEntity

alpine goblet
severe tapir
#

ahhhh

#

I see

alpine goblet
#

!!sides if that helps

severe tapir
#

Okay! So I may have a solution to that

#

let me code it rq lol

#

ill send it in a second

#

    public static int addEnergy(IEntityDataSaver player, int amount) {
        NbtCompound nbt = player.getPersistentData();
        if(nbt != null) {
            int energy = nbt.getInt("projectaot.energy");
            if (energy + amount >= 100) {
                energy = 100;
            } else {
                energy += amount;
            }
            nbt.putInt("projectaot.energy", energy);
            //sync data
            ServerPlayerEntity playerEntity = (ServerPlayerEntity) player;
            ProjectAOTNetworking.sendEnergyValuePacket(playerEntity.getServer(), playerEntity, energy);
            return energy;
        } else {

        }
        return 0;

    }

    public static int removeEnergy(IEntityDataSaver player, int amount) {
        NbtCompound nbt = player.getPersistentData();
        if(nbt != null) {
            int energy = nbt.getInt("projectaot.energy");
            if (energy - amount <= 100) {
                energy = 0;
            } else {
                energy -= amount;
            }
            nbt.putInt("projectaot.energy", energy);
            //sync data
            return energy;
        }
        return 0;

    }
}``` as I was coding this, I know for a fact this is wrong
#

since casting ServerPlayerEntity to IEntityDataSaver probably wont do sh*t lol

#

import net.minecraft.nbt.NbtCompound;

public interface IEntityDataSaver {
    NbtCompound getPersistentData();
}
``` *IEnergyDataSaver class*
severe tapir
alpine goblet
severe tapir
#

Oh sweet

alpine goblet
#

Something registered to that

#

Maybe it's serverworldevents

severe tapir
#

well actually, good news

#
                IEntityDataSaver dataSaver = (IEntityDataSaver) player;
                EnergyData.addEnergy(dataSaver, 1);

            }``` the class where im getting a server entity is where im calling my function
#

so it should be working...?

#

maybe.

alpine goblet
#

Right there is perfect

severe tapir
#

the full class is up above

alpine goblet
#

Any time you edit the energy you send an update

severe tapir
#

but it is a @Override public void onStartTick(MinecraftServer minecraftServer) {

#

awesome.

#

Thank you for your help so far!

#

I guess the last step would be retrieving this value in the HUD

alpine goblet
#

Yep! All that's left is registering it.

severe tapir
#

haha yea

severe tapir
#

so currently I have it sending the packet, now I need to register it.

#

awesome

#

thank you

alpine goblet
#

Hah, I did it right on accident

severe tapir
#

lol

#

am I looking for PayloadTypeRegistry.playS2C().register(ActiveSpellsPacket.ID, ActiveSpellsPacket.CODEC);?

alpine goblet
#

Yep

severe tapir
#

so for me itll be PayloadTypeRegistry.playS2C().register(EnergyPayload.ID, EnergyPayload.CODEC);?

alpine goblet
#

Exactly

severe tapir
#

awesome

#

so now I need to retrieve it in HUD correct?

alpine goblet
#

Not yet. It's being sent to your client, and the client won't freak out when it gets it, but it still will just ignore it.

severe tapir
#

so you recommend making a class like that?

alpine goblet
alpine goblet
#

Less boilerplate that way

severe tapir
#

ahh I see, I may just use a class since the examples you are providing do as well. Eventually I can learn how to use it with a lambda statement, rn it's just numbers and random code in my brain atm

#

coding it up rn

#

interesting, so you use a HUDManager?

#

makes sense... lol

alpine goblet
#

Yeah. It's more of a cache handler, but it's all for the hud

severe tapir
#

actually looking at it now, it may be easier to use the lambda.

#

I assume I can just put the lamda in my HUD class

alpine goblet
#

Well, you've got to register the handler when the game starts, so it would be in Something implements ClientModInitializer

severe tapir
#
    public static final ClientNetworkHandler CLIENT_NETWORK_HANDLER = new ClientNetworkHandler();

    @Override
    public void onInitializeClient() {
        ClientPlayNetworking.registerGlobalReceiver(EnergyPayload.ID, CLIENT_NETWORK_HANDLER);
    }
}```
alpine goblet
#

yeah, in there

#

That works

#

You'd just replace the last CLIENT_NETWORK_HANDLER with () -> { /* do stuff */}

severe tapir
#

oh? interesting. Is it possible to just do. ClientPlayNetworking.registerGlobalReceiver(EnergyPayload.ID, (payload, context) -> { context.client().execute(() -> { //do shit }); }); in my HUD class? or is that crazy

#

since in this case

#

I can pull my value

#

in the same function the HUD is being made

alpine goblet
#

You can't do that, since you've got to make that registerGlobalReceiver call when the client starts. You can, however, merge your HUD class with your ClientNetworkHandler.

severe tapir
#
    public void onHudRender(DrawContext drawContext, RenderTickCounter renderTickCounter) {
        int x = 100;
        int y = 100;
        MinecraftClient client = MinecraftClient.getInstance();
        if(client != null) {
            int width = client.getWindow().getScaledWidth();
            int height = client.getWindow().getScaledHeight();

            x = width / 2;
            y = height;

        }
        if(client.player != null) {
            IEntityDataSaver p = (IEntityDataSaver) client.player;
            if(p.getPersistentData() != null) {


                int energy = p.getPersistentData().getInt("projtectaot.energy");
                ClientPlayNetworking.registerGlobalReceiver(EnergyPayload.ID, (payload, context) -> {
                    context.client().execute(() -> {
                        //do shit
                    });
                });


                drawContext.drawText(client.textRenderer, energy + "/100", x+10, y, 0xFFFFFFFF, true);

                if(energy > 0) {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, FILLED_ENERGY, x, y, 0,0,16,16, 16,16);

                } else {
                    drawContext.drawTexture(RenderLayer::getGuiTexturedOverlay, EMPTY_ENERGY, x, y, 0,0,16,16, 16,16);

                }
            }
        }
    }
}``` heres a full expansion
severe tapir
#

sounds good

#

I think im catching on

#

I do have to log off

#

BUT

#

thank you so much for your help

#

im a lot closer now with your guidance

#

Ill keep at this

#

hopefully one day itll stick with me lol

alpine goblet
#

Perfect timing I have to go too lol. Enjoy!

severe tapir
#

I just had an awakening, I now understand 😂😂