Skip to content

15 March 2025 - World touchups, Inventories, and Entities

<<< Previous Post Next Post >>>


  Just recently I started on finalizing the inventory system and the entity action system. I may or may not have said these were done in an older post. And they were. Just done incorrectly.

Inventories

  Inventory access is a complicated topic. In The Conservatory, only one thing can ever have access to an inventory at once! This might sound annoying, but it has a lot of benefits too.

  • Inventories can be multithreaded under this system.
  • Item loss can't happen unless the code throws it away, because items are always precisely tracked.
  • This also fixes item duplication exploits.

  The issue is that one at a time access is slow and it sucks. I need something new, something more powerful and more capable. It was going to have to follow some rules:

  • Anything that just wants to look at the contents of the inventory, but not change it, (that is, get read-only access) should be allowed to do that at the same time as other read-only accessors.
    • Metaphorically speaking: Multiple people can be reading text on a phone at the same time. We don't need to take turns reading it, we can all see the screen at the same time.
  • Anything that wants to change the inventory (add/move items, change stats) needs read-write access which only one thing can have at a time
    • Metaphorically speaking: Only one person can be typing on a phone at the same time. If you try to have two people type at once, it gets into a mess and everything is ruined.

  Now I can already hear my fellow C# users: "Well, that just sounds like ReaderWriterLock"! you yell at your monitor. And to that I say you are exactly correct!

  In fact, this is yet another scenario where I have happened along something basically perfect for what I need:

ReaderWriterLock works best where most accesses are reads, while writes are infrequent and of short duration. Multiple readers alternate with single writers, so that neither readers nor writers are blocked for long periods.

  Inventory access follows this pattern almost 1:1. For this reason, inventories will now make use of this type.

Entity Actions (Episode ... what, 4 now?)

  Entity Actions are a system that's kind of new, so I have had to rethink them several times as I watch the game evolve. The previous iteration of the system was pretty solid but fundamentally flawed.

  Since by now my regular readers are wondering what the hell is going on and why I have to keep readjusting this system, most of the complexity comes from its defining feature - a feature that I am hellbent on adding - which is stateless actions. I won't reiterate the benefits of this and why I did it - I covered that in past blog posts.

  Instead, I'll mention what the big road block is this time: Your actions can change (i.e. picking up an item changes your actions if that item does stuff)! So how do I efficiently keep track of cooldowns? I can't just keep a list of everything, that's a lot of wasted memory especially if there's dozens of actions. I'd need to duplicate that entire list for every item in the game (as in, every item in an inventory slot, not item type) and every entity in the game (again, every entity in the world, not entity type) which you can imagine would waste so much memory.

  The current solution is to add some undefined behavior on purpose. The way action overrides work (i.e. by picking up an item) is by a layered system where objects can declare they overwrite actions at a certain priority level. Priority goes in order from Intrinsic (species abilities) → Item Actions (the things an item does when you use it) → Special Overrides (forced changes to another action).

  Each action set gets its own set of cooldowns for the (up to) five actions within it, and then the cooldown is selected just like the actions are when combining them into the list of actions that you see based on all overrides.

  The aforementioned "undefined behavior" I have to add is what happens when you edit the action set itself - something you shouldn't be doing anyway (why do you think I designed the layer system?) - and the answer to the question of "what happens?" is "good question!", hence undefined behavior. I'm not solving that problem for you. Use layers.

World Touch-Ups

  The world system has gotten some love in lieu of Godot 4.4 adding Jolt Physics natively. I originally had this optimization in an older post, but removed it because it was far too technical. The takeaway is that Jolt can work with a lot of static collisions faster than Godot's native physics can.

  One of the biggest changes I have done is abandoning trimesh collisions for blocky terrain (you can still use it elsewhere in custom stuff). This is a huge performance boost to generating chunks because I don't need to build a collision mesh anymore. I also use the low level physics API for this, directly telling the physics engine to add collisions instead of making use of CollisionShape3D in Godot.

  Another significant change, albeit a destructive one, is that I have removed wedge shapes for the time being. These are complicated and wreak all sorts of havoc on the physics system. Part of why physics is so much better now is because I only need to use BoxShape3D which is a hell of a lot simpler to compute than a convex hull (not great but not bad) or concave triangle collision mesh (worst by far).


  That all aside, it has been a busy week with all these system fixes. Hopefully this is the last of it!