Hey there,
I'm the author of LittleTiles/CreativeCore and recently I found out that Minecraft has a huge lag spike during unloading chunks if the world contains a lot of tileentities.
How did I find it?
Although the performance of LittleTiles was not bad there used to be quite horrible lag spikes, which occurred rather frequently while the player was moving. It became more and more unbearable. So I made several attempts to fix it, but unfortunately without any success. I couldn't find anything in my code.
A few months later I tried it again, but this time I was ignoring my code, since I suspected it to be an issue caused by Minecraft itself.
The issue itself
Eventually I found that the lag spike was not caused by loading new chunks, but instead while unloading the old ones. After some further investigations I saw those two suspicious looking lines in World.updateEntities()
:
this.tickableTileEntities.removeAll(this.tileEntitiesToBeRemoved);
this.loadedTileEntityList.removeAll(this.tileEntitiesToBeRemoved);
So I debugged it and ... yes ... wow. There were 20,000 tileentities in tickableTileEntities
and loadedTileEntityList
while tileEntitiesToBeRemoved
had a size of 3,000. Stepping over it using the debugger took quite a long time.
I finally had found the issue. It's not caused by the amount of traffic or the complexity of tileentities, but caused by the amount of tileentities no matter if there are furnaces or more complicated ones.
This video demonstrates this issue: https://www.youtube.com/watch?v=vTCJbp45pWM
I also reported this issue to Mojang: https://bugs.mojang.com/browse/MC-119686
How did i fix it?
I replaced those two lists with HashMap<ChunkPos, List<TileEntity>>
. So once a chunk will unload one key will be removed instead of iterating through the whole list several times. This completely solved the lag spikes (as shown in the video).
I implemented it in CreativeCore v1.8 and eventually decided to create this pr.
About this PR
I changed tickableTileEntities
and loadedTileEntityList
from an ArrayList to a ChunkedTileEntityList
, which basically saves all tileentities in a HashMap<ChunkPos, List<TileEntity>>
. Furthermore I added another list to the world (tileEntitiesChunkToBeRemoved
), which contains all chunk positions of unloaded chunks. During each tick those will be removed from tickableTileEntities
and loadedTileEntityList
.
It's still possible unloading a tileentity using the old way, but I changed it for the chunk. A chunk will now call markChunkForRemoval
instead of calling markTileEntityForRemoval
(func_147457_a) for each tileentity.
If there is anything to complain about my naming or the formatting style, just let me know.
Possible mod conflicts
Since CreativeCore changed it already I can ensure that there is currently only one mod it is conflicting with https://github.com/RootsTeam/Embers/issues/237.
As long the iterator is used there are no changes required.
Last words
This issue must have existed for several years now. It probably caused a lot of headaches and definitely ruined the experience for many players, especially if you have build a lot of machines or other stuff close together. So I really hope this improves our modding situation. It affects the server in the same way as the client, only that it might be worse for the server (more loaded tileentities).
I'm looking forward to smoother gameplay and hope this PR will be merged soon. Once that is done I will create another one for 1.11.2.
UPDATE
All the indexed methods have been implemented. There are bad at performance, but the game will no longer crash of a mod is using them (like Embers).
In Regards
CreativeMD
Vanilla Bug Superseded 1.12