This week we added a system that lets you easily store files on the local disk for things like saved games, dupes, and any other user generated content. This api is designed to be as simple and open ended as possible.

// Create a new save game entry
var saveEntry = Storage.CreateEntry( "save" );

// Write game data to files
saveEntry.Files.WriteAllText( "player.json", playerJson );
saveEntry.Files.WriteAllBytes( "world.dat", worldData );

// You can also use JSON serialization
saveEntry.Files.WriteJson( "config.json", gameConfig );

// Set metadata for searching and display
saveEntry.SetMeta( "playerName", "John Doe" );
saveEntry.SetMeta( "level", 42 );
saveEntry.SetMeta( "playtime", 3600 );

// Create and set a thumbnail
var thumbnail = CreateThumbnailBitmap(); // Your method to create a Bitmap
saveEntry.SetThumbnail( thumbnail );

You can easily find and iterate each item on disk.
var allSaves = Storage.GetAll( "save" );

foreach ( var save in allSaves )
{
    // hello
}

Using the storage system mentioned above, you have the option to publish and read them from the Steam Workshop. 

I've tried to make it as simple as possible. Here's how you do it:
saveEntry.Publish();
This pops up a dialog that lets the player configure the upload with a title and a description.

You can use Storage.Query to get a list of files from workshop, and you can convert any of those into a Storage.Entry - which will give you access to all the files inside using the same api as normal, local files.
The Sandbox Mode has been updated with a bunch of new features and bug fixes.

One of the big updates this week is with Dupes. They can now be saved locally and loaded in other sessions. They can also be published to the workshop by right clicking the local dupe. You can also download and play with other people's dupes.


Movie Maker now lets you repeat sequence blocks by extending them beyond the duration of the source clip.
We've also fixed some issues with how sequences get played back, which should be much more reliable now.
To allow more fine grained control over how agents are navigating through the world, we added the ability to specify traversal cost and traversal filters for parts of the NavMesh.

In this maze scene: one Agent allowed to pass green areas & shortcut, there is another agent that cannot pass the green areas and there is one more that is completely trapped.
Here you can see the agent avoiding the expensive zone when the cost is high, and you can see the NavMesh update in real-time when the properties an area changes.

You can find more details here: https://sbox.game/dev/doc/systems/navigation/navmesh-areas/
As someone who loves dungeon crawlers and roguelikes and thinks Dark Souls would be better in first-person, I haven't played any game (s&box or otherwise) in the last few months that grabbed me like Dark Descent did.
The first thing you'll want to do after entering the dungeon is light your lantern, because it really is dark down there. Then if you're anything like me you'll use your bow to pick off enemies at a distance like a coward.

But the melee combat is where the game really shines, as the parrying system is simpler and more intuitive than Chivalry/Mordhau but still makes you feel like a badass when you enter a flow state.

Initially the game feels extremely punishing, but stick with it and soon you'll laugh at the basic skeletons that gave you trouble at first. The boss also isn't as insurmountable as it seems, though I did need to find a magic axe that did both bleeding & freezing damage to help me kill it.
It's exciting seeing games this impressive being made with s&box. I can't wait to get obsessed with it again after more content is added, like shops to spend gold at, and the planned multiplayer features.
25.10.29
25.10.29
22 October 2025
🎁 Added
  • Storage UGC API
  • Movie Maker: Looping Sequences
  • VideoPlayer: Readded HTTP live streaming support (hls/m3u8)
  • NavMeshAreaDefintion Resource
  • NavMeshArea.Area
  • NavMeshLink.Area
  • NavMeshAgent.IsNavigating
  • NavMeshAgent.GetPath()
  • NavMeshAgent.AllowedAreas/ForbiddenAreas
  • bool NavMeshAgent.AllowDefaultArea
  • Capsule properties are marked with [JsonInclude]
  • Game.Overlay.WorkshopPublish
  • FileSystem.CreateMemoryFileSystem()
  • Cloud.IsInstalled( package )
  • Cloud.Load( package )
  • Cloud.ResolvePrimaryAsset( path )
  • Cloud.ResolvePrimaryAssetsFromJson( string )
  • TextEntry.HasClearButton property
  • ResourceLoader.Name (filename without extension)
  • InputFocus.Clear()
🧼 Improved
  • Version overlay tweaked
  • Live games tweaked
  • Store items display when they're leaving
  • Upgraded to FFmpeg 8.0
  • TriggerHurt assigns DamageInfo.Damage
  • SceneTree: enable drag/drop anywhere
  • ProjectSettingsInspector now opens a single popup when forcing the Inspector
  • Materials & models store their referenced cloud packages
  • Only group selection for convert to prefab if it has more than one element
  • Re-initialize the editor camera whenever a new scene is loaded in a SceneViewportWidget
  • Bias nav agent ground detection to prefer bottom trace
  • BaseFileSystem creates the directory structure when writing a file
  • TextEntry is :empty when it has no value set
🪛 Fixed
  • Tool materials used by Hammer/ModelDoc not rendering properly due to dynamic expression reliance
  • Crash when attempting to duplicate a scene root
  • Crash when trying to mix an invalid sound handle
  • Crash when GenerateMipmaps was used from multiple threads
  • Whitelist errors when verifying multiple assemblies at once
  • High Acceleration NavMeshAgent Getting Stuck on Edges
  • Restore IPressable.StartPressing behaviour so it can work with GetUsableComponent again
  • Restore Eye Dropper for InterfaceControlWidget
  • Load NRE when EditorOnly flag is set on a prefab root
  • "Merge Edges" not undoing to it's original state properly
  • MeshComponent's SceneObject being rebuilt whilst disabled
  • Modelphysics trying to drive physics from animation when motion is still enabled, we only want to apply this feature if the modelphysics is in kinematic mode
  • Sandbox.Mounting.ResourceFlags not actually being flags
  • Fixed held down keys repeating when input goes into UI mode instead of ignoring until up
  • Fix ColorAdjustments PP running in the wrong place
🚯 Removed
  • Obsolete NavMeshArea.LinkedCollider use SceneVolumes instead
trophy 1430
Apr 2021 103 posts
Update 25.10.29 : news/update-25-10-29
trophy 1786
Sep 2021 10 posts
first
trophy 1310
Sep 2021 94 posts
📦
trophy 400
Jul 2023 22 posts
🔥🔥🔥
trophy 0
Jul 2024 12 posts
I would suggest you be sure to have Stream-based APIs for Storage so it integrates well with other .NET APIs and third-party libraries .

You should also have async/coroutine/whatever methods for reading/writing storage, since it looks like the ones you shared are synchronous. If they are called on the game loop thread (I assume any game scripts you write run there by default) with large files, you could get frame drops. Adding async variants will make the calls more complex (you start calling on one frame but finish on another) but developers can use this to minimize stutter.
trophy 1290
Apr 2021 335 posts
OpenRead and OpenWrite give you a stream.

https://sbox.game/api/Sandbox.BaseFileSystem
trophy 415
Aug 2022 39 posts
Happy to see Dark Descent getting a shout out.
people
Log in to reply
You can't reply if you're not logged in. That would be crazy.