Posted February 9, 20196 yr Hello, I have a storage class attached to the chunks in the world, all values do work and sync correctly, except one boolean, which always for whatever reason seems to default to false, even when constructively set to true. I send a packet to sync all the data from the server to the client, however, as stated, this value always defaults to false. The client actually handles this value well (as in set to true when I want it too), but the way I'm accessing it must be done through the server side, so I need these values to be synced. I just cannot understand why the server is insisting on this value being false when there is no way for it to be set to false (other than the default value of a boolean). I've spent hours trying to debug and see where things are going wrong: during packet sending, in the event itself, etc. One possible pointer is when I call this function through both client and server, it does work, as the client value is what is true which executes the task I want. Here is where I'm setting this boolean: Spoiler @SubscribeEvent public static void onCapabilityAttachToChunk(AttachCapabilitiesEvent<Chunk> event) { Chunk chunk = event.getObject(); if(chunk != null) { EnumVoidTypes type = CapabilityUtil.getRandedVoidType(chunk.getWorld().rand); IVoidChunk voidChunk = new VoidChunk(chunk, type, type.hasPossibleNaturalNode, 5000, 10000); event.addCapability(CapabilityVoidChunk.ID, new CapabilityProviderSerializable<>(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, voidChunk, CapabilityVoidChunk.DEFAULT_FACING)); } } type.hasPossibleNaturalNode does properly set this variable, it can be replaced with true and the same issue still exists. [all of these classes are viewable in my github (see signature), as well as the gradlew scripts for decompilation] Here is the VoidStorage class, which is stored to the chunks [located in capability/voidchunk/VoidStorage] [interface can be found in api/capablity/voidchunk/IVoidChunk]: Spoiler public class VoidChunk implements IVoidChunk { protected final Chunk chunk; protected EnumVoidTypes voidType; protected int voidEnergy; protected int maxVoidEnergy; protected boolean hasNaturalNode; protected BlockPos nodePos; private boolean shouldSendData = false; public VoidChunk(Chunk chunk, EnumVoidTypes voidType, boolean hasNaturalNode, int voidEnergy, int maxVoidEnergy) { this.chunk = chunk; this.voidType = voidType; this.voidEnergy = voidEnergy; this.maxVoidEnergy = maxVoidEnergy; this.hasNaturalNode = hasNaturalNode; } @Override public void onUpdate() { if(this.shouldSendData){ NetworkManager.sendToAllLoaded(this.chunk.getWorld(), new BlockPos(chunk.x << 4, 0, chunk.z << 4), new PacketVoidChunk(this.getAttachedChunk(), this.getHasNaturalNode(), this.getVoidStored(), this.getVoidType().getId())); this.shouldSendData = false; } } @Override public void onChunkLoad(ChunkEvent.Load event) { if(this.hasNaturalNode && this.nodePos == null) { this.nodePos = new BlockPos(this.chunk.x << 4, this.chunk.getHeightValue(8, 8), this.chunk.z << 4); if(this.chunk.getWorld().getBlockState(this.nodePos) != ModBlocks.VOID_NODE.getDefaultState()) { this.chunk.getWorld().setBlockState(this.nodePos, ModBlocks.VOID_NODE.getDefaultState()); VoidUtils.logger.info("Spawned Void Node @ " + nodePos.toString()); } } } @Override public void onChunkUnload(ChunkEvent.Unload event) { } @Override public Chunk getAttachedChunk() { return this.chunk; } @Override public EnumVoidTypes getVoidType() { return this.voidType; } @Override public void setVoidType(EnumVoidTypes type) { this.voidType = type; } @Override public boolean getHasNaturalNode() { return this.hasNaturalNode; } @Override public void setHasNaturalNode(boolean set) { this.hasNaturalNode = set; } @Override public BlockPos getNodePosition() { return this.nodePos; } @Override public void setNodePosition(BlockPos nodePosition) { this.nodePos = nodePosition; } @Override public int getVoidStored() { return this.voidEnergy; } @Override public void setVoidStored(int set) { this.voidEnergy = set; } @Override public int getMaxVoidStored() { return this.maxVoidEnergy; } @Override public int extractVoidEnergy(int amount, boolean simulate) { int ext = Math.min(this.voidEnergy, amount); if (!simulate) { this.voidEnergy -= ext; this.shouldSendData = true; } return ext; } @Override public int receiveVoidEnergy(int amount, boolean simulate) { int rec = Math.min(this.maxVoidEnergy - this.voidEnergy, amount); if (!simulate) { this.voidEnergy += rec; this.shouldSendData = true; } return rec; } } Here is the Capability Class [CapabilityVoidChunk located in capability/voidchunk/CapabilityVoidChunk]: Spoiler public class CapabilityVoidChunk { @CapabilityInject(IVoidChunk.class) public static final Capability<IVoidChunk> CAPABILITY_VOID_CHUNK = null; public static final EnumFacing DEFAULT_FACING = null; public static final ResourceLocation ID = new ResourceLocation(VoidUtils.MOD_ID, "capability_voidchunk"); public static void registerCap() { CapabilityManager.INSTANCE.register(IVoidChunk.class, new Capability.IStorage<IVoidChunk>() { @Nullable @Override public NBTBase writeNBT(Capability<IVoidChunk> capability, IVoidChunk voidChunk, EnumFacing enumFacing) { NBTTagCompound compound = new NBTTagCompound(); if(voidChunk != null) { compound.setInteger("VoidEnergy", voidChunk.getVoidStored()); compound.setInteger("VoidTypeID", voidChunk.getVoidType().getId()); compound.setBoolean("HasNaturalNode", voidChunk.getHasNaturalNode()); if(voidChunk.getHasNaturalNode()) compound.setLong("NodePos", voidChunk.getNodePosition().toLong()); } return compound; } @Override public void readNBT(Capability<IVoidChunk> capability, IVoidChunk voidChunk, EnumFacing enumFacing, NBTBase nbtBase) { if(nbtBase != null) { if(nbtBase instanceof NBTTagCompound){ NBTTagCompound compound = (NBTTagCompound)nbtBase; voidChunk.setVoidStored(compound.getInteger("VoidEnergy")); voidChunk.setVoidType(EnumVoidTypes.getVoidTypeByID(compound.getInteger("VoidTypeID"))); voidChunk.setHasNaturalNode(compound.getBoolean("HasNaturalNode")); if(voidChunk.getHasNaturalNode() && compound.hasKey("NodePos")) { voidChunk.setNodePosition(BlockPos.fromLong(compound.getLong("NodePos"))); } } } } }, () -> null); } public static IVoidChunk getVoidChunk(Chunk chunk){ return CapabilityUtil.getCapability(chunk, CAPABILITY_VOID_CHUNK, DEFAULT_FACING); } } And here is the event class [CommonEvents, located in events/CommonEvents]: Spoiler @Mod.EventBusSubscriber(modid = VoidUtils.MOD_ID) public class CommonEvents { @SubscribeEvent public static void onCapabilityAttachToChunk(AttachCapabilitiesEvent<Chunk> event) { Chunk chunk = event.getObject(); if(chunk != null) { EnumVoidTypes type = CapabilityUtil.getRandedVoidType(chunk.getWorld().rand); IVoidChunk voidChunk = new VoidChunk(chunk, type, type.hasPossibleNaturalNode, 5000, 10000); event.addCapability(CapabilityVoidChunk.ID, new CapabilityProviderSerializable<>(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, voidChunk, CapabilityVoidChunk.DEFAULT_FACING)); } } @SubscribeEvent public static void onChunkLoad(ChunkEvent.Load event) { World world = event.getWorld(); Chunk chunk = event.getChunk(); if(world != null && chunk != null) { if(!world.isRemote) { if(chunk.hasCapability(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, null)){ IVoidChunk voidChunk = CapabilityVoidChunk.getVoidChunk(chunk); if(voidChunk != null) voidChunk.onChunkLoad(event); } } } } @SubscribeEvent public static void onChunkUnload(ChunkEvent.Unload event) { World world = event.getWorld(); Chunk chunk = event.getChunk(); if(world != null && chunk != null) { if(!world.isRemote) { if(chunk.hasCapability(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, null)){ IVoidChunk voidChunk = CapabilityVoidChunk.getVoidChunk(chunk); if(voidChunk != null) voidChunk.onChunkUnload(event); } } } } @SubscribeEvent public static void onChunkWatch(ChunkWatchEvent event) { EntityPlayer player = event.getPlayer(); Chunk chunk = event.getChunkInstance(); if(player != null && chunk != null) { if(chunk.hasCapability(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, null)){ IVoidChunk voidChunk = CapabilityVoidChunk.getVoidChunk(chunk); if(voidChunk != null) { NetworkManager.sendToPlayer(player, new PacketVoidChunk(voidChunk.getAttachedChunk(), voidChunk.getHasNaturalNode(), voidChunk.getVoidStored(), voidChunk.getVoidType().getId())); } } } } @SubscribeEvent public static void onWorldTick(TickEvent.WorldTickEvent event) { if(!event.world.isRemote && event.phase == TickEvent.Phase.END){ if(event.world.getTotalWorldTime() % 20 == 0) { //update once a second. event.world.profiler.startSection(VoidUtils.MOD_ID + ":onWorldTick"); Iterator<Chunk> loadedChunks = event.world.getPersistentChunkIterable(((WorldServer) event.world).getPlayerChunkMap().getChunkIterator()); while(loadedChunks.hasNext()){ Chunk chunk = loadedChunks.next(); if(chunk.hasCapability(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, null)) { IVoidChunk voidChunk = CapabilityVoidChunk.getVoidChunk(chunk); voidChunk.onUpdate(); } } event.world.profiler.endSection(); } } } } Lastly the packet class [PacketVoidChunk located in network/packets/PacketVoidChunk]: Spoiler public class PacketVoidChunk implements IMessage { public PacketVoidChunk() {} public ChunkPos chunkPos; public boolean hasNaturalNode; public int voidEnergy; public int voidTypeID; public PacketVoidChunk(Chunk chunk, boolean hasNaturalNode, int voidEnergyStored, int voidTypeID) { this.chunkPos = chunk.getPos(); this.hasNaturalNode = hasNaturalNode; this.voidEnergy = voidEnergyStored; this.voidTypeID = voidTypeID; } @Override public void fromBytes(ByteBuf buf) { int chunkX = buf.readInt(); int chunkZ = buf.readInt(); this.chunkPos = new ChunkPos(chunkX, chunkZ); this.hasNaturalNode = buf.readBoolean(); this.voidEnergy = buf.readInt(); this.voidTypeID = buf.readInt(); } @Override public void toBytes(ByteBuf buf) { buf.writeInt(chunkPos.x); buf.writeInt(chunkPos.z); buf.writeBoolean(hasNaturalNode); buf.writeInt(voidEnergy); buf.writeInt(voidTypeID); } public static class Handler implements IMessageHandler<PacketVoidChunk, IMessage> { @Override @SideOnly(Side.CLIENT) public IMessage onMessage(PacketVoidChunk packet, MessageContext messageContext) { VoidUtils.proxy.scheduleSidedTask(() -> { World world = Minecraft.getMinecraft().world; if(world != null) { Chunk chunk = world.getChunk(packet.chunkPos.x, packet.chunkPos.z); if(chunk.hasCapability(CapabilityVoidChunk.CAPABILITY_VOID_CHUNK, null)){ IVoidChunk voidChunk = CapabilityVoidChunk.getVoidChunk(chunk); voidChunk.setVoidStored(packet.voidEnergy); voidChunk.setVoidType(EnumVoidTypes.getVoidTypeByID(packet.voidTypeID)); voidChunk.setHasNaturalNode(packet.hasNaturalNode); } } }); return null; } } } Here are a few more pointers: - I know the issue is some kind of desync between the server and client during the creation of the Capability, I just can't figure out what. - When I go to send the packet, I'm often left with results that look like: Sending hasNaturalNodePacket, currently set before packet: true \ setting to(packet is carrying): false - Without sending the packet, I'm usually left with: Server Side Response: hasNaturalNode: false \ Client Side Response: hasNaturalNode: true - This value is used in VoidChunk#onChunkLoad, where I set a block at specific block coordinates, depending on hasNaturalNode. - As all values are being synced between the server TO the client (ex. the integer voidValue, or the integer voidTypeID), why would this one act like this need to be synced from the client TO the server? Thank you and sorry for the spew of text. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
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.