Jump to content

[1.9] Capability Syncing


Notunknown

Recommended Posts

I am having trouble syncing Capabilities.

 

So in PlayerEvent.Clone I set a field in a capability. This field change in a capability causes it to send data to the client (if logically on the server) using my packets.

 

However, I must be doing something wrong, because it isn't changing.

 

Here is my message handler (it is intended solely for the logical client):

 

public class ResearchMessageHandler implements IMessageHandler<ResearchMessage, IMessage>
{
@Override
public IMessage onMessage(ResearchMessage message, MessageContext ctx)
{
	EntityPlayer player = Minecraft.getMinecraft().thePlayer;

	if (player.hasCapability(CommonProxy.RESEARCH_CAP, null)) //This is true
	{
		IPlayerResearchCapability cap = player.getCapability(CommonProxy.RESEARCH_CAP, null);

		cap.setResearchStateNoCheck(null, message.research, message.state); //This works successfully.
	}

	return null; //Here it still works. But after here the changes mysteriously vanish.
}
}

Link to comment
Share on other sites

http://greyminecraftcoder.blogspot.com.au/2015/01/thread-safety-with-network-messages.html

 

Additionally, idk if Clone event allows syncing player cloned with himself (his client).

 

Player clonning happens e.g when you respawn, and in that moment server has two players (old and new), and client has only old. Sending data about new player while client didn't spwn him yet kills updates. Thread safety MIGHT solve this (but I am not sure).

 

What I am onto - Clone event should never be used to sync data.

Use EntityJoinedWorldEvent - it is called always when you join world and is best for syncing initial data.

1.7.10 is no longer supported by forge, you are on your own.

Link to comment
Share on other sites

As it turns out it was succeeding because of a bad if-statement.

 

Same scenario.

 

public class ResearchMessageHandler implements IMessageHandler<ResearchMessage, IMessage>
{
@Override
public IMessage onMessage(final ResearchMessage message, MessageContext ctx)
{
	Minecraft.getMinecraft().addScheduledTask(new Runnable()
	{
		@Override
		public void run()
		{
			EntityPlayer player = Minecraft.getMinecraft().thePlayer;

			if (player.hasCapability(CommonProxy.RESEARCH_CAP, null))
			{
				IPlayerResearchCapability cap = player.getCapability(CommonProxy.RESEARCH_CAP, null);

				cap.setResearchState(null, message.research, message.state);
			}
		}
	});

	return null;
}
}

Link to comment
Share on other sites

Where are you sending the packet from? Show that code, too.

 

Btw, you should not be using Minecraft.getMinecraft() directly - use your proxies:

// CommonProxy
/**
 * Returns a side-appropriate EntityPlayer for use during message handling
 */
public EntityPlayer getPlayerEntity(MessageContext ctx) {
	return ctx.getServerHandler().playerEntity;
}

/**
 * Returns the current thread based on side during message handling,
 * used for ensuring that the message is being handled by the main thread
 */
public IThreadListener getThreadFromContext(MessageContext ctx) {
	return ctx.getServerHandler().playerEntity.getServerForPlayer();
}

// ClientProxy:
@Override
public EntityPlayer getPlayerEntity(MessageContext ctx) {
	return (ctx.side.isClient() ? mc.thePlayer : super.getPlayerEntity(ctx));
}

@Override
public IThreadListener getThreadFromContext(MessageContext ctx) {
	return (ctx.side.isClient() ? mc : super.getThreadFromContext(ctx));
}

// message code:
YourMod.proxy.getThreadFromContext(ctx).addScheduledTask(...

EntityPlayer player = YourMod.proxy.getPlayerEntity(ctx);

Link to comment
Share on other sites

The sending of the message is complicated. The code which initiates the sending of the message is some test code in an EntityJoinWorldEvent, which ensures that the logical side is SERVER. The part which actually sends the code is:

 

NETWORK_WRAPPER.sendTo(new ResearchMessage(research, state), (EntityPlayerMP)player);

Link to comment
Share on other sites

I am having another issue: I am having the client send a request to the server, and the server is supposed to respond with a response message. However, it then proceeds to crash. For some reason, the request message is being re-processed (I think) on the incorrect side (I think). I have no idea what is happening.

 

package com.phantasma.phantasma.network;

import io.netty.buffer.ByteBuf;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;

public class RequestMessage implements IMessage
{
public RequestType type;

public RequestMessage()
{

}

public RequestMessage(RequestType type)
{
	this.type = type;
}

@Override
public void fromBytes(ByteBuf buf)
{
	int ordinal = buf.readInt(); //java.lang.IndexOutOfBoundsException: readerIndex(0) + length(4) exceeds writerIndex(2): SlicedByteBuf(ridx: 0, widx: 2, cap: 2/2, unwrapped: UnpooledHeapByteBuf(ridx: 0, widx: 3, cap: 256))
	this.type = RequestType.values()[ordinal];
}

@Override
public void toBytes(ByteBuf buf)
{
	int ordinal = this.type.ordinal();
	buf.writeInt(ordinal);
}

public static enum RequestType
{
	RESEARCH
}
}

 

package com.phantasma.phantasma.network;

import com.phantasma.phantasma.Phantasma;
import com.phantasma.phantasma.network.RequestMessage.RequestType;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
import net.minecraftforge.fml.relauncher.Side;

public class RequestMessageHandler implements IMessageHandler<RequestMessage, IMessage>
{
@Override
public IMessage onMessage(RequestMessage message, MessageContext ctx)
{
	if (ctx.side == Side.SERVER)
	{
		EntityPlayer player = Phantasma.proxy.getEntityPlayer(ctx);

		switch(message.type)
		{
		case RESEARCH:
		{
			return new ResearchMessage(player.getCapability(Phantasma.RESEARCH_CAP, null).serialize());
		}
		}
	}
	return null;
}
}

Link to comment
Share on other sites

You have not implemented the thread-safety in your server handler. And using a request message here is pointless, just push the message out from the server without request when the player logs in (PlayerLoggedInEvent).

 

Whenever I respawn I would lose all the capability data, unless there is an event I am missing.

Link to comment
Share on other sites

PlayerEvent.Clone, but you shouldn't send packets from there; instead, use EntityJoinWorldEvent (or possibly RespawnEvent, though I've never tried that one) if you need to sync data after player death and respawn.

 

PlayerEvent.Clone was not working on the client thread, and EntityJoinWorldEvent was not working for sending from the server thread, for some reason.

Link to comment
Share on other sites

PlayerEvent.Clone

Is called ONLY on server and can be used to copy server data from dead player to new player.

Then when new player joins (after respawn) you can send packet from EntityJoinWorldEvent to update client-side NEW player.

 

It WILL work if you use thread safety, which was alredy pointed out.

1.7.10 is no longer supported by forge, you are on your own.

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.



×
×
  • Create New...

Important Information

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