Jump to content

[1.12.2] Capability Values Not Properly Syncing


unassigned

Recommended Posts

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.

  • Thanks 1
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.