Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[Solved][1.16.4] How do I correctly sync capability data between Client and server?


Recommended Posts

Hi, I'm having problems syncing my mod's capability data. There's no errors/crashes or anything, but my code doesn't appear to be doing anything, as reloading the world resets all data values to zero. so I was wondering if someone could look at my code to see if I'm doing anything wrong, or am missing anything.

 

My event handler:

package lk1905.gielinorcraft;

import lk1905.gielinorcraft.api.skill.ISkills;
import lk1905.gielinorcraft.capability.skill.SkillCapability;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerChangedDimensionEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerRespawnEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

@Mod.EventBusSubscriber(modid = Gielinorcraft.MODID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public class EventHandler {

	@SubscribeEvent
	public static void onPlayerClone(PlayerEvent.Clone event) {
		
		LazyOptional<ISkills> oldCap = event.getOriginal().getCapability(SkillCapability.SKILL_CAP, null);
		LazyOptional<ISkills> newCap = event.getPlayer().getCapability(SkillCapability.SKILL_CAP, null);
		ISkills oldSkills = oldCap.orElse(null);
		
		if(oldSkills != null) {
			ISkills newSkills = newCap.orElse(null);
			
			if(newSkills != null) {
				
				for(int i = 0; i < 26; i++) {
					newSkills.setXp(i, oldSkills.getXp(i));
					newSkills.setStaticLevel(i, oldSkills.getStaticLevel(i));
					newSkills.setLevel(i, oldSkills.getLevel(i));
				}
			}
		}
	}
	
	@SubscribeEvent
	public static void onPlayerChangedDimensionEvent(PlayerChangedDimensionEvent event) {
		ServerPlayerEntity player = (ServerPlayerEntity) event.getPlayer();
		if(!player.world.isRemote) {
			player.getCapability(SkillCapability.SKILL_CAP).ifPresent(c -> c.sync(player));
		}
	}
	
	@SubscribeEvent
	public static void onRespawnEvent(PlayerRespawnEvent event) {
		if(!event.getPlayer().world.isRemote) {
			event.getPlayer().getCapability(SkillCapability.SKILL_CAP).ifPresent(c -> c.sync((ServerPlayerEntity) event.getPlayer()));
		}
	}
	
	@SubscribeEvent
	public static void onPlayerConnect(PlayerLoggedInEvent event) {
		ServerPlayerEntity player = (ServerPlayerEntity) event.getPlayer();
		if(!player.world.isRemote) {
			player.getCapability(SkillCapability.SKILL_CAP).ifPresent(c -> c.sync(player));
		}
	}
}

 

My packet handler:

package lk1905.gielinorcraft.network;

import java.util.List;

import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.network.NetworkDirection;
import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.simple.SimpleChannel;

public class PacketHandler {

	private static final String PROTOCOL_VERSION = Integer.toString(1);
	private static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder
												.named(new ResourceLocation("gielinorcraft", "main_channel"))
												.clientAcceptedVersions(PROTOCOL_VERSION::equals)
												.serverAcceptedVersions(PROTOCOL_VERSION::equals)
												.networkProtocolVersion(() -> PROTOCOL_VERSION)
												.simpleChannel();
	public static void register() {
		int disc = 0;
		HANDLER.registerMessage(disc++,
				SkillsPacket.class,
				SkillsPacket::encode,
				SkillsPacket::decode,
				SkillsPacket.Handler::handle);
	}
	
	/**
	 * Sends a packet to a specific player.<br>
	 * Must be called server side.
	 * */
	public static void sendTo(Object msg, ServerPlayerEntity player) {
		if(!(player instanceof FakePlayer)) {
			HANDLER.sendTo(msg, player.connection.netManager, NetworkDirection.PLAY_TO_CLIENT);
		}
	}
	
	/**
	 * Sends a packet to the server.<br>
	 * Must be called client side.
	 * */
	public static void sendToServer(Object msg) {
		HANDLER.sendToServer(msg);
	}
	
	/**Server side.*/
	public static void sendToAllPlayers(Object msg, MinecraftServer server) {
		List<ServerPlayerEntity> list = server.getPlayerList().getPlayers();
		for(ServerPlayerEntity e : list) {
			sendTo(msg, e);
		}
	}
}

 

The packet class for my capability:

package lk1905.gielinorcraft.network;

import java.util.function.Supplier;

import lk1905.gielinorcraft.capability.skill.SkillCapability;
import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent;

public class SkillsPacket {

	private final CompoundNBT nbt;
	
	public SkillsPacket(CompoundNBT nbt) {
		this.nbt = nbt;
	}
	
	public static void encode(SkillsPacket msg, PacketBuffer buf) {
		buf.writeCompoundTag(msg.nbt);
	}
	
	public static SkillsPacket decode(PacketBuffer buf) {
		return new SkillsPacket(buf.readCompoundTag());
	}
	
	public static class Handler{
		public static void handle(final SkillsPacket msg, Supplier<NetworkEvent.Context> ctx) {
		
			Minecraft mc = Minecraft.getInstance();
			
			ctx.get().enqueueWork(() -> {
				
				mc.player.getCapability(SkillCapability.SKILL_CAP).ifPresent(cap -> cap.deserializeNBT(msg.nbt));
				
			});
			ctx.get().setPacketHandled(true);
		}
	}
}

 

All other code is here.

 

 

Link to post
Share on other sites

I don't know if this is intentional but in your SkillStorage, method writeNBT you kept rewrite/override the value of "xp", "static", and "dynamic".

I guess what you are trying to do here is something like:

		for(int i = 0; i < 26; i++) 
        	{
			data.putInt("xp" + i, (int) instance.getXp(i) * 10);
			data.putInt("static" + i, instance.getStaticLevel(i));
			data.putInt("dynamic" + i, instance.getLevel(i));
		}

so does writeNBT(), you assign the same value to every slot or whatever it is.

  • Thanks 1
Link to post
Share on other sites

I don't think there's anything wrong with the SkillStorage, as I can gain xp just fine, in the correct skills, the data is just reset to zero when i close and reopen the world.

 

I also have the "Hitpoints" skill set to level 10 and 1154 xp by default. If I comment out my PlayerLoggedInEvent, the skill is correctly set to those values in game, and is reset to those values when I leave the world. But with the event enabled, both the level and xp values are set to 1 and 0.

Link to post
Share on other sites
On 1/6/2020 at 10:28 AM, diesieben07 said:

If you want the data to be available to the client who's player it is attached to:

  • Send the data to the player in PlayerLoggedInEvent, PlayerRespawnEvent and PlayerChangedDimensionEvent.
  • Send the data to the player whenever it changes.

Don't forget to sync your capability data in Skills#addExp otherwise you wouldn't see any changes before those events.

Link to post
Share on other sites
1 hour ago, Sainthozier said:

Don't forget to sync your capability data in Skills#addExp otherwise you wouldn't see any changes before those events.

This is probably a stupid question, but how would I do that? I tried adding it to SkillStorage the same way I have the getXP, getLevel and getStaticLevel methods, but that made no difference. Is that what you meant, or something different?

Link to post
Share on other sites
12 hours ago, Sainthozier said:

Just call your sync method whenever the data changes.

I added it into the AddXP method like this:

if(entity instanceof PlayerEntity) {
			sync((ServerPlayerEntity) entity);
		}

That didn't make any difference. Is that what you meant?

Link to post
Share on other sites
13 hours ago, diesieben07 said:

This is entirely broken. You are reaching across logical sides. Even if you were on the client here, you must modify capability data on the server, if you want it to persist. Only the server saves data.

I changed that part to this:

		PlayerEntity player = (PlayerEntity) event.getSource().getTrueSource();
		LazyOptional<ISkills> cap = player.getCapability(SkillCapability.SKILL_CAP);
		ISkills skills = cap.orElse(null);

But now I can't gain xp at all. Is that the correct way of doing it?

Link to post
Share on other sites
11 hours ago, diesieben07 said:

What makes you think you are not gaining XP? How have you checked?

My GUI doesn't update the xp values.

 

This is how I reference the player in my GUI:

	private PlayerEntity player = Minecraft.getInstance().player;
	private LazyOptional<ISkills> cap = player.getCapability(SkillCapability.SKILL_CAP);
	private ISkills skills = cap.orElse(null);

Do I need to reference the Server Player instead? If so, how?

 

Or Is the client player already supposed to know my server player data from my events/packets? If so, why aren't they working? They're in the OP.

Link to post
Share on other sites

Is that this part?

	public static void register() {
		CapabilityManager.INSTANCE.register(ISkills.class, new SkillStorage(), () -> new Skills(null));
	}

 

If so, isn't that part supposed to be null? Every other capability I've seen from other people that had an entity/player there had it set to null.

Link to post
Share on other sites
12 minutes ago, LK1905 said:

If so, isn't that part supposed to be null?

No...? I mean, you never set the entity field anywhere else but then you use it to sync (or at least try to) here. Since you never set entity to anything but null that condition will never be true and you never call sync.

 

14 minutes ago, LK1905 said:

Every other capability I've seen from other people that had an entity/player there had it set to null.

Then they are either doing something differently than you or they are equally broken.

Link to post
Share on other sites
  • 2 weeks later...
  • LK1905 changed the title to [Solved][1.16.4] How do I correctly sync capability data between Client and server?

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
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.



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • Goals and Tasks are defined within the flyweight Entity itself whenever it is constructed. I would suggest taking a look at already implemented entities with goals and tasks if you want to implement them yourself.
    • Any mod present on the server (assuming it doesn't have the ignore extension point set) must be present on the client to allow both to connect.
    • Yes, you are passing in an Item instead of a supplier of an item. I would suggest taking another read over the docs.
    • Hi again, I'm trying to create a custom living entity but I keep running into this issue that causes a crash due to a ticking entity. Crash: [12:31:15] [Server thread/ERROR] [minecraft/MinecraftServer]: Encountered an unexpected exception net.minecraft.crash.ReportedException: Ticking entity at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:855) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.tickServer(MinecraftServer.java:787) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.integrated.IntegratedServer.tickServer(IntegratedServer.java:78) ~[forge:?] {re:classloading,pl:runtimedistcleaner:A} at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:642) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:232) ~[forge:?] {re:classloading,pl:accesstransformer:B} at java.lang.Thread.run(Thread.java:748) [?:1.8.0_282] {} Caused by: java.lang.NullPointerException at net.minecraft.entity.LivingEntity.travel(LivingEntity.java:1905) ~[forge:?] {re:classloading} at net.minecraft.entity.LivingEntity.aiStep(LivingEntity.java:2449) ~[forge:?] {re:classloading} at net.minecraft.entity.MobEntity.aiStep(MobEntity.java:488) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.entity.AgeableEntity.aiStep(AgeableEntity.java:115) ~[forge:?] {re:classloading} at net.minecraft.entity.passive.AnimalEntity.aiStep(AnimalEntity.java:51) ~[forge:?] {re:classloading} at net.minecraft.entity.LivingEntity.tick(LivingEntity.java:2158) ~[forge:?] {re:classloading} at net.minecraft.entity.MobEntity.tick(MobEntity.java:300) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.world.server.ServerWorld.tickNonPassenger(ServerWorld.java:611) ~[forge:?] {re:classloading} at net.minecraft.world.World.guardEntityTick(World.java:554) ~[forge:?] {re:classloading,pl:accesstransformer:B} at net.minecraft.world.server.ServerWorld.tick(ServerWorld.java:404) ~[forge:?] {re:classloading} at net.minecraft.server.MinecraftServer.tickChildren(MinecraftServer.java:851) ~[forge:?] {re:classloading,pl:accesstransformer:B} ... 5 more AL lib: (EE) alc_cleanup: 1 device not closed  It seems to be that I have not defined an ai/brain but I have no idea how to do this. I cant seem to see any event to do this in the library.  Any help would be greatly appreciated.
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.