Skip to content

3 March 2025 - The Dev to Alpha Transition Phase

<<< Previous Post Next Post >>>


  Well, I'll just start this one by saying holy hell. I've been gone a while! Lots has changed in the internim. As a matter of fact, I have tried to write this blog post three times in the past, each time I just have to stop because there's so much stuff to talk about. The problem is circular; the more I do, the more there is to talk about.

  For the sake of my sanity (and for your reading pleasure)...

Meta (Lore/Story, Community, etc.)

  • A Discord server dedicated solely to The Conservatory has just gone up. Join here.
  • I rewrote a lot of the lore for various species. For the time being, I have chosen to significantly reduce the content on all species pages since it's in flux right now.
    • I got permission from hdorriker et. al. to reference their universe in my game. If you have no clue what I am talking about, I give him and his friends a huge shoutout on the About page.
  • Lots of tiny internal changes to the story.
  • A new FAQ Page has been added to the website.

Code and Engine

Assets and Mods

  I completely rebuilt the entire asset management system from the ground up. The old system completely ignored Godot's resource system, and I feel a bit foolish for not realizing that there's a reason the assets are compiled. This required redoing basically all code that loads some game object of any kind. That's a lot of code.

  • The old system used to use directories only. Mods had to be directories too.
  • The new system uses ProjectSettings.LoadResourcePack as Godot advises.
    • However, this does not preserve existing files. It's always one or the other, original vs. new. I need both for my system to work.
    • To get around this, I had to design a new type StarFileSystem, a virtual representation of a filesystem from an archive file that can be indexed using Godot-like paths and ObjectIdentity (IDs in my game).
    • In essence, this system allows me to load pck files dynamically and store their contents in a separate cache per-mod, allowing me (and other modders) to index any version of a file that I want.
    • I had to come up with an agnostic way to point simple IDs (like, say, vanilla:grass) to its relevant assets without including a path in the ID.
    • A significant amount of existing game code had to be refactored to account for this new system.

  Part of the reason this took so long is because I screwed it up. Twice. So I had to rework the entire system three times before I could account for all the quirks (and frankly I'm tired).

IEntityAction - The Entity Action System

  I designed the Entity Action system. This is an agnostic, universal input system for all entities, including players. The system is an interface layer, that is, it provides an expectation (or "contract") for what an entity can do.

  If it's a thing, and you have to do that thing somehow by using some input, then it classifies as an entity action. Yes, it's that vague, which is part of its design.

  This system is very large and complex behind the curtain, but rest assured, implementing it is easy. The reason it is designed this way is because of the reusability it provides for assets, you see, any entity can perform any action in any context (unless explicitly denied by code)!

  So what does this mean?

  • Actions can be reused. I only have to make one unarmed melee action, and then every single entity ever added to the entire game (both now and all future mods) can share that one, single action.
    • Actions, by design, don't care how they are being performed. They only need a reference to the entity performing the action.
    • Actions can receive additional context, i.e. the action for firing a mining laser can (but does NOT have to) be given information about the item being used.
      • In this scenario, it will damage/discharge the item if it has been provided with the item.
      • If this information is missing, it'll still fire the laser since that's about all that it can do. There's no item to discharge, so don't bother with it.
      • This behavior is well-defined and predictable!
  • I think one of the more compelling points of this is that mods that want to add, say, the ability to shapeshift into some other entity, can do so very easily. It's quite literally as easy as setting the entity's controller to the player controller and It Just WorksTM

  This system took a long time to design and a long time to get right. I had to make it twice.

Terrain Renderer Mk. III

  Terrain rendering has had a huge facelift, and when I say "huge", well, I don't know how to articulate it.

  • Atlasing was implemented using texture arrays, meaning that if an atlas overflows, it can just make a second one.
  • Vertex data has been dramatically improved (it's smaller, and easier to pack)
  • Palette rendering has been dramatically improved (palettes are sent as a texture instead of a uniform, too)
  • Lighting has been dramatically improved (significantly cheaper algorithm to snap to pixels)
  • Performance has been dramatically improved (it's one material for the entire world!)

  The terrain system literally went from "primitive, overcomplicated mess" to "advanced, simple, well thought out procedure". I can't even quantify how much work this took.

Registries Mk. II

  With the addition of entity actions and the older entity identity system, registries needed a huge rework. Specifically, registries needed to become variant. In C#, this is something applied to an interface that changes how its generic parameters work. Namely, it affects how they can be cast. For example, the ability to cast List<string> into IEnumerable<object> implicitly (note how we went from string to object) is provided by covariance. This works in the opposite direction for contravariant parameters (an example is if I have a method that accepts an Action<string> parameter, I can pass in an Action<object> parameter and it's fine with that even though the types mismatch).

  To improve registry behavior, registry objects needed to be made covariant (despite receiving registerable objects!) and registerable classes needed to be made contravariant! This is kind of turning the system on its head, but it's extremely important that it is designed this way. It's a bit confusing, and frankly I can't find an understandable way to describe what it does or why I did it. It's an absolute requirement for types like IEntityAction<...> and IEntityIdentity to work.

Summary

  Basically, a significant part of the game's internals got entirely redone, some had to be done several times. In fact, I'm still finishing up the new asset thing. That's why I've been gone for a month. As I approach alpha testing phase, I need everything to be ready!