Asset sync
Put a mod's models, textures and sounds in a data pack on the server. Every player receives them on join, with no resource pack to install.
Why
Content that players add (custom characters, say) normally needs two packs: a data pack for the server and a resource pack every player must install. Asset sync makes it one data pack. Strut sends the files during the join, keeps them in a cache, loads them as a resource pack, and only then lets the player spawn.
Using it
Most servers need no code. Put files here, in any data pack:
data/<namespace>/asset_sync/<path>
They reach players as assets/<namespace>/<path>. For example:
| In the data pack | What the player sees |
|---|---|
data/mymod/asset_sync/strut/models/entity/eve.strutmodel | assets/mymod/strut/models/entity/eve.strutmodel |
data/mymod/asset_sync/textures/entity/eve.png | assets/mymod/textures/entity/eve.png |
data/mymod/asset_sync/sounds/eve/cry1.ogg and sounds.json | The same paths under assets/mymod/. sounds.json and lang files merge with the mod's own. |
data/mymod/asset_sync/strut/animations/entity/eve.strutanim | The client's animation, and also the server's event timeline. No second copy needed. |
data/mymod/asset_sync/strut/event_bindings/eve.json | An event binding set. |
For a mod that wants to require the assets, or generate some in code:
StrutAssetSync.setRequired(true);
StrutAssetSync.addSource(sink -> sink.add("mymod", "sounds.json", soundsJson));
Sources are called on every data reload, after the data packs, so what they add overrides the packs. On the client:
StrutAssetSyncClient.onAssetsReady(() -> {
// the server's models, sounds and textures are loaded; the player has not joined yet
});
What is accepted
- File types:
png,ogg,json,mcmeta,strutmodel,strutanim,lang,txt. Anything else (classes, jars, scripts, shaders) is skipped with a warning. - Paths: lower-case letters, digits and
_ . - /only. A file calledBia.pngis skipped. - Reserved namespaces:
minecraft,realms,fabric,fabricloaderandccannot be written to. - Size: 64 MiB per file and 512 MiB of distinct content held in memory on the server, by default (
StrutAssetSync.setLimits). Identical files count once.
What happens on join
- The server sends the list of files with their hashes.
- The client compares it with its cache and asks only for what it lacks.
- The server sends those files in chunks, with flow control, and the client verifies every file's hash before keeping it.
- The client makes the files one more resource pack, reloads resources once, and tells the server it is done.
- The server lets the player in.
A returning player downloads nothing that has not changed. The cache is shared across servers and lives under strut/asset-cache in the game folder.
When the server reloads
A /reload that changes the assets sends each connected player who accepted the earlier ones only the difference, and their client reloads once. A player who declined is not asked again. If a reload removes every asset, connected players drop them.
The player's controls
A server can only offer. The player's limits, in config/strut-client.json, are enforced on the client whatever the server says:
| Key | Default | Meaning |
|---|---|---|
acceptServerAssets | true | Turn asset sync off entirely. |
maxDownloadPerServerMiB | 512 | The most one sync may download. |
cacheLimitMiB | 1024 | Least recently used files are deleted above this. |
maxTexturePixels | 67108864 | The largest a synced PNG may be (8192 x 8192). Larger is refused. |
If a player declines or is over their cap, they join without the assets, unless your mod called setRequired(true), in which case they are refused with a clear reason.
Safety
A server that can put files on a player's computer has to be treated as hostile, so the client checks everything:
- Nothing received is ever run. Only data types are accepted, and no server-chosen name is ever used as a path on disk: cache files are named by their hash.
- Every file's SHA-256 is verified before it is used, and sizes are checked before and during transfer.
- Every read of a message is bounded before anything is allocated, and every state has a timeout.
- PNG headers are checked before the game decodes them, so a tiny file cannot expand into an enormous image.
What remains is the same exposure a vanilla server resource pack has: the game's own image and sound decoders, and other mods that read resource files.
Trying it
Start the game or server with -Dstrut.assetSyncDemo=true. The server offers a small strut_demo namespace. Join and press F9: the overlay shows the assets active and whether the game's own language loader and resource manager can see them. /strut assets status shows what the server offers and each player's progress.