#Sending NBT int value to Client HUD
148 messages · Page 1 of 1 (latest)
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.
You're pretty much there, except for returning null on "getId." I believe you get the networkHandler from a ServerPlayerEntity and send it there, possibly with ServerPlayNetworking from fapi. Let me pull up my code ra
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; }
Something like that. Doesn't need to be in a separate class or have the onPlayReady hook ofc
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...?
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
Don't believe it is
Oh well then no xd
hahah
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
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
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
Definitely not
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
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.
if this doesn't answer your question feel free to ask me more about it! I appreciate your help!
Nah, that's exactly what I was looking for 👍
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
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
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?
Yep!
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
Are you using split sources?
You'd have a main folder and a client folder set up for you if you are
🤨
Can you search your build.gradle for "split" as in split sources?
splitEnvironmentSourceSets
no results
Ok, not using split sources then. Good to know.
I may have something interesting.... let me know what you think about this
Keep the sides as separate as you can, tangling them up is unfun. Anyways, you want this on the server side, so somewhere from the server entrypoint
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
You're mixing up sides here. Your "onHudRender" method can only ever be called on the client, so trying to send the packet isn't helpful - the client isn't the one that controls the power. It has to be told the value, not tell it.
This is much better, but it needs to take a ServerPlayerEntity - that's the server's copy of the player
!!sides if that helps
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*
I guess what im struggling with is: where do I put this function xddd
You can do that, as long as you inject the interface - it's actually a rather good way of doing it.
Oh sweet
ServerLifecycleEvents.TICK or something
Something registered to that
Maybe it's serverworldevents
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.
Right there is perfect
the full class is up above
Any time you edit the energy you send an update
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
Yep! All that's left is registering it.
haha yea
And that
https://github.com/skycatminepokie/mystical/blob/master/src%2Fmain%2Fjava%2Fcom%2Fskycat%2Fmystical%2FMystical.java#L72 registration on the server side
so currently I have it sending the packet, now I need to register it.
awesome
thank you
Oh wait that's the same for both sides
Hah, I did it right on accident
lol
am I looking for PayloadTypeRegistry.playS2C().register(ActiveSpellsPacket.ID, ActiveSpellsPacket.CODEC);?
Yep
so for me itll be PayloadTypeRegistry.playS2C().register(EnergyPayload.ID, EnergyPayload.CODEC);?
Exactly
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.
Then on the client side a handler
so you recommend making a class like that?
You can do it that way, or just use a lambda.
Less boilerplate that way
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
Yeah. It's more of a cache handler, but it's all for the hud
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
Well, you've got to register the handler when the game starts, so it would be in Something implements ClientModInitializer
public static final ClientNetworkHandler CLIENT_NETWORK_HANDLER = new ClientNetworkHandler();
@Override
public void onInitializeClient() {
ClientPlayNetworking.registerGlobalReceiver(EnergyPayload.ID, CLIENT_NETWORK_HANDLER);
}
}```
yeah, in there
That works
You'd just replace the last CLIENT_NETWORK_HANDLER with () -> { /* do stuff */}
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
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.
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
gotcha
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
Perfect timing I have to go too lol. Enjoy!
I just had an awakening, I now understand 😂😂
