Posted February 28, 20178 yr I have a network packet that syncs all the player data from a custom capability from server to client when the player logs in. On a server this works properly every time. In SinglePlayer the data syncs inconsistently (Sometimes it syncs, sometimes it doesn't). @SubscribeEvent public void onPlayerLogin(PlayerLoggedInEvent event) { EntityPlayer player = event.player; IPlayerData playerData = PlayerDataProvider.getPlayerCapability(player); MagicMastery.network.sendTo(new MessageSyncAllPlayerData(playerData), (EntityPlayerMP) player); } The weird thing is, when I put a breakpoint on this line: MagicMastery.network.sendTo(new MessageSyncAllPlayerData(playerData), (EntityPlayerMP) player); The game halts because of the breakpoint of course, so I click resume, and it syncs properly every time. It seems the CombinedClient needs a second, or even half a second, to properly send/load/or whatever for this packet to work properly. I think this because it works on the server every time, and it takes the server some time to send the data to the player about the world already, so this gives my message a chance to work as well. Also when I put the breakpoint, the game pauses when it gets to the breakpoint until I click resume, giving the message time to work properly. I need this to sync properly on login because I have a levitation spell that allows the player to float around, and if they are floating when they log off they need to still be floating when they log back in so they don't fall. I could get around this by syncing the isLevitating data on player tick, but the other data won't be synced properly still. I could then sync all the other player data every 5 seconds or so but this seems unnecessary. I should really only sync the data when the player logs in and then whenever anything is changed. Is there an event that takes place about a second or more after PlayerLoggedInEvent that doesn't happen too often where I can send the packet to sync all the data. Ugh, it's so weird this works perfectly on a server but not in SinglePlayer. It's usually the other way around lol. UPDATE: I seem to have fixed the issue by doing this: @SubscribeEvent public void onPlayerLogin(PlayerLoggedInEvent event) throws InterruptedException { if (MagicMastery.proxy instanceof CombinedClientProxy) { Thread.sleep(200); } EntityPlayer player = event.player; IPlayerData playerData = PlayerDataProvider.getPlayerCapability(player); MagicMastery.network.sendTo(new MessageSyncAllPlayerData(playerData), (EntityPlayerMP) player); } SOLVED: @Override public IMessage onMessage(MessageSyncAllPlayerData message, MessageContext ctx) { //final EntityPlayer player = MagicMastery.proxy.getEntityPlayer(ctx); //final IPlayerData serverData = message.getData(); MagicMastery.proxy.getThreadListener(ctx).addScheduledTask(() -> { // The finals need to go in here final EntityPlayer player = MagicMastery.proxy.getEntityPlayer(ctx); final IPlayerData serverData = message.getData(); if (player != null && serverData != null) { Edited March 1, 20178 yr by Kriptikz Solved
February 28, 20178 yr Post your packet and handler classes. Do you use Minecraft#addScheduledTask to process the message?
February 28, 20178 yr Author 19 minutes ago, Jay Avery said: Post your packet and handler classes. Do you use Minecraft#addScheduledTask to process the message? yes, I do use Minecraft#addScheduledTask public class MessageSyncAllPlayerData implements IMessage { private IPlayerData data = new PlayerData(); public MessageSyncAllPlayerData() { } public MessageSyncAllPlayerData(IPlayerData playerData) { this.data = playerData; } @Override public void fromBytes(ByteBuf buf) { if (this.data != null) { this.data.setIsLevitating(buf.readBoolean()); this.data.setMana(buf.readFloat()); this.data.setMaxMana(buf.readFloat()); this.data.setPlayerLevel(buf.readInt()); this.data.setPlayerXp(buf.readInt()); this.data.setPlayerCurrentLevelMaxXp(buf.readInt()); this.data.setShouldPlayerGainXp(buf.readBoolean()); this.data.setManaRegenPerSecond(buf.readFloat()); this.data.setSelectedSpell(buf.readInt()); for (int i = 0; i < IPlayerData.NUMBER_OF_SPELLS; i++) { readSpellArray(buf, i); } } } @Override public void toBytes(ByteBuf buf) { if (this.data != null) { buf.writeBoolean(this.data.getIsLevitating()); buf.writeFloat(this.data.getMana()); buf.writeFloat(this.data.getMaxMana()); buf.writeInt(this.data.getPlayerLevel()); buf.writeInt(this.data.getPlayerXp()); buf.writeInt(this.data.getPlayerCurrentLevelMaxXp()); buf.writeBoolean(this.data.getShouldPlayerGainXp()); buf.writeFloat(this.data.getManaRegenPerSecond()); buf.writeInt(this.data.getSelectedSpell()); for (int i = 0; i < IPlayerData.NUMBER_OF_SPELLS; i++) { writeSpellArray(buf, this.data.getSpellFromId(i)); } } } public void setData(IPlayerData data) { this.data = data; } public IPlayerData getData() { return this.data; } public void writeSpellArray(ByteBuf buf, int[] array) { for (int i = 0; i < 4; i++) { buf.writeInt(array[i]); } } public void readSpellArray(ByteBuf buf,int spellId) { int [] array = new int[4]; for (int i = 0; i < 4; i++) { array[i] = buf.readInt(); } this.data.setSpellFromId(spellId, array); } public static class MessageHandler implements IMessageHandler<MessageSyncAllPlayerData, IMessage> { @Override public IMessage onMessage(MessageSyncAllPlayerData message, MessageContext ctx) { final EntityPlayer player = MagicMastery.proxy.getEntityPlayer(ctx); final IPlayerData serverData = message.getData(); MagicMastery.proxy.getThreadListener(ctx).addScheduledTask(() -> { if (player != null && serverData != null) { IPlayerData clientData = PlayerDataProvider.getPlayerCapability(player); if (clientData != null && serverData != null) { clientData.setIsLevitating(serverData.getIsLevitating()); clientData.setMana(serverData.getMana()); clientData.setMaxMana(serverData.getMaxMana()); clientData.setPlayerLevel(serverData.getPlayerLevel()); clientData.setPlayerXp(serverData.getPlayerXp()); clientData.setPlayerCurrentLevelMaxXp(serverData.getPlayerCurrentLevelMaxXp()); clientData.setShouldPlayerGainXp(serverData.getShouldPlayerGainXp()); clientData.setManaRegenPerSecond(serverData.getManaRegenPerSecond()); clientData.setSelectedSpell(serverData.getSelectedSpell()); for (int i = 0; i < IPlayerData.NUMBER_OF_SPELLS; i++) { clientData.setSpellFromId(i, serverData.getSpellFromId(i)); } } } }); return null; } } }
February 28, 20178 yr Author 7 hours ago, diesieben07 said: data cannot possibly contain a value inside fromBytes. fromBytes is called after your message class has been constructed using the no-argument constructor, you must fill the fields in this method. They all have default values set in my PlayerData class, shouldn't this line create the object with all the default values: private IPlayerData data = new PlayerData(); Here is some of my PlayerData: public class PlayerData implements IPlayerData { private int ticksStunned; private boolean isStunned = false; private int stunDuration = 20; private boolean isLevitating = false; // levitation amp of 5 is hovering, <5 goes down, >5 goes up. private int levitationAmp = 5; private float mana = 0; private float maxMana = 150; private int playerLevel = 1; private int playerXp = 0; private int playerCurrentLevelMaxXp = 100; private boolean shouldPlayerGainXp = true; private float manaRegenPerSecond = 0.5F; private int selectedSpell = 0; Also, I got rid of the null check and the inconsistent sync was still a problem without: if (MagicMastery.proxy instanceof CombinedClientProxy) { Thread.sleep(200); } Is using Thread.sleep here ok? It should only do that in SinglePlayer as well.
February 28, 20178 yr Author 22 minutes ago, diesieben07 said: No, Thread.sleep is a terrible idea to solve a thread inconsistency. Where did you put that code? @SubscribeEvent public void onPlayerLogin(PlayerLoggedInEvent event) throws InterruptedException { if (MagicMastery.proxy instanceof CombinedClientProxy) { Thread.sleep(200); } EntityPlayer player = event.player; IPlayerData playerData = PlayerDataProvider.getPlayerCapability(player); MagicMastery.network.sendTo(new MessageSyncAllPlayerData(playerData), (EntityPlayerMP) player); }
March 1, 20178 yr Author 26 minutes ago, diesieben07 said: Yeah, that's a terrible idea, don't do that. Feelsbadman What can go wrong with it? This is only a problem with SinglePlayer, when connecting to a dedicated server it works fine. There just needs to be at least a 200 millisecond delay when in SinglePlayer in order for the message to work and the data to sync. Is there a proper way to make the game wait at least 200 ms, only when in SinglePlayer? Edited March 1, 20178 yr by Kriptikz
March 1, 20178 yr Author 6 minutes ago, diesieben07 said: The proper way is to figure out why that 200ms delay fixes it and then fix that bug. Ok, I'm going to create a testmod where I create a very simple capability and a message to sync the data to client and see if this is still an issue.
March 1, 20178 yr Author Solved, I had to move the finals into addScheduledTask This: @Override public IMessage onMessage(MessageSyncAllPlayerData message, MessageContext ctx) { final EntityPlayer player = MagicMastery.proxy.getEntityPlayer(ctx); final IPlayerData serverData = message.getData(); MagicMastery.proxy.getThreadListener(ctx).addScheduledTask(() -> { if (player != null && serverData != null) { IPlayerData clientData = PlayerDataProvider.getPlayerCapability(player); if (clientData != null && serverData != null) { clientData.setIsLevitating(serverData.getIsLevitating()); clientData.setMana(serverData.getMana()); clientData.setMaxMana(serverData.getMaxMana()); clientData.setPlayerLevel(serverData.getPlayerLevel()); clientData.setPlayerXp(serverData.getPlayerXp()); clientData.setPlayerCurrentLevelMaxXp(serverData.getPlayerCurrentLevelMaxXp()); clientData.setShouldPlayerGainXp(serverData.getShouldPlayerGainXp()); clientData.setManaRegenPerSecond(serverData.getManaRegenPerSecond()); clientData.setSelectedSpell(serverData.getSelectedSpell()); for (int i = 0; i < IPlayerData.NUMBER_OF_SPELLS; i++) { clientData.setSpellFromId(i, serverData.getSpellFromId(i)); } } } }); re needed to be like this: @Override public IMessage onMessage(MessageSyncAllPlayerData message, MessageContext ctx) { MagicMastery.proxy.getThreadListener(ctx).addScheduledTask(() -> { final EntityPlayer player = MagicMastery.proxy.getEntityPlayer(ctx); final IPlayerData serverData = message.getData(); if (player != null && serverData != null) { IPlayerData clientData = PlayerDataProvider.getPlayerCapability(player); if (clientData != null && serverData != null) {
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.