Source: Decompiled from
HytaleServer.jar(pre-release2026.02.05-9ce2783f7)
Hytale's world generation uses a layered file system for asset resolution. This enables mod asset overrides — mods can replace or extend base generator files without modifying the originals. The system is built on three layers: FileIO (facade), FileIOSystem (resolution strategy), and AssetFileSystem (multi-pack overlay).
graph TD
A[FileIO - Static Facade] --> B[FileIOSystem - ThreadLocal]
B --> C[AssetFileSystem]
C --> D[Asset Pack 1 - Mod Override]
C --> E[Asset Pack 2 - Version Pack]
C --> F[Base Asset Pack]
D --> G[resolve path → first match wins]
E --> G
F --> G
Class: FileIO
Static facade that dispatches all file operations through the currently active FileIOSystem. Uses a thread-local holder so each generation thread can have its own file system view.
| Method | Description |
|---|---|
setDefaultRoot(Path) |
Set the global default file system root |
openFileIOSystem(FileIOSystem) |
Activate a custom FileIOSystem on the current thread |
closeFileIOSystem() |
Close the active system, revert to default |
exists(AssetPath) |
Check if a file exists |
resolve(Path) |
Resolve a path through the active FileIOSystem |
load(AssetPath, AssetLoader) |
Load and deserialize a file |
list(Path, Predicate, UnaryOperator) |
List files with filtering |
Class: FileIOSystem
Defines the resolution strategy for finding files across multiple roots.
| Method | Description |
|---|---|
baseRoot() |
Get the base root path |
roots() |
Get PathArray of all resolution roots (ordered by priority) |
resolve(Path) |
Resolve a relative path through the root hierarchy |
load(AssetPath, AssetLoader) |
Load file content |
- Iterate through
roots()in order (highest priority first) - For each root, check if
root + relativePathexists - Return the first match as an
AssetPath - If no match, fall back to
baseRoot()
Class: AssetFileSystem implements FileIOSystem
The production file system used during world generation. Supports layered asset packs with priority-based resolution.
- Non-version asset packs (mod overrides, in reverse registration order)
- Version-specific packs (if
VERSION_OVERRIDESfeature flag is enabled, packs where version <= config version) - Base asset pack (always lowest priority)
| Cache | Type | Description |
|---|---|---|
files |
Map<Path, AssetPath> |
Resolved path cache |
resources |
Map<AssetPath, Resource<?>> |
Loaded content cache (type-safe) |
The resource cache enforces type safety — if a file was previously loaded as type A, attempting to load it as type B throws an IllegalStateException.
For each registered asset pack (reverse order):
1. If pack root is NOT under $Versions/ directory:
→ Add to resolution roots (mod override)
2. If VERSION_OVERRIDES enabled AND pack root IS under $Versions/:
→ If pack version <= world config version:
→ Add to resolution roots (version pack)
3. Base pack always added last (lowest priority)
Class: AssetPath
Represents a file with both relative and absolute paths.
| Field | Type | Description |
|---|---|---|
path |
Path |
Relative path (without root prefix, used for comparison/hashing) |
filepath |
Path |
Absolute file path (root + relative, used for actual I/O) |
| Method | Description |
|---|---|
fromAbsolute(root, filepath) |
Create from absolute path — relativizes against root |
fromRelative(root, assetPath) |
Create from relative path — appends to root |
rename(filename) |
Create new AssetPath with renamed file |
Equality and hashing are based on the relative path only, enabling consistent lookup regardless of which root resolved the file.
Each asset pack has a manifest with metadata:
| Field | Type | Description |
|---|---|---|
name |
string |
Pack name |
group |
string |
Pack group/category |
website |
string |
Pack website URL |
description |
string |
Pack description |
version |
string |
Version string (used in version-specific resolution) |
authors |
AuthorInfo[] |
Pack authors |
serverVersion |
string |
Compatible server version |
graph TD
A["FileIO.load(path)"] --> B["FileIOSystem.resolve(path)"]
B --> C["Iterate roots() in priority order"]
C --> D{"File exists at root + path?"}
D -- Yes --> E["Return AssetPath"]
D -- No --> F["Try next root"]
F --> D
E --> G["Files.newInputStream(filepath)"]
G --> H["AssetLoader.load(stream)"]
H --> I["Cache result in resources map"]
Given this pack structure:
base-pack/
├── HytaleGenerator/
│ └── Biomes/
│ ├── Forest.json
│ └── Desert.json
my-mod-pack/
├── HytaleGenerator/
│ └── Biomes/
│ └── Forest.json ← overrides base Forest.json
When the generator loads HytaleGenerator/Biomes/Forest.json:
- Checks
my-mod-packfirst (higher priority) - Finds
Forest.json→ uses mod version - Never checks base pack for this file
When loading Desert.json:
- Checks
my-mod-pack→ not found - Checks base pack → found → uses base version
Source files: FileIO.java, FileIOSystem.java, AssetFileSystem.java, AssetPath.java, FileLoadingContext.java