Jump to content

Recommended Posts

Posted (edited)

Hi

 

I have this one-variable-capability that needs to sync to all clients on the server in order for them to check in their event handler what to render:

IntercraftEventHandler.java

...
@OnlyIn(Dist.CLIENT)
@SubscribeEvent
public static void onRenderEntity(RenderLivingEvent.Specials.Pre event)
{
    if (event.getEntity() instanceof PlayerEntity) {
        if (event.isCancelable()) {

            IIdentityHidden hidden = event.getEntity().getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
            if (hidden.getHidden()) {


		// If true, cancel this event.
                event.setCanceled(true);

            }
        }
    }
}
...

 

Here are the capability files that store a single boolean value:

 
 
 
 
  Reveal hidden contents

 

Then I register it like so:

IntercraftCore.java

...
public void onCommonSetup(final FMLCommonSetupEvent event)
{
	CapabilityManager.INSTANCE.register(IIdentityHidden.class,  new IdentityHiddenStorage(),   new IdentityHiddenStorage.Factory());
}
...

 

 

The problem is that the capability is stored on the server and therefore nothing happens. So I created a message to send to all clients when that value changed in my network..

IntercraftPacketHandler.java

 
 
 
 
  Reveal hidden contents

 

.. and stored it like this in IntercraftCore.java:

public static final SimpleChannel NETWORK = IntercraftPacketHandler.getNetworkChannel();

 

 

Problem

Ok, that's the code I have, and I wouldn't be here if it worked. I have 0 experience with this and last time it didn't work at all. So if its a common mistake I'll listen.

I have no problem with starting a singleplayer world, but when I log in on a server or log in to a LAN world the client that logs in crashes with a NullPointerException.

 

---- Minecraft Crash Report ----
// I just don't know what went wrong :(

Time: 10/30/19 11:16 AM
Description: Unexpected error

java.lang.NullPointerException: Unexpected error
	at net.minecraft.client.multiplayer.PlayerController.func_78750_j(PlayerController.java:251) ~[?:?] {pl:runtimedistcleaner:A}
	at net.minecraft.client.multiplayer.PlayerController.func_78765_e(PlayerController.java:231) ~[?:?] {pl:runtimedistcleaner:A}
	at net.minecraft.client.Minecraft.func_71407_l(Minecraft.java:1283) ~[?:?] {pl:accesstransformer:B,pl:runtimedistcleaner:A}
	at net.minecraft.client.Minecraft.func_195542_b(Minecraft.java:866) ~[?:?] {pl:accesstransformer:B,pl:runtimedistcleaner:A}
	at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:384) ~[?:?] {pl:accesstransformer:B,pl:runtimedistcleaner:A}
	at net.minecraft.client.main.Main.main(SourceFile:155) ~[1.14.4-forge-28.1.1.jar:?] {}
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_181] {}
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_181] {}
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_181] {}
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_181] {}
	at net.minecraftforge.fml.loading.FMLClientLaunchProvider.lambda$launchService$0(FMLClientLaunchProvider.java:56) ~[forge-1.14.4-28.1.1.jar:28.1] {}
	at cpw.mods.modlauncher.LaunchServiceHandlerDecorator.launch(LaunchServiceHandlerDecorator.java:37) [modlauncher-3.2.0.jar:?] {}
	at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:50) [modlauncher-3.2.0.jar:?] {}
	at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:68) [modlauncher-3.2.0.jar:?] {}
	at cpw.mods.modlauncher.Launcher.run(Launcher.java:80) [modlauncher-3.2.0.jar:?] {}
	at cpw.mods.modlauncher.Launcher.main(Launcher.java:65) [modlauncher-3.2.0.jar:?] {}

...

 

Full + download:

  Reveal hidden contents

 

Edited by Simon_kungen
Posted
  On 10/30/2019 at 12:12 PM, diesieben07 said:

You cannot send packets at this point.

 

To sync a capability to all clients you need to send the sync packet under the following conditions:

  • In PlayerLoggedInEventPlayerRespawnEvent and PlayerChangedDimensionEvent send the player's data to that player.
  • In PlayerEvent.StartTracking send the data of PlayerEvent.StartTracking#getTarget (provided that it's a player) to PlayerEvent.StartTracking#getPlayer.
  • Whenever the data of player P changes, send that data to the players tracking player P and player P (you can use PacketDistributor.TRACKING_ENTITY_AND_SELF).
Expand  

Ok, so, umm. I changed IdentityHidden.java to this:

public class IdentityHidden implements IIdentityHidden
{

    private boolean hidden;


    public IdentityHidden()
    {
        this.hidden = false;
    }


    @Override
    public boolean getHidden()
    {
        return hidden;
    }

    @Override
    public void setHidden(boolean hidden)
    {
        this.hidden = hidden;
        IntercraftCore.NETWORK.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.noArg(), new MessageIdentityHidden(hidden));

    }
}

 

And in IntercraftEventHandler.java I added this:

@SubscribeEvent
public static void onPlayerLoggedIn(final PlayerEvent.PlayerLoggedInEvent event)
{
    IIdentityHidden hidden = event.getEntity().getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(hidden.getHidden()));
}

@SubscribeEvent
public static void onPlayerRespawn(final PlayerEvent.PlayerRespawnEvent event)
{
    IIdentityHidden hidden = event.getEntity().getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(hidden.getHidden()));
}

@SubscribeEvent
public static void onPlayerChangedDimension(final PlayerEvent.PlayerChangedDimensionEvent event)
{
    IIdentityHidden hidden = event.getEntity().getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(hidden.getHidden()));
}

@SubscribeEvent
public static void onPlayerStartTracking(final PlayerEvent.StartTracking event)
{
    IIdentityHidden hiddenTarget = event.getTarget().getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(hiddenTarget.getHidden()));

}

 

Is there something I missed? I have it send the updated value in the setter function of my capability class so I can more easily use it in other places such as my debug command and in the item that changes that value when worn (CuriosAPI Item).

 

ItemMask.java

...
@Override
public void onEquipped(String identifier, LivingEntity entityLivingBase)
{
    ItemMask.this.onEquipped(identifier, entityLivingBase);
    playEquipSound(entityLivingBase);

    if (hidesIdentity()) {
        if (entityLivingBase instanceof PlayerEntity) {
            entityLivingBase.sendMessage(new TranslationTextComponent("info."+Reference.MODID+".identity.hidden"));

            IIdentityHidden hidden = entityLivingBase.getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
            if (!hidden.getHidden())
                hidden.setHidden(true);
        }
    }
}

@Override
public void onUnequipped(String identifier, LivingEntity entityLivingBase)
{
    ItemMask.this.onUnequipped(identifier,entityLivingBase);

    if (hidesIdentity()) {
        if (entityLivingBase instanceof PlayerEntity) {
            entityLivingBase.sendMessage(new TranslationTextComponent("info."+Reference.MODID+".identity.shown"));

            IIdentityHidden hidden = entityLivingBase.getCapability(IdentityHiddenProvider.HID_CAP).orElse(IdentityHiddenProvider.HID_CAP.getDefaultInstance());
            if (hidden.getHidden())
                hidden.setHidden(false);
        }
    }
}
...

 

Posted
  On 10/30/2019 at 2:50 PM, diesieben07 said:

TRACKING_ENTITY_AND_SELF requires an entity argument. This code is going to crash with a NullPointerException.

 

This doesn't make sense. If you epxect the capability to always be there (which you do, you attach it to all players after all), then you should throw an exception if it's absent. That's what orElseThrow is for. Always prefer throwing exceptions upon invalid state instead of silently continueing with useless default values.

 

You haven't shown your packet class, but considering you only pass it one value it cannot possibly be correct. It needs the entity ID of the player that the packet is about, too. Otherwise the client has no idea whose capability to update with the new value.

Expand  

 

Ok, updated so it uses a throws a NullPointerException instead of a default implementation. And you're right about me not having the Package class correct, which is something I forgot to show, so here it is updated with player UUID as the second argument:

MessageIdentityHidden.java

public class MessageIdentityHidden
{

    private final boolean hidden;
    private final UUID playerUUID;

    public MessageIdentityHidden(UUID playerUUID, boolean hidden)
    {
        this.hidden = hidden;
        this.playerUUID = playerUUID;
    }

    public MessageIdentityHidden(UUID playerUUID)
    {
        this.hidden = false;
        this.playerUUID = playerUUID;
    }



    public static void encode(final MessageIdentityHidden message, final PacketBuffer buffer)
    {
        buffer.writeBoolean(message.hidden);
        buffer.writeUniqueId(message.playerUUID);
    }


    public static MessageIdentityHidden decode(final PacketBuffer buffer)
    {
        return new MessageIdentityHidden(buffer.readUniqueId(), buffer.readBoolean());
    }

    public static void handle(MessageIdentityHidden message, Supplier<NetworkEvent.Context> ctx)
    {
        ctx.get().enqueueWork(() -> {
            
      		// No idea what to do here.



        });
        ctx.get().setPacketHandled(true);
    }
}

 

I have mostly looked at ToasterMod-1.14's code as I have no idea how to do this. And as he left his handle function empty I did the same with mine.

Posted
  On 10/30/2019 at 7:55 PM, diesieben07 said:

Do not use the UUID. Use the entity ID, it is more lightweight and made exactly for this purpose.

As for the "No idea what to do here": You get the entity from the world (World#getEntityByID) and then update it's capability (provided that it's a player, which it should be unless the server is lying).

Expand  

Ok, I had so if you want to sync to other clients you have to specify a player. But now the problem is that it doesn't find the capability on the player anymore in onPlayerStartTracking:

public static void onPlayerStartTracking(final PlayerEvent.StartTracking event)
{
    IIdentityHidden hiddenTarget = event.getTarget().getCapability(IdentityHiddenProvider.HID_CAP).orElseThrow(NullPointerException::new);
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(event.getPlayer().getEntityId(),hiddenTarget.getHidden()));

}

 

IdentityHidden.java

public class IdentityHidden implements IIdentityHidden
{

    private boolean hidden;


    public IdentityHidden(boolean hidden)
    {
        this.hidden = hidden;
    }

    public IdentityHidden()
    {
        this.hidden = false;
    }



    @Override
    public boolean getHidden()
    {
        return hidden;
    }

    @Override
    public void setHidden(PlayerEntity player,boolean hidden) // For client syncing.
    {
        this.hidden = hidden;
        IntercraftCore.NETWORK.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> player), new MessageIdentityHidden(player.getEntityId(), hidden));

    }

    @Override
    public void setHidden(boolean hidden)
    {
        this.hidden = hidden; // For server reading.
    }
}

 

MessageIdentityHidden.java

public class MessageIdentityHidden
{

    private final boolean hidden;
    private final int playerID;

    public MessageIdentityHidden(int playerID, boolean hidden)
    {
        this.hidden = hidden;
        this.playerID = playerID;
    }

    public MessageIdentityHidden(int playerID)
    {
        this.hidden = false;
        this.playerID = playerID;
    }



    public static void encode(final MessageIdentityHidden message, final PacketBuffer buffer)
    {
        buffer.writeBoolean(message.hidden);
        buffer.writeInt(message.playerID);
    }


    public static MessageIdentityHidden decode(final PacketBuffer buffer)
    {
        return new MessageIdentityHidden(buffer.readInt(), buffer.readBoolean());
    }

    public static void handle(MessageIdentityHidden message, Supplier<NetworkEvent.Context> ctx)
    {
        ctx.get().enqueueWork(() -> {

            PlayerEntity player = (PlayerEntity) ctx.get().getSender().getServerWorld().getEntityByID(message.playerID);
            IIdentityHidden hidden = player.getCapability(IdentityHiddenProvider.HID_CAP).orElseThrow(NullPointerException::new);
            hidden.setHidden(player, message.hidden);



        });
        ctx.get().setPacketHandled(true);
    }
}

 

Posted
  On 10/30/2019 at 10:29 PM, diesieben07 said:

You are now trying to send the packet also on the client (because your packet calls setHidden when it's received, which sends the packet). That makes no sense.

Expand  

Ok, so. If the in the handle function message's playerID is the sender, who's capability am I supposed to change?

 

  On 10/30/2019 at 10:29 PM, diesieben07 said:

Your packet is received on the client. You cannot use NetworkEvent.Context#getSender. It makes no sense on the client (like explained in it's documentation).

Expand  

Alright. If the playerID is not the right player who's capability I'm not supposed to change, who's should I do? And in that case how do I get the server world in the handle function? And does that even make sense to do it like that? Get the capability in the function and change it to the message's value. Should I scrap completely the playerID parameter for the setHidden() function and not send a message when I change the value?

 

  On 10/30/2019 at 10:29 PM, diesieben07 said:
  On 10/30/2019 at 9:24 PM, Simon_kungen said:

But now the problem is that it doesn't find the capability on the player anymore in onPlayerStartTracking:

Expand  

Please elaborate.

Expand  

Now when I join a server or start a server world it appears to not find the IdentityHidden capability of the target player and therefore throws the NullPointerException error.

 

@SubscribeEvent
public static void onPlayerStartTracking(final PlayerEvent.StartTracking event)
{
    IIdentityHidden hiddenTarget = event.getTarget().getCapability(IdentityHiddenProvider.HID_CAP).orElseThrow(NullPointerException::new); // <-- doesn't find the capability and throws a error.
    IntercraftCore.NETWORK.send(PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) event.getPlayer()),new MessageIdentityHidden(event.getPlayer().getEntityId(),hiddenTarget.getHidden()));

}

 

 

The last one is what confuses me the most. Before it worked fine with the .orElseThrow fix you told me to change due to it not making sense because it should put it on all players. But I'm not even sure if I'm even attaching capabilities correctly. I thought it simply needed to have the @SubscribeEvent annotation in my Event Handler class which has the @Mod.EventBusSubscriber annotation to the class, which ended up not firing. So in my main mod class I just attached it as a listener to MinecraftForge.EVENT_BUS.

IntercraftCore.java

MinecraftForge.EVENT_BUS.addListener(IntercraftEventHandler::attachCapabilityEntity);

 

It worked, so I left it at that.

Posted
  On 11/1/2019 at 2:53 PM, diesieben07 said:
  On 11/1/2019 at 2:08 PM, Simon_kungen said:

If the playerID is not the right player who's capability I'm not supposed to change, who's should I do?

Expand  

I don't know where you got this idea from. It's not what I said. 

Expand  

 

  On 11/1/2019 at 2:53 PM, diesieben07 said:
  On 11/1/2019 at 2:08 PM, Simon_kungen said:

And in that case how do I get the server world in the handle function?

Expand  

You can't. You are on the client. There is no server world on the client.

Expand  

Ok, sorry. In that case, how to get the player from the playerID?

MessageIdentityHidden.java

public static void handle(MessageIdentityHidden message, Supplier<NetworkEvent.Context> ctx)
{
    ctx.get().enqueueWork(() -> {
        PlayerEntity player = message.playerID// How do I get the target player by the ID here?
        IIdentityHidden hidden = player.getCapability(IdentityHiddenProvider.HID_CAP).orElseThrow(NullPointerException::new);
        hidden.setHidden(message.hidden);



    });
    ctx.get().setPacketHandled(true);
}

 

Posted

Ok. Looks like it is finally working, I had some issues with the playerID being 0. The decoder has to write the values in the same order they are read, which I presumed didn't matter due to them being two different primitive types (int and boolean). Then I had a simple filter as to not change the value of the client's just in case.

  • Simon_kungen changed the title to [SOLVED] [1.14.4] Problems with Networking
Posted
  On 11/1/2019 at 5:14 PM, diesieben07 said:

This sounds very suspicious... Can you share your final code?

Expand  

MessageIdentityHidden.java

public class MessageIdentityHidden
{

    private final boolean hidden;
    private final int playerID;

    public MessageIdentityHidden(int playerID, boolean hidden)
    {
        this.hidden = hidden;
        this.playerID = playerID;
    }



    public static void encode(final MessageIdentityHidden message, final PacketBuffer buffer)
    {
        buffer.writeInt(message.playerID);
        buffer.writeBoolean(message.hidden);
    }


    public static MessageIdentityHidden decode(final PacketBuffer buffer)
    {
        return new MessageIdentityHidden(buffer.readInt(), buffer.readBoolean());
    }

    public static void handle(MessageIdentityHidden message, Supplier<NetworkEvent.Context> ctx)
    {
        ctx.get().enqueueWork(() -> {

            if (message.playerID == Minecraft.getInstance().player.getEntityId()) return;

            PlayerEntity player = (PlayerEntity) Minecraft.getInstance().player.world.getEntityByID(message.playerID);

            IIdentityHidden hidden = player.getCapability(IdentityHiddenProvider.HID_CAP).orElseThrow(NullPointerException::new);
            hidden.setHidden(message.hidden);



        });
        ctx.get().setPacketHandled(true);
    }
}

 

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.