Jump to content

[Solved]Problem with Event ordering[1.10] (How to properly update Capability for GUI on client)


Recommended Posts

Posted (edited)

I've made my custom Capability, everything is working pretty well. I have an error though where when I enter a world, my custom Mana bar will just be set to the default value for mana on the client side. Which means that the Mana bar that I've made shows 0 mana untill I use an mana consuming item and force an update. 

 

I've written a Packet to update my client with the server information and this packet works marvelous. Whenever the game launches my client needs to have the Capability attached again while the server retrieves the information from the NBT file, that's why I'm sending a packet that updates the client with all the server information. 

 

The problem I'm running in to is that I'm trying to run an PlayerLoggedInEvent to update my client with all the information needed. But this PlayerLoggedInEvent fires BEFORE Capabilities are added. 

 


[23:40:15] [Server thread/INFO]: [com.cheese.rpvp.capabilities.handlers.CustomDataHandlerBar:loggedIn:68]: Message sent login, mana = 90 maxmana 100
[23:40:16] [Client thread/INFO]: [com.cheese.rpvp.capabilities.handlers.CustomDataHandlerBar:attachCapabilities:37]: Custom ability Bar added
[23:40:16] [Client thread/INFO]: [com.cheese.rpvp.capabilities.handlers.CustomAdventurerHandler:attachCapabilities:31]: Custom ability Adv added
[23:40:16] [Client thread/INFO]: [com.cheese.rpvp.capabilities.handlers.CustomItemBar:attachCapabilities:34]: Capability added

 

Now I've already tried setting my playerLoggedInEvent to the lowest priority: 

    @SubscribeEvent(priority = EventPriority.LOWEST)
    public void loggedIn(PlayerLoggedInEvent event){
        if(!event.player.worldObj.isRemote) {
            if (event.player.hasCapability(CAPABILITY_BAR, EnumFacing.DOWN)) {
                final IBarHandler instance = getHandler(event.player);
                final NBTTagCompound tag = new NBTTagCompound();
                tag.setInteger("maxmana", instance.getMaxMana());
                tag.setInteger("maxhealth", instance.getMaxHealth());
                tag.setInteger("mana", instance.getMana());
                tag.setInteger("health", instance.getHealth());
                tag.setInteger("fatigue", instance.getFatigue());
                tag.setInteger("job", instance.getJob());
                Main.packetHandler.barWrapper.sendTo(new BarMessage(tag), (EntityPlayerMP)event.player);
                System.out.println("Message sent login, mana = " + instance.getMana() + " maxmana " + instance.getMaxMana());

            }
        }
    }

 

while setting my AttachCapabilities to the highest. 

    @SubscribeEvent(priority = EventPriority.HIGH)
    public void attachCapabilities(AttachCapabilitiesEvent<Entity> event){
        if(event.getObject() instanceof EntityPlayer) {
                System.out.println("Custom ability Bar added");
                event.addCapability(new ResourceLocation(Main.MODID, "BARS"), new BarProvider());
            }
        }

 

 

to no Avail. The order keeps the same and when my player logs in his manabar looks completely empty, even if on the server side he has plenty of mana.

 

 

What I'm wondering now is: Is there any Event that I can call that will fire AFTER my capability is added. I need an EntityPlayer to attach to the message so I can properly update my Capabilities. If there's a way to make my Capability fire first that'll work too.  

 

 

Problem description:

 

My Messagehandler OnPlayerLoggedInEvent which updates my client capability is sent BEFORE the capability is attached to the client. I'm looking for a different event to call when my client joins the server that will pop AFTER capabilities are added, but preferably within a second of joining a world. EntityJoinWorldEvent with instanceof EntityPlayer also doesn't work.

 

The events all work seperately, and I can call the method that updates the info after the capability is added and it works fine, so that's not the problem. I just can't get them to operate in the wanted order.

 

SOLLUTION:

Quote

Edit: I Made a variable in my GuiHandler using a ClientConnectedToServerEvent to make sure that the client would request an update every time it connected To make sure I didn't send a message every tick the client would check if the player had access to my Mana and then if the variable was True, set it to false after a use. The ClientConnectedToServerEvent made sure the variable was properly set every time I logged in again.. Then I created a simple packet from the client to the server to ask for an update, on which the server send back a packet with the relevant, updated information.

 

No tickhandler required, no funky workaround. I'm quite frankly not sure why I didn't try this in the first place. 

 

Edited by oldcheese
Posted

When I run into these sort of things I often just simply set a "timer" that delays the action a tick or two.  You can just create a boolean field called something like packetSent that defaults to false and is also set to false in the player joins world event and then in the player tick event you can check if it is false and if so (and on the server) you send the packet and set packetSent to true. If the player tick event is too soon as well, you can make the field a counter and set it to something like 2 and count down to delay the packet a couple ticks. If you're worried that two players might join in exactly the same tick you should use a map of booleans instead, but hopefully you get the idea.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Would players be able to inherit information from eachother if I delayed it too much and another player joined? I'd assumed that when I get an entity from a joinworldevent it'd be a specific player, not an object redirecting me to the latest player. Maybe I'm misreading this.

 

But your sollution is to register a new tickhandler for playerTickEvent and not send the update packet untill the Capability is attached? That seems like as good as a workaround as any, though I've been a bit reluctant to use too many tickhandlers myself. 

 

Thanks for the help. When I get back from college I'll start rewriting my code and post updates if I solve it. For future readers.

Posted

This is completely off the top of my head but isn't there an event called something like GatherCapabilitiesEvent that fires after the AttachCapabilitiesEvents?

Failing that what's wrong with EntityJoinWorldEvent? It's all right for me

Posted (edited)
4 hours ago, FredTargaryen said:

This is completely off the top of my head but isn't there an event called something like GatherCapabilitiesEvent that fires after the AttachCapabilitiesEvents?

Failing that what's wrong with EntityJoinWorldEvent? It's all right for me

Well, my EntityJoinWorldEvent fires BEFORE the Attach capability event. That's where it fails. I want to update my client by sending them a packet. But if the packet attempts to update before the capability is attached to the client, there's not much use. 

 

I can try to use the gather capabilities event. But if you have an implementation of a packet where it successfully updates the client side after logging in using EntityJoinWorldEvent I'd love to see how you did it. Fixing something I did wrong would be easier than trying to workaround using tick handlers 

 

Sorry if the spelling sucks. I'm typing from my phone still. 

 

 

Update: I created a playerTickHandler with a few booleans. One boolean from my GuiHandler that draws the actual element that turns to true when things aren't properly loaded on the client side. Then a boolean near the Eventhandler to make sure that the load isn't called a second time. This ensures that whenever my capabilities DO exist (Or my GUI couldn't access them) the tickhandler updates the player once, giving them the proper resources.

 

Edit: This only works in single player now that I test it. Back into the fray I go.

 

 

Edit: Made a variable in my GuiHandler using a ClientConnectedToServerEvent to make sure that the client would request an update every time it connected. Then I created a simple packet from the client to the server to ask for an update, on which the server send back a packet with the relevant, updated information.

 

No tickhandler required, no funky workaround. I'm quite frankly not sure why I didn't try this in the first place. 

Edited by oldcheese
Sollution. Kinda.
  • Like 1
Posted

Yeah sometimes turning a problem around can be simpler.

 

I  think you kinda made my suggestion more complicated than it needed to be. I think it may not have worked because you confused the client and server side processing (usually if something works in single player and not in multiplayer it is a problem with side management). But looks like you made a real attempt and I know packets to sync client can be a pain.

 

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

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



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
    • Remove rubidium - you are already using embeddium, which is a fork of rubidium
  • Topics

×
×
  • Create New...

Important Information

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