Jump to content

Recommended Posts

Posted

Hello,

So I have a capability that stores a value within chunks that can be accessed across items/blocks within my mod. This value, however, I want to increase back to a default value if it has decreased at all. I want this value to increase very slowly, but I don't exactly know how I should do this as efficiently as possible to prevent lag. I'm already subscribed to some events: ChunkDataEvent.Save, ChunkDataEvent.Load, ChunkEvent.Load, ChunkEvent.Unload and ChunkWatchEvent. Here is the capability class that I'm using:

Spoiler

package unassigned.plentifulutilities.voidenergy.capability;

import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.world.ChunkDataEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.ChunkWatchEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import unassigned.plentifulutilities.PlentifulUtilities;
import unassigned.plentifulutilities.network.MessageUpdateVoidValue;
import unassigned.plentifulutilities.utils.CapabilityProviderSimple;
import unassigned.plentifulutilities.utils.ModUtil;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidHolder;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidHolderModifiable;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidStorage;

import javax.annotation.Nullable;

/**
 * This code is not owned by me! This is a test derived from Choonster's TestMod3. This code, however, has been modified to fit my current needs -
 * and will be re-written with the knowledge obtained from using Choonster's code as a base.
 *
 * - I am aware that all ChunkData classes will be deprecated and removed in 1.13 - but as stated about, this is a test to help with my
 * understanding of how chunks, capabilities and holder classes work.
 */
public class CapabilityVoidEnergy {

    @CapabilityInject(IVoidHolder.class)
    public static final Capability<IVoidHolder> CHUNK_VOID_ENERGY = null;

    public static final EnumFacing DEFAULT_FACING = null;

    public static final int DEFAULT_CAPACITY = 1000;

    private static final ResourceLocation ID = new ResourceLocation(ModUtil.MODID, "void_energy");

    public static void register() {
        CapabilityManager.INSTANCE.register(IVoidHolder.class, new Capability.IStorage<IVoidHolder>() {
            @Override
            public NBTBase writeNBT(final Capability<IVoidHolder> capability, final IVoidHolder instance, final EnumFacing side) {
                return new NBTTagCompound();
            }

            @Override
            public void readNBT(final Capability<IVoidHolder> capability, final IVoidHolder instance, final EnumFacing side, final NBTBase nbt) {

            }
        }, VoidEnergyHolder::new);
    }

    @Nullable
    public static IVoidHolder getVoidEnergyHolder(final World world){
        return getCapability(world, CHUNK_VOID_ENERGY, DEFAULT_FACING);
    }

    @Nullable
    public static <T> T getCapability(@Nullable ICapabilityProvider provider, Capability<T> capability, @Nullable EnumFacing facing) {
        return provider != null && provider.hasCapability(capability, facing) ? provider.getCapability(capability, facing) : null;
    }

    @Nullable
    public static IVoidStorage getVoidEnergy(final World world, final ChunkPos chunkPos){
        final IVoidHolder voidHolder = getVoidEnergyHolder(world);
        if(voidHolder == null) return null;

        return voidHolder.getVoidEnergy(chunkPos);
    }

    public static IVoidStorage getVoidEnergy(final Chunk chunk){ return getVoidEnergy(chunk.getWorld(), chunk.getPos()); }


    @Mod.EventBusSubscriber(modid = ModUtil.MODID)
    public static class VoidHandler {

        @SubscribeEvent
        public static void attachCapabilities(final AttachCapabilitiesEvent<World> event) {
            final IVoidHolder voidHolder = new VoidEnergyHolder();
            event.addCapability(ID, new CapabilityProviderSimple<>(voidHolder, CHUNK_VOID_ENERGY, DEFAULT_FACING));
        }

        @SubscribeEvent
        public static void chunkDataLoad(final ChunkDataEvent.Load event) {
            final World world = event.getWorld();
            final ChunkPos chunkPos = event.getChunk().getPos();

            final IVoidHolder voidHolder = getVoidEnergyHolder(world);
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            final VoidEnergy voidEnergy = new VoidEnergy(DEFAULT_CAPACITY, world, chunkPos);

            final NBTTagCompound chunkData = event.getData();
            if (chunkData.hasKey(ID.toString(), Constants.NBT.TAG_INT)) {
                final NBTTagInt energyTag = (NBTTagInt) chunkData.getTag(ID.toString());
                voidEnergy.deserializeNBT(energyTag);
            }

            ((IVoidHolderModifiable) voidHolder).setVoidEnergy(chunkPos, voidEnergy);
        }

        @SubscribeEvent
        public static void chunkLoad(final ChunkEvent.Load event) {
            final World world = event.getWorld();
            final ChunkPos chunkPos = event.getChunk().getPos();

            final IVoidHolder voidHolder = getVoidEnergyHolder(world);
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            if (voidHolder.getVoidEnergy(chunkPos) != null) return;

            final IVoidStorage voidStorage = new VoidEnergy(DEFAULT_CAPACITY, world, chunkPos);
            ((IVoidHolderModifiable) voidHolder).setVoidEnergy(chunkPos, voidStorage);
        }

        @SubscribeEvent
        public static void chunkDataSave(final ChunkDataEvent.Save event) {
            final IVoidStorage voidStorage = getVoidEnergy(event.getChunk());
            if (!(voidStorage instanceof VoidEnergy)) return;

            event.getData().setTag(ID.toString(), ((VoidEnergy) voidStorage).serializeNBT());
        }

        @SubscribeEvent
        public static void chunkUnload(final ChunkEvent.Unload event) {
            final IVoidHolder voidHolder = getVoidEnergyHolder(event.getWorld());
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            ((IVoidHolderModifiable) voidHolder).removeVoidEnergy(event.getChunk().getPos());
        }

        @SubscribeEvent
        public static void chunkWatch(final ChunkWatchEvent.Watch event) {
            final EntityPlayerMP player = event.getPlayer();
            final IVoidStorage voidStorage = getVoidEnergy(player.getEntityWorld(), event.getChunk());
            if (voidStorage == null) return;

            PlentifulUtilities.network.sendTo(new MessageUpdateVoidValue(voidStorage), player);
        }
    }
}

 

 

All other class references can be found on my github in the folder labeled 'voidenergy'. 

 

Thank you.

Posted

Use the WorldTickEvent and do whatever it is you need to do there. If you are concerned about performance - don't be, there aren't that many chunks loaded in the world at any given time to be concerned. You can optimize this further by putting the data that needs updating in a Stack or something(when the value changes below default, since this is when you want to do the restoration), then iterate over that stack and process the value in the data and either add it back(after you are done iterating of course) if it still needs updating or doing nothing.

Also I might be mistaken here but isn't a Chunk itself a ICapabilityProvider? As in couldn't you attach your capability directly to the chunk, rather than to the world?

Posted (edited)
21 minutes ago, V0idWa1k3r said:

Use the WorldTickEvent and do whatever it is you need to do there. If you are concerned about performance - don't be, there aren't that many chunks loaded in the world at any given time to be concerned. You can optimize this further by putting the data that needs updating in a Stack or something(when the value changes below default, since this is when you want to do the restoration), then iterate over that stack and process the value in the data and either add it back(after you are done iterating of course) if it still needs updating or doing nothing.

 

So I'd get the persistent chunks from the event.world (which I'm presuming gives all currently loaded chunks), get their capability that I've created and then check if its below said default value?

21 minutes ago, V0idWa1k3r said:

 Also I might be mistaken here but isn't a Chunk itself a ICapabilityProvider? As in couldn't you attach your capability directly to the chunk, rather than to the world?

 Would this require a drastic change in how I'm saving/loading the chunks' 'energy'? As stated, this is derived from an example that I'm trying to learn from. (Choonster). I am aware by me using ChunkDataEvents (which are soon to be deprecated) makes this code obsolete when transitioning to 1.13. So a small list of things that would have to be done in order to keep some kind of compatibility would be nice. As storing things using capabilities like this is still somewhat new to me.

 

Thanks.

Edited by unassigned
Posted
4 minutes ago, unassigned said:

So I'd get the persistent chunks from the event.world (which I'm presuming gives all currently loaded chunks), get their void capability and then check if its below said default value?

Don't you already have a set of all chunks currently in posession of your data? Your VoidEnergyHolder stores the data somehow, presumably in something like a Map<ChunkPos, IVoidStorage>? You can iterate all values of a map just fine. With my suggestion you don't even need to do that though.

 

6 minutes ago, unassigned said:

Would this require a drastic change in how I'm saving/loading the chunks' 'energy'?

Why yes it would. Instead of manually listening to the save/load events you would have to do... absolutely nothing, as the capability, as long as it implements INBTSerializable/ICapabilitySerializable would do everything for you.

All other code you have would remain unaffected(mostly, you would retreive the capability a bit differently but that's it).

Posted
23 minutes ago, V0idWa1k3r said:

Why yes it would. Instead of manually listening to the save/load events you would have to do... absolutely nothing, as the capability, as long as it implements INBTSerializable/ICapabilitySerializable would do everything for you.

All other code you have would remain unaffected(mostly, you would retreive the capability a bit differently but that's it).

Okay, I'll do this after I get this framework done. 

 

23 minutes ago, V0idWa1k3r said:

Don't you already have a set of all chunks currently in posession of your data? Your VoidEnergyHolder stores the data somehow, presumably in something like a Map<ChunkPos, IVoidStorage>? You can iterate all values of a map just fine. With my suggestion you don't even need to do that though.

Yeah, I can use this map and iterate through the values, however, how would I slow this down to a rate that I would like. In a TE I would normally use the ticks % some-val == 0 to slow, however, I don't see this applicable within the world as a whole.

Posted
6 minutes ago, unassigned said:

I don't see this applicable within the world as a whole.

Why? What stops you from having a ticksHappend variable per world, incrementing it and doing the same % operator on it as you would in a TE?

Posted (edited)
20 minutes ago, V0idWa1k3r said:

Why? What stops you from having a ticksHappend variable per world, incrementing it and doing the same % operator on it as you would in a TE?

Where would I be keeping this variable stored? Would it be with the capability, or could I do some kind of easier implementation? (ex. would I localize it within the TickEvent?)

 

As well, I've gone through the capability code and tried my best to port this to a serialized capability. All methods that are currently listed as deprecated are going to be removed, however, I am not sure if this is actually correct. Here is the code I have now:

Spoiler

package unassigned.plentifulutilities.voidenergy.capability;

import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.CapabilityInject;
import net.minecraftforge.common.capabilities.CapabilityManager;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.world.ChunkDataEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.ChunkWatchEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import unassigned.plentifulutilities.PlentifulUtilities;
import unassigned.plentifulutilities.network.MessageUpdateVoidValue;
import unassigned.plentifulutilities.utils.CapabilityProviderSerializable;
import unassigned.plentifulutilities.utils.CapabilityProviderSimple;
import unassigned.plentifulutilities.utils.ModUtil;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidHolder;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidHolderModifiable;
import unassigned.plentifulutilities.voidenergy.base.energy.IVoidStorage;

import javax.annotation.Nullable;
import java.util.Map;

/**
 * This code is not owned by me! This is a test derived from Choonster's TestMod3. This code, however, has been modified to fit my current needs -
 * and will be re-written with the knowledge obtained from using Choonster's code as a base.
 *
 * - I am aware that all ChunkData classes will be deprecated and removed in 1.13 - but as stated about, this is a test to help with my
 * understanding of how chunks, capabilities and holder classes work.
 */
public class CapabilityVoidEnergy {

    @CapabilityInject(IVoidHolder.class)
    public static final Capability<IVoidHolder> CHUNK_VOID_ENERGY = null;

    @CapabilityInject(IVoidStorage.class)
    public static final Capability<IVoidStorage> CHUNK_VOID_STORAGE = null;

    public static final EnumFacing DEFAULT_FACING = null;

    public static final int DEFAULT_CAPACITY = 1000;

    private static final ResourceLocation ID = new ResourceLocation(ModUtil.MODID, "void_energy");

    public static void register() {
        //todo remove
        CapabilityManager.INSTANCE.register(IVoidHolder.class, new Capability.IStorage<IVoidHolder>() {
            @Override
            public NBTBase writeNBT(final Capability<IVoidHolder> capability, final IVoidHolder instance, final EnumFacing side) {
                return new NBTTagCompound();
            }

            @Override
            public void readNBT(final Capability<IVoidHolder> capability, final IVoidHolder instance, final EnumFacing side, final NBTBase nbt) {

            }
        }, VoidEnergyHolder::new);
        //new chunk based capability
        CapabilityManager.INSTANCE.register(IVoidStorage.class, new Capability.IStorage<IVoidStorage>() {
            @Override
            public NBTBase writeNBT(final Capability<IVoidStorage> capability, final IVoidStorage instance, final EnumFacing side) {
                return new NBTTagInt(instance.getVoidStored());
            }

            @Override
            public void readNBT(final Capability<IVoidStorage> capability, final IVoidStorage instance, final EnumFacing side, final NBTBase nbt) {
                if (!(instance instanceof VoidEnergy))
                    throw new IllegalArgumentException("Given incorrect instance from implementation");

                ((VoidEnergy) instance).setVoidEnergy(((NBTTagInt) nbt).getInt());
            }
        }, () -> null);
    }

    @Deprecated
    @Nullable
    public static IVoidHolder getVoidEnergyHolder(final World world){
        return getCapability(world, CHUNK_VOID_ENERGY, DEFAULT_FACING);
    }

    @Nullable
    public static IVoidStorage getVoidEnergy(final Chunk chunk) {
        return getCapability(chunk, CHUNK_VOID_STORAGE, DEFAULT_FACING);
    }

    @Nullable
    public static <T> T getCapability(@Nullable ICapabilityProvider provider, Capability<T> capability, @Nullable EnumFacing facing) {
        return provider != null && provider.hasCapability(capability, facing) ? provider.getCapability(capability, facing) : null;
    }

    @Mod.EventBusSubscriber(modid = ModUtil.MODID)
    public static class VoidHandler {

        //todo: remove deprecations

        @Deprecated
        @SubscribeEvent
        public static void attachCapabilities(final AttachCapabilitiesEvent<World> event) {
            final IVoidHolder voidHolder = new VoidEnergyHolder();
            event.addCapability(ID, new CapabilityProviderSimple<>(voidHolder, CHUNK_VOID_ENERGY, DEFAULT_FACING));
        }

        @SubscribeEvent
        public static void attachChunkCapabiliies(final AttachCapabilitiesEvent<Chunk> event){
            final Chunk chunk = event.getObject();
            final IVoidStorage voidStorage = new VoidEnergy(DEFAULT_CAPACITY, chunk.getWorld(), chunk.getPos());
            event.addCapability(ID, new CapabilityProviderSerializable<>(CHUNK_VOID_STORAGE, DEFAULT_FACING, voidStorage));
        }

        @Deprecated
        @SubscribeEvent
        public static void chunkDataLoad(final ChunkDataEvent.Load event) {
            final World world = event.getWorld();
            final ChunkPos chunkPos = event.getChunk().getPos();

            final IVoidHolder voidHolder = getVoidEnergyHolder(world);
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            final VoidEnergy voidEnergy = new VoidEnergy(DEFAULT_CAPACITY, world, chunkPos);

            final NBTTagCompound chunkData = event.getData();
            if (chunkData.hasKey(ID.toString(), Constants.NBT.TAG_INT)) {
                final NBTTagInt energyTag = (NBTTagInt) chunkData.getTag(ID.toString());
                voidEnergy.deserializeNBT(energyTag);
            }

            ((IVoidHolderModifiable) voidHolder).setVoidEnergy(chunkPos, voidEnergy);
        }

        @Deprecated
        @SubscribeEvent
        public static void chunkLoad(final ChunkEvent.Load event) {
            final World world = event.getWorld();
            final ChunkPos chunkPos = event.getChunk().getPos();

            final IVoidHolder voidHolder = getVoidEnergyHolder(world);
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            if (voidHolder.getVoidEnergy(chunkPos) != null) return;

            final IVoidStorage voidStorage = new VoidEnergy(DEFAULT_CAPACITY, world, chunkPos);
            ((IVoidHolderModifiable) voidHolder).setVoidEnergy(chunkPos, voidStorage);
        }

        @Deprecated
        @SubscribeEvent
        public static void chunkDataSave(final ChunkDataEvent.Save event) {
            final IVoidStorage voidStorage = getVoidEnergy(event.getChunk());
            if (!(voidStorage instanceof VoidEnergy)) return;

            event.getData().setTag(ID.toString(), ((VoidEnergy) voidStorage).serializeNBT());
        }

        @Deprecated
        @SubscribeEvent
        public static void chunkUnload(final ChunkEvent.Unload event) {
            final IVoidHolder voidHolder = getVoidEnergyHolder(event.getWorld());
            if (!(voidHolder instanceof IVoidHolderModifiable)) return;

            ((IVoidHolderModifiable) voidHolder).removeVoidEnergy(event.getChunk().getPos());
        }

        @SubscribeEvent
        public static void chunkWatch(final ChunkWatchEvent.Watch event) {
            final EntityPlayerMP player = event.getPlayer();
            final Chunk chunk = event.getChunkInstance();
            if(chunk == null) return;

            final IVoidStorage voidStorage = getVoidEnergy(chunk);
            if(voidStorage == null) return;

            PlentifulUtilities.network.sendTo(new MessageUpdateVoidValue(voidStorage), player);
        }

        public static void onWorldTick(final TickEvent.WorldTickEvent event){
            World world = event.world;

            if(!world.isRemote)
            {
                //if(world.) slow down ticking rates
                for(IVoidStorage storage : VoidEnergyHolder.getVoidEnergies().values())
                {
                    if(storage.getVoidStored() < DEFAULT_CAPACITY)
                    {
                        storage.receiveVoid(world.rand.nextInt(3), false);
                    }
                }
            }
        }
    }
}

 

 

And I pushed another version to github if you are interested in other classes. Thanks for your help.

Edited by unassigned
elaboration
Posted
11 minutes ago, unassigned said:

Where would I be keeping this variable stored? Would it be with the capability, or could I do some kind of easier implementation? (ex. would I localize it within the TickEvent?)

Define "localize". You can store it either in your capability or in a map, that doesn't matter.

 

Since your capability is now stored per chunk you need to iterate over chunks in the world and get the capability instead of iterating over data in your world capability.

 

12 minutes ago, unassigned said:

however, I am not sure if this is actually correct.

Finish your code and provide any problems you might or might not have.

Posted
1 hour ago, V0idWa1k3r said:

Finish your code and provide any problems you might or might not have.

Okay, so this may or may not be the correct way of doing this, however, I attached an integer onto the capability for keeping track of 'ticks' it has been below the default value. This value is saved within its NBT and is process across the client and server. However, I ran into an issue with the for loop, as it never becomes active, presumably because I'm not correctly trying to access the chunks loaded. Maybe I do not understand the method for getting the currently loaded chunks. I'd like to note I cannot use that HashMap I was using before, as its basically removed from the capability due to moving to serialization. 

 

Spoiler

        @SubscribeEvent
        public static void onWorldTick(final TickEvent.WorldTickEvent event){
            World world = event.world;

            if(!world.isRemote)
            {
                System.out.println("outside for");
                for(Map.Entry<ChunkPos, ForgeChunkManager.Ticket> entry : world.getPersistentChunks().entries())
                {
                    System.out.println("within for");

                    Chunk chunkToCheck = world.getChunkFromChunkCoords(entry.getKey().x, entry.getKey().z);
                    final IVoidStorage voidStorage = CapabilityVoidEnergy.getVoidEnergy(chunkToCheck);
                    System.out.println(voidStorage);
                    if(voidStorage != null)
                    {

                        if(voidStorage.getVoidStored() < DEFAULT_CAPACITY)
                        {
                            voidStorage.setTicksElapsed(voidStorage.getTicksElapsed()+1); //test and mess code - only want this to tick while it needs to (ex. storage is below default)
                            if(voidStorage.getTicksElapsed() % 520 == 0)
                            {
                                System.out.println("tick!");
                                voidStorage.receiveVoid(world.rand.nextInt(10), false);
                            }
                        }
                    }
                    else { System.out.println("debug: skipped chunk as it had no void storage!"); /*todo remove debug*/ }
                }
            }
        }

 

 

And by 'it never becomes active': I mean that "outside for" is printed at the proper rate, while "within for" is never printed.

Again, I have pushed all code to the github if you'd like to see more. 

Posted

World#getPersistentChunks returns all chunks that are kept loaded by forge's chunk loading system, not all currently loaded chunks.

You could still keep a list of all loaded chunks by subscribing to the ChunkEvent.Load/Unload and keeping the chunk in question in a collection of some kind.

Posted
30 minutes ago, V0idWa1k3r said:

World#getPersistentChunks returns all chunks that are kept loaded by forge's chunk loading system, not all currently loaded chunks.

 You could still keep a list of all loaded chunks by subscribing to the ChunkEvent.Load/Unload and keeping the chunk in question in a collection of some kind.

Okay, this worked well I believe, here is the code for the chunk processing:

        @SubscribeEvent
        public static void chunkUnload(final ChunkEvent.Unload event) {
            ChunkPos chunkPos = event.getChunk().getPos();

            IVoidStorage voidStorage = getVoidEnergy(event.getChunk());
            if(voidStorage == null) return;

            VOID_CHUNKS_LOADED.remove(chunkPos);
        }

        @SubscribeEvent
        public static void chunkLoad(final ChunkEvent.Load event) {
            ChunkPos chunkPos = event.getChunk().getPos();

            IVoidStorage voidStorage = getVoidEnergy(event.getChunk());
            if(voidStorage == null) return;

            VOID_CHUNKS_LOADED.put(chunkPos, voidStorage);
        }

 

And here is my new TickEvent:

Spoiler

 @SubscribeEvent
        public static void onWorldTick(final TickEvent.WorldTickEvent event){
            World world = event.world;

            if(!world.isRemote)
            {
                for(Map.Entry<ChunkPos, IVoidStorage> entry : VOID_CHUNKS_LOADED.entrySet())
                {
                    IVoidStorage voidStorage = entry.getValue();

                    if(voidStorage.getVoidStored() < DEFAULT_CAPACITY)
                    {
                        System.out.println("running! | " + voidStorage.getTicksElapsed() + " | " + voidStorage.getVoidStored());
                        voidStorage.setTicksElapsed(voidStorage.getTicksElapsed()+1);
                        if(voidStorage.getTicksElapsed() % 60 == 0)
                        {
                            System.out.println("tick!");
                            voidStorage.receiveVoid(world.rand.nextInt(10)+ 1, false);
                        }
                    }
                }
            }
        }

 

 

 

 

Everything seems to be working fine, other than my values that are checked with the client do no match the values that get output by console here. So obviously, this leads me to think that I'm not sending a packet somewhere, however, I do not know where. I send one during ChunkWatchEvent and every time void related things (ticks/energy) is changed.

 

Here is the WatchEvent:

Spoiler

        @SubscribeEvent
        public static void chunkWatch(final ChunkWatchEvent.Watch event) {
            final EntityPlayerMP player = event.getPlayer();
            final Chunk chunk = event.getChunkInstance();
            if(chunk == null) return;

            final IVoidStorage voidStorage = getVoidEnergy(chunk);
            if(voidStorage == null) return;

            PlentifulUtilities.network.sendTo(new MessageUpdateVoidValue(voidStorage), player);
        }

 

 

 

and here is the VoidEnergy class which is the framework behind the whole void system.

Spoiler

public class VoidEnergy extends VoidStorage implements IVoidStorage, INBTSerializable<NBTTagCompound> {

    private final World world;

    private final ChunkPos chunkPos;

    public VoidEnergy(final int capacity, final World world, final ChunkPos chunkPos){
        super(capacity);
        this.world = world;
        this.chunkPos = chunkPos;

        energy = capacity;
    }

    @Override
    public NBTTagCompound serializeNBT() {
        NBTTagCompound tag = new NBTTagCompound();
        tag.setInteger("VoidStored", getVoidStored());
        tag.setInteger("TicksElapsed", getTicksElapsed());

        return tag;
    }

    @Override
    public void deserializeNBT(NBTTagCompound nbt) {
        energy = nbt.getInteger("VoidStored");
        ticks = nbt.getInteger("TicksElapsed");
    }

    @Override
    public World getWorld() {
        return world;
    }

    @Override
    public ChunkPos getChunkPos() {
        return chunkPos;
    }

    @Override
    public int receiveVoid(int maxReceive, boolean simulate) {
        final int voidReceived = super.receiveVoid(maxReceive, simulate);

        if(!simulate && voidReceived != 0) onVoidChanged();

        return voidReceived;
    }

    @Override
    public int extractVoid(int maxExtract, boolean simulate) {
        final int voidExtracted = super.extractVoid(maxExtract, simulate);

        if (!simulate && voidExtracted != 0) onVoidChanged();

        return voidExtracted;
    }

    public void setVoidEnergy(final int energy){
        this.energy = energy;
        onVoidChanged();
    }

    @Override
    public void setTicksElapsed(final int ticks){
        this.ticks = ticks;
        onVoidChanged();
    }

    /**
     *  This section of the code was derived from Choonster. This code is not to be published publicly.
     *
     * **/
    protected void onVoidChanged() {
        final World world = getWorld();
        final ChunkPos chunkPos = getChunkPos();
        if(world.isRemote) return;

        final BlockPos chunkOrigin = chunkPos.getBlock(0,0,0);
        if(world.isBlockLoaded(chunkOrigin)) { world.getChunkFromChunkCoords(chunkPos.x, chunkPos.z).markDirty(); }

        final PlayerChunkMapEntry playerchunkMapEntry = ((WorldServer) world).getPlayerChunkMap().getEntry(chunkPos.x, chunkPos.z);
        if(playerchunkMapEntry == null) return;

        final IMessage message = new MessageUpdateVoidValue(this);
        PlentifulUtilities.network.sendToAllTracking(message, new NetworkRegistry.TargetPoint(world.provider.getDimension(), chunkOrigin.getX(), chunkOrigin.getY(), chunkOrigin.getZ(), 0));
    }

}

 

 

Thanks for your help.

Posted
5 minutes ago, unassigned said:

if(voidStorage == null) return;

This should never be the case since you are adding the capability to each and every chunk. And if this is the case - something is seriously messed up and you should just crash the game.

 

7 minutes ago, unassigned said:

my values that are checked with the client do no match the values that get output by console here. So obviously, this leads me to think that I'm not sending a packet somewhere, however, I do not know where. I send one during ChunkWatchEvent and every time void related things (ticks/energy) is changed.

Use the debugger to see 

  • When the update packet is sent to the client, if it is sent at all
  • What are the contents of said packet
  • What data arrives at the client
  • What the client does with that data
Posted (edited)
9 hours ago, V0idWa1k3r said:

Use the debugger to see 

  • When the update packet is sent to the client, if it is sent at all
  •  What are the contents of said packet
  • What data arrives at the client
  • What the client does with that data
2

I've been testing for about an hour now, and cannot seem to put a finger on what is happening. I've added a network sync line in the TickEvent when the amount of energy stored is updated.

                        ((VoidEnergy) voidStorage).receiveVoid((world.rand.nextInt(10)+ 1), false);
                        PlentifulUtilities.network.sendToAll(new MessageUpdateVoidValue(voidStorage));

And within my network class, I've added debugs to tell me what messages are being sent. Here is what the debugger shows when the threshold of energy drops below the default value:

[11:47:57] [Server thread/INFO] [STDOUT]: [unassigned.plentifulutilities.voidenergy.capability.VoidEnergy:onVoidChanged:114]: sending packet!
[11:47:58] [main/INFO] [STDOUT]: [unassigned.plentifulutilities.network.MessageUpdateVoidValue$Handler:lambda$onMessage$0:82]: Pack received and updating! SV: 905 | ticks Sent: 0
[11:47:58] [main/INFO] [STDOUT]: [unassigned.plentifulutilities.network.MessageUpdateVoidValue$Handler:lambda$onMessage$0:82]: Pack received and updating! SV: 980 | ticks Sent: 0
[11:47:58] [main/INFO] [STDOUT]: [unassigned.plentifulutilities.network.MessageUpdateVoidValue$Handler:lambda$onMessage$0:82]: Pack received and updating! SV: 988 | ticks Sent: 0
[11:47:58] [main/INFO] [STDOUT]: [unassigned.plentifulutilities.network.MessageUpdateVoidValue$Handler:lambda$onMessage$0:82]: Pack received and updating! SV: 992 | ticks Sent: 0
[11:47:58] [main/INFO] [STDOUT]: [unassigned.plentifulutilities.network.MessageUpdateVoidValue$Handler:lambda$onMessage$0:82]: Pack received and updating! SV: 1002 | ticks Sent: 0

(do not worry about the ticks sent, I disabled that functionality for now as I try to fix this)

As you can see, it properly gets itself back to above 1000, however, the Item that checks this will still display the first value of 905.

33271eda5db312f1b75e01899d0d0a83.png

What is even more interesting is that the Item I have to decrease this value WILL sync up with the client value that is displayed.

Here are the two items in question:

the 'setter' item:

Spoiler

    private void addRemoveVoidEnergy(World world, EntityPlayer player, int amt){
        final Chunk chunk = world.getChunkFromBlockCoords(new BlockPos(player));
        final ChunkPos chunkPos = chunk.getPos();
        final IVoidStorage voidStorage = CapabilityVoidEnergy.getVoidEnergy(chunk);

        if(voidStorage != null)
        {
            if(player.isSneaking())
            {
                voidStorage.extractVoid(amt, false);
            }else
            {
                voidStorage.receiveVoid(amt, false);
            }
        }
    }

    @Override
    public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn) {
        if(!worldIn.isRemote)
        {
            addRemoveVoidEnergy(worldIn, playerIn, 50);
        }

        return super.onItemRightClick(worldIn, playerIn, handIn);
    }

 

 

The 'getter' item:

Spoiler

    @Override
    public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn) {
        if(!worldIn.isRemote){
            final Chunk chunk = worldIn.getChunkFromBlockCoords(new BlockPos(playerIn));
            final ChunkPos chunkPos = chunk.getPos();
            final IVoidStorage voidStorage = CapabilityVoidEnergy.getVoidEnergy(chunk);

            if(voidStorage != null){
                //playerIn.sendMessage(new TextComponentTranslation("message." + ModUtil.MODID + ":void_energy.get", chunkPos, voidStorage.getVoidStored()));
                playerIn.sendMessage(new TextComponentString("The current area has " + voidStorage.getVoidStored() + " / " + voidStorage.getMaxVoidStored() + " SV! [ " + chunkPos.x + ", " + chunkPos.z + "]"));
            }else {
                playerIn.sendMessage(new TextComponentTranslation("message." + ModUtil.MODID + ":void_energy.not_found", chunkPos));
            }
        }

        return new ActionResult<>(EnumActionResult.SUCCESS, playerIn.getHeldItem(handIn));
    }

 

 

What it seems is that the items seem to sync up fine, however, when I try to change the value within the TickEvent, it never syncs, even though it clearly says packets are being sent to the client with the correct data. I pushed another version, if you'd like to see where networking, view the 'network' folder, and for all the void energy related things, view the 'voidenergy' folder.

 

Thank you for your help.

Edited by unassigned
bugged formatting
Posted

Your issue is the fact that you were operating with different data all this time. You were iterating over the entries in the map, but needed to iterate over actual data attached to the chunks. Because the data was different(as in the VoidEnergy instances were different) there were all kinds of issues. 

Don't store the data in the map, in fact ditch the map alltogether. Have a list, or an array, or something containing all ChunkPositions of loaded chunks. Also store this in the world capability since there are multiple world instances(dimensions). Your packet will also need a dimension ID. Iterate over those positions and get the actual data attached to the chunk. Then do whatever it is you need to do with it.

Also probably don't send it each tick.

Posted
12 hours ago, V0idWa1k3r said:

Don't store the data in the map, in fact ditch the map alltogether. Have a list, or an array, or something containing all ChunkPositions of loaded chunks. Also store this in the world capability since there are multiple world instances(dimensions).

So I'd create another capability with a storage class similar to what I was doing with IVoidHolder? One that just holds that list of ChunkPos and stores to the world? Would I still populate this list using the onChunkLoad / onChunkUnload events?

 

Posted

Okay, I have fixed up my code and it is now working fine. I ended up reusing my IVoidHolder and IVoidHolderModifiable so that the list can still be stored and the chunks be accessed under one instance. As well, I used sendToAllTracking to include the dimension it is in when sending the packet. All of these combined allowed me to access the chunks and modify them as I wish.

 

Thank you for your help.

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.

Announcements



×
×
  • Create New...

Important Information

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