MonkeyLoader mod for Resonite: local storage
  • C# 96.8%
  • Python 3.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
KyuubiYoru 6e8fc3e949 Name variant files in the asset store by a short id
A variant's full name, like version=3&compression=BC3nm_Crunched&
quality=100&width=1024..., put store paths at up to 238 characters
relative to the storage folder. In a typical Windows location many went
past 260, which Explorer and other tools without long-path support
can't copy.

Variant files are now named by the first 16 hex digits of the full
name's SHA-256 hash, and VariantNames/<id> holds the full name. A name
file only counts when its name hashes to its id. Names stay even when
no variant uses them, since sync may bring a variant file after its
name. The store's part of a path is now at most 98 characters.

Stores from 0.7.16 and earlier keep working: the mod and
rebuild-package.py still read full-name files, and the first clean-up
of a session renames them. Renaming instead of copying keeps a synced
folder from uploading the variants again. On a copy of a real store's
names, 38,536 files in 9,429 folders took 0.5 s, and twelve real items
rebuilt the same before and after, in C# and Python.

Closes #15
2026-10-07 07:25:47 +02:00
ResoLocalStorage Name variant files in the asset store by a short id 2026-10-07 07:25:47 +02:00
ResoLocalStorage.Tests Name variant files in the asset store by a short id 2026-10-07 07:25:47 +02:00
tools Name variant files in the asset store by a short id 2026-10-07 07:25:47 +02:00
.gitignore Initial commit 2026-09-25 08:07:36 +02:00
global.json Add Local Storage inventory backed by .resonitepackage files 2026-09-25 08:45:00 +02:00
LICENSE Correct the README against the code and add the MIT license 2026-10-07 02:35:29 +02:00
README.md Name variant files in the asset store by a short id 2026-10-07 07:25:47 +02:00
ResoLocalStorage.slnx Add Local Storage inventory backed by .resonitepackage files 2026-09-25 08:45:00 +02:00

ResoLocalStorage

A MonkeyLoader mod for Resonite that adds a Local Storage inventory on your own disk.

It is modelled on art0007i/LocalStorage, with one difference: every saved item is a self-contained .resonitepackage holding the object, all of its assets and its thumbnail. You can drag any saved item into Resonite and it spawns, even on a machine without the mod (see Limits for assets that came from the cloud). Wiping the local database does not break saved items.

If you'd rather store each asset only once, turn on shared assets.

Warning

Your storage folder holds the only copy of what you save there, and after Move to … the items are gone from the cloud as well. If their files are deleted or damaged, their assets are lost for good. With shared assets, one damaged file in .assets breaks every item that uses it. Back up the whole storage folder regularly, .assets included.

Installation

You need Resonite with MonkeyLoader and its Resonite game pack.

Download ResoLocalStorage.nupkg from the latest release, put it in MonkeyLoader/Mods inside the Resonite folder, then start Resonite.

Usage

Open the inventory, press the inventories button (top left) and pick Local Storage. It works like a normal inventory folder: save held items, create folders, spawn, equip avatars, delete. It stays reachable while logged out.

Copying cloud folders

In your cloud inventory, press Copy to Local Storage. It copies the selected folder (or folder link), or the folder you're in when nothing is selected, with everything below it into the Local Storage root, next to anything already there.

  • Items become .resonitepackage files, or .rlsitem files with shared assets on, with their name, description, tags and thumbnail. Their assets are downloaded as needed; one that can't be downloaded is left out of the package. Worlds also get an orb picture for the inventory, rendered the way worlds saved into Local Storage get one.
  • Folder links inside are copied as links; the folders they point to are not copied.
  • It runs in the background, 4 items at a time. The dash's sync indicator counts the items left, and a notification says when it's done and whether anything failed. The MonkeyLoader log names every item that failed.

Cloud storage breakdown

Cloud Storage in the inventory's button bar opens a popup over the dash that shows where your cloud storage goes. It reads your inventory folders, your worlds and every other record you own, with the assets each item uses; it downloads no assets. The result is kept until you press Rescan.

  • Each asset counts once, however many items use it. Every folder and item shows its unique part, used by nothing outside it and so freed by deleting it, and its shared part, used by other items too. The bar shows unique in bright and shared in dark.
  • Rows are sorted by unique size. A folder's "Shared only among these" row is space that several of its entries share and nothing outside the folder uses; it only comes back once all of those entries are deleted. With that row, the unique column adds up to the folder's own unique size.
  • Opening an item shows its thumbnail, with Spawn in front of you for objects and Play for voice messages. Below that it lists every other item it shares assets with, and how much, plus what deleting it together with all of them frees. A folder has the same list for items outside it.
  • Outside your inventory holds records you own that the inventory doesn't show. Sent in messages has the items and voice messages you sent in chat, which the game keeps as your records; a message whose item is deleted may no longer open. Records whose folder the inventory no longer reaches keep that folder's path, and records without one go under No folder.
  • At the top level, "Not used by any item found" is used storage the cloud reports that no scanned item accounts for.

The toolbar above the list acts on the item or folder the page shows, or on the rows you tick. The top Inventory folder can only be copied. A folder under Outside your inventory acts on every item in it. Copy and Move take only the objects and worlds and leave the rest, such as voice messages, in the cloud; the result says how many were left out. Those can only be deleted.

  • Copy to … copies into the destination, a Local Storage folder named on the button.
  • Move to … copies there, then deletes from the cloud exactly what was copied. An item only counts as copied if every one of its assets downloaded; anything else stays in the cloud.
  • Change destination opens your Local Storage folders to pick another one. The destination starts at the Local Storage root and stays until you change it. The inventory's own Copy button always copies into the root.
  • Delete from Cloud deletes for good.

Every item shows when it was last saved after its name, so several saves of one world can be told apart; the results of acting on it name it the same way.

Tick the boxes on the left of a folder's rows to act on several items and folders at once; the box in the header ticks every row shown. Ticks stay while you look at one of the rows or pick a folder, and clear when you queue an action or open another folder.

Move and Delete need a second press within 3 seconds. A folder record is only deleted once the cloud lists nothing in it, so nothing is left behind where you can't see it; the result says how many folders were kept. What left the cloud also leaves the breakdown right away, with no rescan needed.

Actions queue up: while one runs, you can keep browsing and press more. They run one at a time in the order you pressed them, and the summary line shows how many are waiting. Ticked rows go in as one action with one result. Something an earlier action already took out of the cloud, such as an item in a folder you moved, fails with "no longer in the cloud" instead of being copied twice. Rescan only works once the queue is empty.

Worlds

With Local Storage open in the inventory, use Save As or Save Copy on a world and pick Save to Inventory on the orb that appears. The world is written as one package with all its assets and thumbnail.

  • Open World on the inventory entry starts a session from the package. Spawning the entry gives a world orb that does the same.
  • After Save As, or after opening a world from Local Storage, the world's normal Save button writes back to its package.
  • World packages only open with the mod installed; the vanilla importer only accepts objects.

Shared assets

With ShareAssets on, new items and worlds are saved as small <name>.rlsitem files, and their assets go into one .assets folder in the storage folder. An asset used by several items, or by several saved versions of a world, is stored once.

  • An .rlsitem is a .resonitepackage without the assets its records list in their asset manifest. It keeps the records, the object or world data and the thumbnails.
  • .assets has nearly the same layout as the inside of a package: Assets/<signature>, Metadata/<signature>.<type> and Variants/<signature>/<id>. A variant's full name, such as version=3&compression=BC3nm_Crunched&quality=100&width=1024…, can make a path too long for some Windows tools, so its file is named by an id: the first 16 hex digits of the full name's SHA-256 hash. VariantNames/<id> holds the full name.
  • Up to 0.7.16, variant files were named by their full name. Those are still read, and the first start with a newer version renames them, which takes about a second for 40,000 files. Update the mod on every machine that shares the storage folder.
  • Spawning and opening work as with packages. Assets are copied into Resonite's cache on load; Resonite never reads .assets itself.
  • Existing files keep their format. Saving over a world keeps the format it has.
  • Deleting an .rlsitem, or a folder with some in it, also deletes the files in .assets that no remaining item needs. Saving over a world does the same for what only the old version used. The mod keeps count of which items need which assets and only re-reads items that changed, so this takes milliseconds even with thousands of items. It also notices items you add or remove outside the game.
  • At startup, files in .assets that nothing needs, such as leftovers of an interrupted save, are deleted.

.rlsitem files only spawn with the mod. To get normal packages back without Resonite or the mod, use tools/rebuild-package.py (Python 3.8+, no other dependencies):

python3 tools/rebuild-package.py "LocalStorage/Hat.rlsitem"          # writes Hat.resonitepackage here
python3 tools/rebuild-package.py LocalStorage -o ~/rebuilt            # every .rlsitem, keeping folders

It finds .assets next to the item or in a folder above it, and checks every asset against its signature. The rule it follows is simple enough to redo with any zip tool: copy all entries of the .rlsitem, then for each hash in the records' assetManifest, add the matching files from .assets under the same names, except that each variant goes in as Variants/<signature>/<full name> with the name from VariantNames.

Where files go

By default the storage folder is LocalStorage inside Resonite's data path. The MonkeyLoader log prints the exact location at startup and whenever it changes.

  • Inventory folders are real folders.
  • Items and worlds are <name>.resonitepackage, or <name>.rlsitem with shared assets. Packages you copy into the folder yourself, such as vanilla exports, show up as items too.
  • Folder links are <name>.link.json.
  • File and folder names are made to work on both Windows and Linux, so the storage folder can be copied between them. Characters either system rejects, such as : or ?, become _. Long names are shortened to fit both, and a name that matches another one apart from case gets a number. Items, worlds and links keep their full name in the file; a folder's name is its name on disk.
  • Items keep the time they were last saved, also when copied or moved from the cloud: in the package, and as the file's date. The inventory shows that time after each item's name; the name stored in the package stays as it is.
  • Deleting an item, folder or link in the inventory deletes its files for good, as deleting from the cloud inventory does.

The inventory reads the disk when you open Local Storage from the inventories list; reopen it to see changes made outside the game.

Settings

Change these in MonkeyLoader's in-game settings, or in MonkeyLoader/Configs/ResoLocalStorage.json while Resonite is closed.

Key Default Meaning
StoragePath empty Storage folder. Empty uses <data path>/LocalStorage. A change applies right away; saves already running finish in the old folder.
IncludeVariants false Also store generated asset variants in packages. Bigger files, faster first load elsewhere.
ShareAssets false Save new items and worlds as .rlsitem files with their assets in the shared .assets folder.

Limits

  • Assets that came from the cloud keep their cloud links, with a copy in the package. With the mod, that copy fills Resonite's cache when the item loads. Without it, Resonite downloads those assets from the cloud and ignores the copy.
  • Favouriting a local item (avatar, home, etc.) stores a link only this machine can resolve.
  • Opening a world package imports its assets into the local database, so a big world takes that space twice.
  • Saving over a world replaces its package; older versions are not kept.
  • Other users can join a local world's session, but can't open its package themselves.
  • An .rlsitem needs the .assets folder it was saved with. Rebuild it into a package before moving it elsewhere or sharing it.

Building

dotnet build ResoLocalStorage.slnx
dotnet test ResoLocalStorage.slnx

The Resonite path is detected from the default Steam location; set ResonitePath otherwise. Add -p:ResoniteInstallOnBuild=true to copy the package into MonkeyLoader/Mods.

The tests for rebuild-package.py need python3 and are skipped without it. Set RLS_ROUNDTRIP_DIR to a folder of packages to also split and rebuild each of them, checking that every entry comes back unchanged.

License

MIT, see LICENSE.