
Astro2202
Members-
Posts
18 -
Joined
-
Last visited
Recent Profile Visitors
8666 profile views
Astro2202's Achievements

Tree Puncher (2/8)
0
Reputation
-
I don't think I made it work yet but I'm on it. I removed both EventHandler and WorldDecayData. It was some unused leftover code from previous attempts. Fixed! I put the worldTickEvent in the EventHandler class and made it call a new method onTick() in DefaultDecay. I think this should fix that issue. I implemented the iterator instead and you are right this is much much better. Can you further elaborate on this? This is the part of the capability which I'm not fully sure of how it works. And both these are no longer an issue. I am pleased to say that all functions well now! I'm no longer experiencing any major bugs. Thank you very much for this useful advice. I know I made some real basic mistakes here but now I know better and I will be a better programmer/modder because of it. So once again I'm very grateful, thank you! All the changes I made so far are pushed on to GitHub.
-
So as a temporary solution I added a check to see if the dimension is the overworld and will only swap the block on the end phase: @SubscribeEvent public void onWorldTick(final TickEvent.WorldTickEvent event){ if(event.phase.equals(TickEvent.Phase.END)){ if(event.world.dimension().equals(World.OVERWORLD)){ swapBlock(event.world); } } } public void swapBlock(World world){ if(!world.isClientSide){ for(BlockPos blockPos : blockPosList){ if(random.nextInt(decayChance) == 0){ System.out.println(blockPos + " is Decaying"); //System.out.println(world); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ //TODO: Fix issue when reloading world causing server freeze this.blocksToBeRemoved.add(blockPos); System.out.println(blockPos + " Decayed"); } } } } blockPosList.removeAll(blocksToBeRemoved); blocksToBeRemoved.clear(); } This somewhat fixed it in the sense that all blocks that are placed now decay. It also just works when I reload the world or reload the game entirely. But another problem still remains.. All the block positions are stored in blockPosList and when a block decays it should be removed from this list. Somehow certain blocks don't get removed from this list or even reappear in the list after being removed. I tried to track this behavior with debug but can't find where or why these positions keep being added to the list. Because of this, some blocks that are already cracked keep getting called to be decayed. Though when it does, the setBlockAndUpdate method returns false and never updates (maybe because it already is a cracked stone brick?). When I remove the block ingame, the position still remains and because of this a cracked stone brick block appears out of thin air in the position where I just broke the block. Even when this happens and I remove the block again, the position remains in the list the list is obviously private and new positions can only be added with the addBlock method. This method is only called in the BlockEvent.EntityPlaceEvent event and in the capability itself when loading the nbt data which puts all the saved positions back in to the IDecay instance. For further troubleshooting I also print what I save and what I load in the capability. Even though the list still contains these positions that shouldn't be there, it seems like they are not always saved when pressing escape. When pressing escape I can clearly see in the console that the world is being saved and there is no blockposition that is being saved with it. That said this isn't always the case. in some cases there is a blockpos that is still being saved but the confusing part is that this doesn't always happen and when it does there are also other blockpositions in the list that are not saved with it. It's very confusing and I've been trying to pinpoint where the bug is but failed so far. My guess is that it still has something to do with the different dimensions although it always clearly says overworld and in the other dimensions nothing is ever saved. So the blocks that have decayed somehow don't get removed or reappear in the list. and when these positions are in the list they may or may not be saved with the capability. When I debug I can see that if a block successfully decays it's added to blocksToBeRemoved and in turn will be removed from blockPosList. This works. I can see that when the removeAll method is ran, the blockPosList shrinks with the amount that blocksToBeRemoved was. It's when the game keeps running that out of nowhere blockPosList contains the blockpositions again. I thought maybe they are loaded back in from the capability but I can't see the load happening in the console. It's at this point that I wanted to see what would be saved when I press escape and it's here that I noticed these positions that magically appeared again in the list are not being saved (most of them). I'm sorry if this is a complicating explanation.
-
Thank you for your advice! Instead of storing a hard reference I just use the current world object of the event every time to try and replace the block in: @SubscribeEvent public void onWorldTick(final TickEvent.WorldTickEvent event){ swapBlock(event.world); } public void swapBlock(World world){ if(!world.isClientSide){ for(BlockPos blockPos : blockPosList){ if(random.nextInt(decayChance) == 0){ System.out.println(blockPos + " is Decaying"); //System.out.println(world); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ //TODO: Fix issue when reloading world causing server freeze this.blocksToBeRemoved.add(blockPos); System.out.println(blockPos + " Decayed"); } } } } blockPosList.removeAll(blocksToBeRemoved); blocksToBeRemoved.clear(); } This has changed all behavior and I'm not fully sure of what's going on. I'll have to explain this step by step because it's pretty specific. When I first load the world from first startup of the game, I place a couple of stone bricks. Some stone bricks change in to cracked stone bricks and the console mentioned they decayed. first weird thing is that sometimes the console says blockpos is decaying and blockpos decayed twice about the same block. Some stone bricks don't seem to decay at all. The console often mentions that a block is decaying and has decayed without the block changing in the game. Because of this they are removed out of the blockpos list and decay logic is no longer applied to them but they haven't actually been replaced with cracked stone bricks ingame. The console also prints often that a block is decaying but not that it has decayed. When I now save and quit to title and reload the world as well as restart the game the same behavior persists for as far as I can tell. My guess is that it either has to do with that the capability is attached three times or the world object is not consistent. I added a print line in the AttachingCapabilitiesEvent: @SubscribeEvent public static void onAttachingCapabilitiesEvent(final AttachCapabilitiesEvent<World> event){ if(event.getObject() instanceof World){ DecayProvider provider = new DecayProvider(); event.addCapability(new ResourceLocation(DecayMod.MOD_ID, "decayhandler"), provider); event.addListener(provider::invalidate); System.out.println("Capability attached"); } } And the console always prints "Capability attached" three times when I load in to a world. Perhaps there are multiple IDecay objects and that's causing this behavior but I'm just guessing at this point.
-
So just to make sure that I get this right, the IDecay objects are not unloaded when I unload the world? I just put this "System.out.println("Initialized DefaultDecay");" in the constructor of DefaultDecay to see when it would be initialized and how many times. I don't fully understand why but when I load the world for the first time it's printed three times. When I quit to title and reload the world it's also printed three times. Doesn't this mean it's reinitialized? I'm just not sure if the IDecay object is actually unloaded or not when I reload the world. Edit: I already figured out that this event is called three times: @SubscribeEvent public static void onAttachingCapabilitiesEvent(final AttachCapabilitiesEvent<World> event){ if(event.getObject() instanceof World){ DecayProvider provider = new DecayProvider(); event.addCapability(new ResourceLocation(DecayMod.MOD_ID, "decayhandler"), provider); event.addListener(provider::invalidate); System.out.println("Capability attached"); } } As you can see I also did a console print here and Capability attached is also printed three times every time I load in to the world. Doesn't this mean that an IDecay object is newly created every time I load in to the world? Three times apparently I don't know what that is about.
-
There is a specific scenario where the game breaks but doesn't crash and doesn't give me a crash report. My mod is supposed to replace certain blocks after a certain amount of time in the world. This already works. Quick explanation: It's a decay mod where blocks can break and decay until they are completely gone. Only blocks placed by the player are affected. I implemented it in to a capability in order to save which blocks need to change after the player would quit the world. So the decaying and the saving of which blocks need to decay already work but here's the problem. When I quit the game and restart it works. When I save and quit to title and then reload the world, the issue occurs. During this scenario as soon as the block is supposed to decay, everything stops working without explanation. I can see in the console that a block is about to decay and then nothing... This is the basic class in which I worked out the concept of decay on just stone bricks: public class DefaultDecay implements IDecay { private List<BlockPos> blockPosList; private List<BlockPos> blocksToBeRemoved; private World world; private Random random; private int decayChance; public DefaultDecay(){ this.blockPosList = new ArrayList<>(); this.blocksToBeRemoved = new ArrayList<>(); this.random = new Random(); this.decayChance = 1000; MinecraftForge.EVENT_BUS.register(this); } @Override public List<BlockPos> getAllBlockPositions() { return this.blockPosList; } @Override public void addBlock(BlockPos blockPos) { blockPosList.add(blockPos); } @Override public void removeBlock(BlockPos blockPosToRemove) { BlockPos tempBlockPos = BlockPos.ZERO; for(BlockPos blockPos : blockPosList){ if(blockPos.equals(blockPosToRemove)){ tempBlockPos = blockPos; } } if(!tempBlockPos.equals(BlockPos.ZERO)){ blockPosList.remove(tempBlockPos); System.out.println("Decay logic on position removed"); } } @SubscribeEvent public void onWorldTick(final TickEvent.WorldTickEvent event){ if(this.world == null){ this.world = event.world; } if(random.nextInt(2) == 0){ swapBlock(); } } public void swapBlock(){ if(!world.isClientSide){ for(BlockPos blockPos : blockPosList){ if(random.nextInt(decayChance) == 0){ System.out.println(blockPos + " is Decaying"); System.out.println(world); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ //TODO: Fix issue when reloading world causing server freeze this.blocksToBeRemoved.add(blockPos); System.out.println(blockPos + " Decayed"); } } } } blockPosList.removeAll(blocksToBeRemoved); blocksToBeRemoved.clear(); } } A lot of code will be changed, this is just to see if I can make it work. When the problem occurs in that specific scenario that I described above it happens here: if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())) I already checked with debugging. the block position is correct and the world object is correct. When the code steps in the setBlockAndUpdate method, the problem occurs and the code is interrupted without an error message or crash. I traced the steps going in to this method when it works and when it doesn't. Here's where the first difference is: @Nullable public IChunk getChunk(int p_212849_1_, int p_212849_2_, ChunkStatus p_212849_3_, boolean p_212849_4_) { if (Thread.currentThread() != this.mainThread) { return CompletableFuture.supplyAsync(() -> { return this.getChunk(p_212849_1_, p_212849_2_, p_212849_3_, p_212849_4_); }, this.mainThreadProcessor).join(); } else { IProfiler iprofiler = this.level.getProfiler(); iprofiler.incrementCounter("getChunk"); long i = ChunkPos.asLong(p_212849_1_, p_212849_2_); .... When it works, the if statement is false. But when the world is reloaded without quitting the game it's true. So it has to do with threads. When the .join() method is called I'm no longer able to understand what's going on. What I can tell you is that it goes down a couple of steps more before the thread is "parked". Don't quote me on that though I hardly understand it. When this happens, the game doesn't crash but mobs don't walk, commands don't work, blocks don't update etc... The server side freezes. I already had a topic post on this previously but a lot of my code has changed and now I have more information about the issue. The previous post was too vague and got lengthy so I'm hoping that with the start of this post I can be more clear about the issue at hand. Here is a link to the previous post: It might contain some code, context or information that can be useful. Here is a link to all the source code of the project available on GitHub: https://github.com/Astro2202/DecayMod I'm using Intellij. This is a pretty complicated issue. There is not necessarily anything wrong about my code. I'm just wondering if anyone has a clue of what I can do to prevent this from happening and might have an explanation for why this is happening.
-
[Solved] [1.16.5] My code stops working without any error message
Astro2202 replied to Astro2202's topic in Modder Support
Ok so this might be a lengthy one. (I think) I succeeded in fully implementing the capability and adding the implementation mentioned in my previous comment. I also removed the need of a decayhandler class and all logic that happened in the DecayHandler now takes places in the capability. The problem is that I'm experiencing the same issue that originally made me open this thread. The code stops running without error when I reload the world. I will post all relevant classes and carefully explain what the problem is. To start with: When I load a world, AttachCapabilitiesEvent<World> get's fired and adds a capability to the world provider. <- this sentence is probably false but don't know how to put it. This event gets fired in a class called DecayEventHandler.java: @Mod.EventBusSubscriber(modid = DecayMod.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE) public class DecayEventHandler { @SubscribeEvent public static void onAttachingCapabilitiesEvent(final AttachCapabilitiesEvent<World> event){ if(event.getObject() instanceof World){ DecayProvider provider = new DecayProvider(); event.addCapability(new ResourceLocation(DecayMod.MOD_ID, "decayhandler"), provider); event.addListener(provider::invalidate); } } @SubscribeEvent public static void onBlockPlaceEvent(BlockEvent.EntityPlaceEvent event){ World world = event.getEntity().getCommandSenderWorld(); System.out.println("Placement"); world.getCapability(DecayCapability.DECAY_CAPABILITY).ifPresent(h -> { System.out.println("Capability is present"); if(event.getPlacedBlock().equals(Blocks.STONE_BRICKS.defaultBlockState())){ System.out.println("DecayHandler for stone brick initialized"); h.addBlock(event.getPos()); } }); } } As you can see, the capability is added and given a provider. To be honest I am not really sure why and how everything works. I've based this on multiple tutorials, guides and documentation to come up with this. But anyway, the provider is an instance of DecayProvider.java: public class DecayProvider implements ICapabilitySerializable<CompoundNBT> { private final DefaultDecay decayHandler = new DefaultDecay(); private final LazyOptional<IDecay> decayHandlerOptional = LazyOptional.of(() -> decayHandler); public void invalidate(){ decayHandlerOptional.invalidate(); } @Nonnull @Override public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) { return decayHandlerOptional.cast(); } @Nullable @Override @SuppressWarnings("unchecked") public CompoundNBT serializeNBT() { if(DecayCapability.DECAY_CAPABILITY == null){ return new CompoundNBT(); }else{ return (CompoundNBT) DecayCapability.DECAY_CAPABILITY.writeNBT(decayHandler, null); } } @Override public void deserializeNBT(CompoundNBT nbt) { if(DecayCapability.DECAY_CAPABILITY != null){ DecayCapability.DECAY_CAPABILITY.readNBT(decayHandler, null, nbt); } } } This provider contains the DefaultDecay.java class. this class contains all decay logic and a list of block positions where it should apply the decay logic on. public class DefaultDecay implements IDecay { private List<BlockPos> blockPosList; private List<BlockPos> blocksToBeRemoved; private World world; private Random random; private int decayChance; public DefaultDecay(){ this.blockPosList = new ArrayList<>(); this.blocksToBeRemoved = new ArrayList<>(); this.random = new Random(); this.decayChance = 1000; MinecraftForge.EVENT_BUS.register(this); } @Override public List<BlockPos> getAllBlockPositions() { return this.blockPosList; } @Override public void addBlock(BlockPos blockPos) { blockPosList.add(blockPos); } @Override public void removeBlock(BlockPos blockPosToRemove) { BlockPos tempBlockPos = BlockPos.ZERO; for(BlockPos blockPos : blockPosList){ if(blockPos.equals(blockPosToRemove)){ tempBlockPos = blockPos; } } if(!tempBlockPos.equals(BlockPos.ZERO)){ blockPosList.remove(tempBlockPos); } } @SubscribeEvent public void onWorldTick(final TickEvent.WorldTickEvent event){ if(this.world == null){ this.world = event.world; } for(BlockPos blockPos : blockPosList){ if(random.nextInt(decayChance) == 0){ System.out.println(blockPos + " is Decaying"); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ this.blocksToBeRemoved.add(blockPos); System.out.println(blockPos + " Decayed"); } } } blockPosList.removeAll(blocksToBeRemoved); blocksToBeRemoved.clear(); } } This class implements the IDecay.java interface public interface IDecay { List<BlockPos> getAllBlockPositions(); void addBlock(BlockPos blockPos); void removeBlock(BlockPos blockPos); } DecayProvider.java also states which capability is used and saved. As you can see in the class posted above it states "DECAY_CAPABIITY" Now here is the DecayCapability.java class: public class DecayCapability { @CapabilityInject(IDecay.class) @SuppressWarnings("ConstantConditions") public static Capability<IDecay> DECAY_CAPABILITY = null; public static void registerCapabilities(){ CapabilityManager.INSTANCE.register(IDecay.class, new Storage(), DefaultDecay::new); } public static class Storage implements Capability.IStorage<IDecay>{ @Nullable @Override public INBT writeNBT(Capability<IDecay> capability, IDecay instance, Direction side) { final CompoundNBT nbt = new CompoundNBT(); int counter = 0; for(BlockPos blockPos : instance.getAllBlockPositions()){ nbt.putInt("xPos" + counter, blockPos.getX()); nbt.putInt("yPos" + counter, blockPos.getY()); nbt.putInt("zPos" + counter, blockPos.getZ()); counter++; } nbt.putInt("amount", counter); return nbt; } @Override public void readNBT(Capability<IDecay> capability, IDecay instance, Direction side, INBT nbt) { for(int i = 0; i < (((CompoundNBT) nbt).getInt("amount")); i++){ BlockPos blockPos = new BlockPos(((CompoundNBT) nbt).getInt("xPos" + i), ((CompoundNBT) nbt).getInt("yPos" + i), ((CompoundNBT) nbt).getInt("zPos" + i)); instance.addBlock(blockPos); } } } } The capability is registered and it's here that I specified how to save and load all the positions of the DefaultDecay instance. The method that registers the capability is spoken to in the FML CommonSetupEvent private void setup(final FMLCommonSetupEvent event) { System.out.println("Init!"); DecayCapability.registerCapabilities(); } I think I have given all the code that is relevant. Now everything works fine when I first load in to the world. I get all messages in the console that I wrote down. I place Stone bricks and they decay. All good. It's when I save and quit the world, then rejoin the world that it starts breaking. Here is what is last printed in the console: [23:03:03] [Server thread/DEBUG] [ne.mi.fm.FMLWorldPersistenceHook/WP]: Gathering id map for writing to world save New World [23:03:03] [Server thread/DEBUG] [ne.mi.fm.FMLWorldPersistenceHook/WP]: ID Map collection complete New World [23:03:07] [Server thread/INFO] [STDOUT/]: [com.astro.decaymod.core.capabilities.DefaultDecay:onWorldTick:68]: BlockPos{x=-180, y=84, z=63}is Decaying [23:03:07] [Server thread/INFO] [STDOUT/]: [com.astro.decaymod.core.capabilities.DefaultDecay:onWorldTick:71]: BlockPos{x=-180, y=84, z=63}Decayed [23:03:10] [Server thread/INFO] [STDOUT/]: [com.astro.decaymod.core.capabilities.DefaultDecay:onWorldTick:68]: BlockPos{x=-180, y=83, z=63}is Decaying After this last line, nothing else is printed and the remaining blocks don't decay anymore. The problem occurs in this line: System.out.println(blockPos + "is Decaying"); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ System.out.println(blockPos + "Decayed"); In some rare cases - like coincidentally in this run like you can see in the console - there is 1 block that still decays just fine. But in most cases the first block that decays after the reload breaks the code just like the second one after the reload did now. It seems like the code steps in the setBlockAndUpdate method and never comes out. It just stops functioning. I just now also noticed that ingame commands no longer work. Something has gone really wrong in the back and I don't know why. It could be because I haven't implemented the capability right. I only half know what I'm doing with it. I also realize that there is still a lot of code that needs improving like I still didn't make use of the scheduled block tick system. So I'm thinking, I either didn't implement the saving right or it's an unrelated issue. What do you think @Draco18s? Edit: I've done some further "testing" and when this problem occurs the whole server side seems to freeze. Mobs don't walk anymore, commands don't work anymore and when destroying the bottom half of tall grass the upper half does not destroy with it. I first load in the world and place 4 stone bricks, they can decay like I coded. I place 4 stone bricks, quit and rejoin, and sometimes 1 but otherwise no stone bricks decay and as soon as the setBlockAndUpdate method is called the server freezes. Like you can see in the console, the block position given to this method is still correct. So to me it's then obvious the problem lies with the world object that I'm trying to call it on right? perhaps it's no longer the same object. Edit 2: Ok so the problem is not solved but good news! When I fully quit the game and reboot and then load in the same world it is fully functional! It remembers what blocks I placed and need to decay and it doesn't freeze like it does when I reload the world without quitting the game. This is a big relief. So the saving at least somewhat works thanks to your feedback! -
[Solved] [1.16.5] My code stops working without any error message
Astro2202 replied to Astro2202's topic in Modder Support
So I've done some digging in to the capabilities system and I think I'm finally starting to understand it. I've tried to make use of the AttachCapabilitiesEvent<World> to add a capability with my DecayHandler class in it. When I would place a block I would set the decayhandler of that capability with the values of the block I placed. But there are two problems here. 1) I'm unsure of when and how many times the AttachCapabilitiesEvent<World> event is called. It could be that there is only one instance of this capability for what I know. Meaning that there also could be only one DecayHandler which there can't of course. The amount of decayhandlers available also shouldn't be defined by the amount of times that the AttachCapabilitiesEvent<World> is called but by the actual blocks that you place. 2) Correct me if I'm wrong, but when a capability is added, you give it a provider which gives you a default class of what you want to save. I need to provide these default values myself but I can't just give a default blockstate and position so the values are null and because of that the game will crash because of a nullpointerexception. So here's what I'm thinking. I can create a capability with simply a list of decayhandlers that is initially empty that is added with the AttachCapabilitiesEvent<World> event. Then on the BlockEvent.EntityPlaceEvent event I can create the decayhandler and add it to the list within the capability. The world will remember all the decayhandlers because when the writeNBT method is called, I can simply iterate over the list of decayHandlers and save the relevant data. When they need to be loaded back in I can simply create the decayhanders again with the data that was saved, and add them back to the list within the capability. Is this a valid idea? Also I'm not sure with what you mean here. Isnt the world capability an existing system? Or do you mean the capabilities provided by forge e.g. IItemHandler? Can you also please explain when and how many times AttachCapabilitiesEvent<World> is called? Last thing, I appreciate your posts! They really set me in the right direction. It's hard to find relevant information and it's often outdated or conflicting with other sources. At least in your comments I can have complete trust. -
[Solved] [1.16.5] My code stops working without any error message
Astro2202 replied to Astro2202's topic in Modder Support
I do, but haven't put much though into it yet. I was trying to make it work in the overworld and worry about other worlds later. Thank you for your response. It seems that I have a lot of homework to do. I will take your feedback in to consideration and implement it accordingly. This might take a while but I'll update the thread if I either fix everything or get stuck again. Thank you for your time! -
[Solved] [1.16.5] My code stops working without any error message
Astro2202 replied to Astro2202's topic in Modder Support
The class is initialized in the BlockEvent.EntityPlaceEvent. EntityPlaceEvent @SubscribeEvent public static void onBlockPlace(final BlockEvent.EntityPlaceEvent placeEvent){ BlockState blockState = placeEvent.getPlacedBlock(); BlockPos pos = placeEvent.getPos(); System.out.println("placement"); if(blockState.getBlock().equals(Blocks.STONE_BRICKS)){ System.out.println("Stonebrick placed"); DecayHandler decayHandler = new DecayHandler(blockState, pos, world); decayHandlers.add(decayHandler); } } The OnTick method is called in the TickEvent.WorldTickEvent. WorldTickEvent: @SubscribeEvent public static void worldTickEvent(final TickEvent.WorldTickEvent tickEvent){ if(world == null){ world = tickEvent.world; } List<DecayHandler> decayHandlersToBeRemoved = new ArrayList<>(); for(DecayHandler decayHandler: decayHandlers){ DecayHandler decayHandlerToBeRemoved = decayHandler.OnTick(); if(decayHandlerToBeRemoved != null){ decayHandlersToBeRemoved.add(decayHandlerToBeRemoved); } } if(!decayHandlersToBeRemoved.isEmpty()){ for(DecayHandler decayHandlerToBeRemoved : decayHandlersToBeRemoved){ DecayHandler temp = null; for(DecayHandler decayHandler : decayHandlers){ if(decayHandler.equals(decayHandlerToBeRemoved)){ temp = decayHandler; } } if(temp != null){ decayHandlers.remove(temp); } } } //worldDecayData.get(world); } Here are the variables used in these events: public static List<DecayHandler> decayHandlers = new ArrayList<>(); public static World world; Also what might be relevant, the DecayHandler is also removed when the stone brick is removed ingame: BreakEvent: @SubscribeEvent public static void onBlockDestroy(final BlockEvent.BreakEvent breakEvent){ BlockPos blockPos = breakEvent.getPos(); DecayHandler decayHandlerToBeRemoved = null; System.out.println("broke block @ " + blockPos); for(DecayHandler decayHandler : decayHandlers){ System.out.println("decayHandler location: " + decayHandler.blockPos); if(decayHandler.blockPos.equals(blockPos)){ decayHandlerToBeRemoved = decayHandler; } } if(decayHandlerToBeRemoved != null){ decayHandlers.remove(decayHandlerToBeRemoved); System.out.println("decayHandler Removed"); } } Perhaps I misused the term iteration.. The code should be running constantly without interruption but just stops without error or warning. It's because while this problem occurs, there are always DecayHandlers active that are called in every WorldTickEvent. I unfortunately have never seen anything about a scheduled block tick system. Thank you for bringing it to my attention. When it isn't 0, the block will not decay and thus nothing will happen to the block. Like you can see in the code above, blocks that have decayed need to be removed out of a list and when the block has decayed it returns itself so that it can be identified which DecayHandler to remove. If it hasn't decayed, it returns null and so it will not be removed. I know there is some incredibly inefficient code here but right now i'm just trying to work out my idea and correctly implement it later. I've actually been trying to save this information for a couple of days now and actually have a thread about this that I posted not long before this one. It's when trying to make saving work that I discovered this problem. When I comment out all my code used to try and save data (right now I'm trying to use WorldSavedData by the way), the problem still occurs and I thought it was an unrelated issue that I need to fix first. So if I understand correctly, I should try and save my classes and stop running them while quitting a world. Because the class keeps running even when the world isn't loaded, which causes the issue when I try and reload the world? Because the decay happens at a random moment, sometimes the code keeps running for a couple of seconds even though I'm already loaded back in to the world. It's just as soon as a block needs to be updated that the code stops even though the stone brick that was originally placed is fully loaded in and all the parameters of the DecayHandler class are still correct. Maybe some important information, I do have a good understanding of Java. It's just that I don't really have a lot of experience with Forge and don't have a full understanding of it yet. But I'm eager to learn! Thank you for your response, I realize that I'm probably making stupid mistakes and don't see the obvious, but I hope that's normal for a Forge rookie. -
I wrote a class that stores the BlockPos and BlockState of a specific block that needs to be changed into another block after a certain amount of time. So far I just coded stone bricks being changed into cracked stone bricks. In every worldtickevent I call a method called onTick in which there is a slight chance (1 in 1000) that this change will occur. The class that does this is called DecayHandler.java DecayHandler.Java : public class DecayHandler { public BlockState blockState; public BlockPos blockPos; public World world; private Random random; public int decayChance = 1000; private boolean decayed = false; public DecayHandler(BlockState blockState, BlockPos blockPos, World world){ this.blockState = blockState; this.blockPos = blockPos; this.world = world; random = new Random(); } public DecayHandler OnTick(){ //TODO: Refactor in other methods and implement BlockSwapper if(random.nextInt(decayChance) == 0 && !decayed){ System.out.println(this.blockPos + " Decayed"); if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())){ this.blockState = Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState(); decayed = true; return this; } } //WorldDecayData.get(); return null; } } This code works fine when I load in to a world and place stone bricks down. The stone bricks stay there for a couple of seconds and then they change into cracked stone bricks. The problem occurs when I save and quit to title and rejoin the same world when I placed stone brick that hadn't decayed yet. The game seems to remember what DecayHandlers were running which I guess is logical because I never closed it. So when the world is reloaded again my code stops without error and no longer functions. I can see in the terminal that the last message that was displayed was that a block decayed so I put a breakpoint on it to see what went wrong. By the way, the game keeps running just fine but my code just stops. The part where it seems to go wrong is in this line: if(world.setBlockAndUpdate(blockPos, Blocks.CRACKED_STONE_BRICKS.getBlock().defaultBlockState())) When I try to step in to every single detail the callstack becomes insanely large and I'm unable to understand what's going on. The only thing I know is that right at the end they put my thread in to parking or something? I really don't understand what was going on. Can someone explain why this is happening? I don't have a clue of what's going on. Edit: Here is a GitHub link of the project: https://github.com/Astro2202/DecayMod
-
What I already notice is that it's no longer markDirty() but setDirty(). I'll try to implement it as best as I can using these docs. Thank you very much! This wiki seems incredibly useful and I didn't even know it existed... If I still can't manage to let it work I'll post my current code. Edit: After having a closer look I soon found out that I have the same problems with this documentation. While it does give useful context about some of the uses and such of WorldSavedData, it's no longer up to date. The methods mentioned that you're supposed to use to make it work no longer exist and I can't seem to figure out what the new implementation is. For added context for what I'm trying to achieve: I'm trying to create a decay mod in which the blocks - And this is important - that are ONLY placed by the player are affected by the decay logic that I am trying to implement. I am not adding any extra blocks to the game, just adding behavior to existing ones. For example Stone Bricks will change into cracked or mossy stone brick depending on their situation after a certain amount of time. I'm very new to modding so the solution that I came up with is adding a DecayHandler class to every block placed by the player that keeps track of the block that is placed and applies decaying functionality to it. The problem is that when you close the game, these DecayHandlers are thrown out and all decaying simulation is lost when the world is reloaded. The reason that I'm giving this information is because I'd like to ask two things: Is this a good way to do it and is WorldSavedData the best way to store my DecayHandlers?