Posted November 24, 20186 yr 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. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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?
November 24, 20186 yr Author 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 November 24, 20186 yr by unassigned Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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).
November 24, 20186 yr Author 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. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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?
November 24, 20186 yr Author 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 November 24, 20186 yr by unassigned elaboration Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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.
November 24, 20186 yr Author 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. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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.
November 24, 20186 yr Author 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. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 24, 20186 yr 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
November 24, 20186 yr Author 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. 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 November 24, 20186 yr by unassigned bugged formatting Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 25, 20186 yr Author 23 minutes ago, V0idWa1k3r said: Could you please update your github repository so I can debug this locally? Okay, I pushed some updated code. Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 25, 20186 yr 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.
November 25, 20186 yr Author 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? Currently developing: https://github.com/unassignedxd/Dynamic-Quarries
November 26, 20186 yr Author 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. 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.