1
votes

Background:

I am working on a 2d infinite world generation. It is tile based meaning my terrain is fully made out of squares. You can imagine it like 2d Minecraft (looking at the terrain from above).

I implemented standard chunk system where the terrain gets chopped into small 8x8 tile areas that get loaded and deleted as player moves around the world. This, so far mentioned, works perfectly smooth without any hiccups or lag. I am using Lua and Corona SDK.

The problem:

Since the player will be able to modify the terrain, I need a fast and efficient system of saving chunks in memory once the player loads a new chunk and a system of loading those chunks from memory if they have been loaded previously.

This is where the problem takes place. It needs to read from and save to files (memory) quite often which causes noticeable lag. Making chunks bigger is not an option.

Solutions I tried but all caused lag:

a) First and obvious solution I implemented was to just create a text file for each chunk with tile names as strings. It looked something like this: x12y10.txt and inside the file I just dumped all tile names in order they need to be placed on screen: "Grass Grass Water Sand Sand Sand Grass Grass...". That worked but loading strings was slow so I tried another solution: save tiles as indexes.

b) Saving tiles as their indexes. I paired every tile to a number. Since numbers are shorter, they take less memory and are faster to load. I gave each tile it's own index: Grass -> id 1, Water -> id 2, Sand -> id 3 and so on. This way I only needed to save 1 or 2 chars instead of full string per tile. My txt files looked like this now: "1 1 2 3 3 3 1 1...". This worked better but still caused lag.

c) Next improvement I did was with how chunks are organized in memory. Instead of dumping all the chunks in a single folder, for each x coordinate I made a folder and put all chunks that have that x value in there.

So instead of this:

  • Folder with all chunks: x0y0.txt, x0y1.txt, x0y2.txt, x1y0.txt, x1y1.txt, x1y2.txt

Inside folder with all chunks I had this:

  • Folder x0: x0y0.txt, x0y1.txt, x0y2.txt

  • Folder x1: x1y0.txt, x1y1.txt, x1y2.txt

I am not sure how much this helped for small number of chunks, but I am pretty sure for thousands of chunks, improvement is there.

Possible solutions?

I have some ideas for improvements, but I would like to hear your opinion on the solutions.

a) Saving terrain in binary files?

b) I have read about Minecraft region format, really tried to understand how it works, but did not get it since there is little information about it. So if anyone knows it and could explain their system to me, I would be really grateful.

c) Another faster file format?

d) Is making/accessing many folders slow? Is there a better alternative?

2
Why saving data in files? Use single Lua table located in memory: {['x12y10']='1 1 2 3 3 3 1 1', ['x0y0']='2 1 2 1 2 1 2 1', ...} - Egor Skriptunoff
The terrain can generate endlessly. Dumping every single chunk ever made in a table is a really bad idea. Also terrain needs to save if there was a change. Upon leaving the game, that lua table will be destroyed and terrain changes deleted with it. - FICHEKK
A human can't make so many changes to overflow Lua VM memory. Storing one million modified cells in a table is OK for Lua. One million mouse clicks is 11 days of clicking for human :-) Yes, the table must be saved on exit and loaded on entry. - Egor Skriptunoff

2 Answers

2
votes

I really feel like this is cs-101 question, but cannot google up any answer right away so quick summary.

All files are just sequences of bytes. If we're talking about reading and writing raw bytes, no format will make 64 bytes appear in memory faster than another.

Text file is a sequence of bytes with slight limitations on their values (well, the limitation is if you want standard text programs to display it). A string "11" (sequence of bits: 110001110001) from a text file won't be loaded faster than sequence of unprintable bits 100000100000 from "binary" file.

Structuring directories at the very least reduces the number of nodes system checks when trying the file you've requested to open. But mechanisms underlying the filesystems are very complex and affected by a lot of factors. The overall guess is that frequent reading even of small files will be slow. And all files carry some stockpiling overhead (system info to keep them tracked and ordered), small files will have lower useful/auxiliary info ratio. I know of at least one 2d project with mutable map that was making hdds growl and grunt before they moved onto bigger files years ago.

You don't have to make chunks bigger, that's different thing, but you can write them into the same file. Instead of million of files by 64 bytes you can have a single megabyte file (assuming you use a byte per chunk). A million chunks is lot for a player to modify or walk around. If you unpack that data to tables, it will take up more space but you don't have to decipher all the string, only the currently needed bytes. Yes, modifying a megabyte string in lua will cause creation of another megabyte string which is slow, but you don't have to do it every time, or you can split string into smaller ones and modify those. And only do writing when needed. I/O bufferization may even happen without your intervention but again it is usually helpful for big files. Yes there will be more than a byte of info per tile (2^8 possible states per tile is a lot however), the system stays the same.

The same thing is done for textures, because loading data in a single big chunk in a single big scoop is faster than searching around for tiny bit here and there. Indexing a single long area of memory is also faster than chasing pointers around.

On top of that, you may try to read\write less bytes than you want in the memory. For example by compressing data.

2
votes

In minecraft chunks are not stored unless they have been visited / modified, otherwise they are generated.

That would leave you a system where only blocks which have been modified by the player would need to be stored, with the un-modified areas being re-generated by using the same random seed, each time.

Creating a hierarchy of modifications ... A chunk is an 8x8 block, create a super-chunk which is 8x8 chunks, and only look for a file, if any of the 8x8 super-chunk has been modified.

Possibly store all of the super-chunk in one file, which would limit the number of files (adding more files does decrease the speed of the system, and also uses space on the system inefficiently).

If you have any spare time-space, perhaps have a cache of the chunks near the player, and pre-load the modified areas which are being approached. This would limit the visible lag required