Hello Cosmorists!
2025 started off with the Narrow Echoes update with many changes! Following that was a more focused Verdant update that added a new cave biome a little over a week later! Let's get into the devlog!

-
Although Narrow Echoes was a fairly large update, the update was mostly all over the place in terms of what the game added or changed. Things such as graphical changes, quality of life improvements, steam achievements, steam cloud, autosaves, explosives, nighttime enemies, many ambient sounds and sound effects, and so on. Future updates are now going to have a different structure.
The structure of future updates
0.x updates
-
These will still serve as the game's major updates, and are expected to take roughly the same amount of time to make of a few months. This time, it'll follow a particular theme. Instead of small / moderate changes spread out across the whole game like with Narrow Echoes, it'll focus on a broad yet specific part of the game that follows the main theme of the update.
One example is Aquatica. This is a whole planet, a fairly broad part of the game given that it has multiple biomes, many creatures, and unique resources, but is also one specific part of the game, as Aquatica is just one explorable world of 3 in the game so far.
In this example, it's a major update that focuses almost purely on Aquatica developed in the same time period as Narrow Echoes. The amount of content added to the game remains the same, but is much more concentrated.
It's also likely to touch other unrelated parts of the game, especially polishing up the game, but to a lesser degree, and the main purpose of those unrelated changes is to make the update feel a little bigger.
While these major updates are being made, it's likely for me to want to update another part of the game, and in that case, a regular update unrelated to the main theme is likely to be made in between to keep content flowing in these quieter periods of updates.
0.x.y updates
-
These will also serve as regular updates like before, and just like with the major updates, it'll also focus on one particular part of the game. These will focus on an even more specific part of the game than the major updates. There will also be times where these updates are also going to be non-focused, and update multiple parts of the game in small chunks.
Going back to the Aquatica example, instead of updating the whole planet, an example of this is something like a single biome being updated or added, similar to how the Verdant update focused entirely on the Verdant caves. For the less focused ones, it can be a new enemy in one biome, a new resource dropped by a new tile on another planet, and so on in one update, somewhat like a scaled down Narrow Echoes.
0.x.y.z updates
-
These updates are mostly unchanged, and they'll still serve as small bug-fixing / tweaking updates, not following a particular theme like the content updates. These will likely to be all over the place if it fixes many bugs that are in different unrelated parts of the game.
Reasons for the structure change
-
The primary reason for the structure change is for the updates to be more focused instead of being mostly spread out in many different parts of the game.
Immediately after fixing cloud bugs in v 0.1.0.2, almost all of my creative energy was directed at the Verdant caves, and apart from minor tweaks elsewhere, that was where all efforts went. This made the Verdant update (v 0.1.1) feel like a decently sized update after only a little over a week of working on it.
A major update with a more narrow focus made in a few months invested into the update's theme means a single thing to look forward to, and a lot more concentrated content for that theme!


A programming tangent
-
At the moment, my attention is focused on the way the game saves data. It's a bit of a mess, and felt that it needed a refactor.
How game data is stored
-
Cosmorists is programmed in C#, but the same principle can apply to other programming languages.
Everything in the game is stored in raw binary, and mostly optimized for minimal storage usage. The type int for instance, is often stored with a variable length.
An example of storing an integer
-
Let's try the number 81000 as an example. It will save as a binary array like this: [03 68 3C 01] (These are hex numbers). The first byte, 03, tells the reader to look at the next 3 bytes: [68 3C 01]. The position of each byte is multiplied by a power of 256, and that power is equal to the index of the array.
Converting these to decimal values for familiarity, we get [104, 60, 1].
Conversion goes like this:
104 * 256 ^ 0 +
60 * 256 ^ 1 +
1 * 256 ^ 2
= 81000
It's stored this way instead of blocks of 4 bytes mainly to reduce the amount of space smaller integers take up, and if for some reason, A long should be used instead of an int, there's the added flexibility of being type-independent, as long as it's an integer being read.
An example of storing floating point numbers
-
Floating point numbers are more precise values than integers, such as the player's position, the enemy's position, the temperature, the time of day, and other precise values.
In C#, there are float and double values. float has less precision, and takes up 4 bytes. double has more precision, but takes up 8 bytes.
These values are saved exactly as 4 or 8 byte chunks depending on the type.
I won't get into everything, but this is the basis of how the game stores data. Nearly everything in the game able to be saved is stored as an integer index, and that index is what gets saved, often taking as little as 2 bytes each on the disk. This is mostly how the game's save file size remains so small, only crossing around 100 KB in size after many hours of playing on the same save.
Why the refactor
-
Although the file size is small, if I ever need to create a new class containing data that needs to be saved, I will always have to write code responsible for converting the data to a byte array, and more that reads from that byte array to get it to work with the disk.
There are also some classes who saving code is harder to modify when taking backwards compatibility into consideration, and if at some point, wish to add more data to a class that also needs to be saved, there will have to be if statements that checks the last version that was last played on so existing saves won't break, and after enough versions, that code can turn into a spaghetti mess that even I wouldn't understand well.
The solution I'm aiming for
-
3 Words. Key-Value pairs. This is where the game's saving code has long trended towards, and it works like this: The game saves a byte, and associates it with another piece of data, and that piece of data can have its own set of key-value pairs, going all the way down to either integers, or floating-point numbers.
Right after releasing the Verdant Update, I coded a more general-purpose data structure that stores data exclusively as key-value pairs, and it can also store instances of itself. I don't know what to call it, but I'll name it an "object dictionary tree" since it's recursive and works like a tree, makes heavy use of dictionary lookups, and takes in many basic types of objects.
A good analogy is the way files and folders are stored on operating systems. A file has a name with data inside, and a folder can store named subfolders and other files inside. Those subfolders can store more subfolders and files, and so on until the subfolders contain either nothing, or exclusively files without any subfolders of its own.
It can convert to and from a byte array with a single call, and the object dictionary tree does all the heavy lifting doing the conversion. It still deals with saving data to and from raw binary, such as the variable length integers, and floating point numbers taking up 4 or 8 bytes.
Here's an example of it in code filled with random data for testing purposes:
In this example, each key is the first value in square brackets, and the associated values hold the data. It can be an int, a long, a string, an array of those types, or even another dictionary, and have its own set of key-value pairs, which can also store data in the same way.
If you're wondering, this takes up 199 bytes, and these are some of the first few bytes :)
This 199 length byte array can also convert back to the data shown above, with the only difference being that the empty dictionaries and arrays are ignored when converting.
This solution is sought after for a few major reasons: Greater flexibility, an easier time maintaining backwards compatibility, and each class having less responsibility than before.
Dealing with saves will soon be easier once the refactor is done. If there's more data that needs to be saved, all I need to do is add the necessary variables containing the data to the tree, and when reading from it, assign the same variable from where it is in the tree.
It's very likely that saves will take up more storage than before, roughly 2x as much, as the byte arrays also contain additional bytes that tell the reader which variable type is saved, when to do recursion, and when to read arrays, but the file sizes are already small, and will make little difference on the disk.
That's enough technical jargon that most of you likely didn't understand, now onto the creations :)


Creations / Media
-
The first month of the perfect square year 2025 certainly had something in store for community made creations! :)
Fanart
-
Discord user MJ made this Cosmorists fanart! Look at how eager that slime is to attack the cosmorist who just wants copper from this cave!
Screenshots
-
All these scorpions are swarming this cosmorist in the desert! Look at how many of them there are!
(by AIB):

Now that about wraps up this month! The Narrow Echoes update dropped, followed by the Verdant update 10 days later! Once the save code is refactored to use the new object dictionary trees, saving should be a lot less tedious :)
See you for next month's Latest in Cosmorists!
